From 16916be51d746b1e1057b3219e5bec8f8814259e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 5 Nov 2010 23:44:47 -0700 Subject: altosui: Split out flight monitoring to separate window This creates a per-TD (or replay) window to contain the flight monitoring information, allowing multiple monitors. This also adds per-TD preferences for monitoring channel. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosDisplayThread.java | 40 +++---- ao-tools/altosui/AltosFlightReader.java | 38 +++++++ ao-tools/altosui/AltosFlightUI.java | 128 +++++++++++++++++++++ ao-tools/altosui/AltosLog.java | 17 +-- ao-tools/altosui/AltosPreferences.java | 20 ++-- ao-tools/altosui/AltosReplayReader.java | 57 ++++++++++ ao-tools/altosui/AltosReplayThread.java | 83 -------------- ao-tools/altosui/AltosSerial.java | 2 + ao-tools/altosui/AltosStatusTable.java | 5 +- ao-tools/altosui/AltosTelemetryReader.java | 62 +++++++++++ ao-tools/altosui/AltosUI.java | 171 +++++++---------------------- ao-tools/altosui/Makefile.am | 5 +- 12 files changed, 366 insertions(+), 262 deletions(-) create mode 100644 ao-tools/altosui/AltosFlightReader.java create mode 100644 ao-tools/altosui/AltosFlightUI.java create mode 100644 ao-tools/altosui/AltosReplayReader.java delete mode 100644 ao-tools/altosui/AltosReplayThread.java create mode 100644 ao-tools/altosui/AltosTelemetryReader.java diff --git a/ao-tools/altosui/AltosDisplayThread.java b/ao-tools/altosui/AltosDisplayThread.java index 9cc3d5ce..b15472ed 100644 --- a/ao-tools/altosui/AltosDisplayThread.java +++ b/ao-tools/altosui/AltosDisplayThread.java @@ -30,13 +30,14 @@ import java.util.concurrent.LinkedBlockingQueue; public class AltosDisplayThread extends Thread { - Frame parent; - IdleThread idle_thread; - AltosVoice voice; - String name; - int crc_errors; - AltosStatusTable flightStatus; - AltosInfoTable flightInfo; + Frame parent; + IdleThread idle_thread; + AltosVoice voice; + String name; + AltosFlightReader reader; + int crc_errors; + AltosStatusTable flightStatus; + AltosInfoTable flightInfo; class IdleThread extends Thread { @@ -150,14 +151,6 @@ public class AltosDisplayThread extends Thread { } } - void init() { } - - AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; } - - void close(boolean interrupted) { } - - void update(AltosState state) throws InterruptedException { } - boolean tell(AltosState state, AltosState old_state) { boolean ret = false; if (old_state == null || old_state.state != state.state) { @@ -208,12 +201,12 @@ public class AltosDisplayThread extends Thread { try { for (;;) { try { - AltosRecord record = read(); + AltosRecord record = reader.read(); if (record == null) break; old_state = state; state = new AltosState(record, state); - update(state); + reader.update(state); show(state, crc_errors); told = tell(state, old_state); idle_thread.notice(state, told); @@ -232,7 +225,9 @@ public class AltosDisplayThread extends Thread { "Telemetry Read Error", JOptionPane.ERROR_MESSAGE); } finally { - close(interrupted); + if (!interrupted) + idle_thread.report(true); + reader.close(interrupted); idle_thread.interrupt(); try { idle_thread.join(); @@ -240,16 +235,11 @@ public class AltosDisplayThread extends Thread { } } - public AltosDisplayThread(Frame in_parent, AltosVoice in_voice, AltosStatusTable in_status, AltosInfoTable in_info) { + public AltosDisplayThread(Frame in_parent, AltosVoice in_voice, AltosStatusTable in_status, AltosInfoTable in_info, AltosFlightReader in_reader) { parent = in_parent; voice = in_voice; flightStatus = in_status; flightInfo = in_info; + reader = in_reader; } - - public void report() { - if (idle_thread != null) - idle_thread.report(true); - } - } diff --git a/ao-tools/altosui/AltosFlightReader.java b/ao-tools/altosui/AltosFlightReader.java new file mode 100644 index 00000000..3d59de9a --- /dev/null +++ b/ao-tools/altosui/AltosFlightReader.java @@ -0,0 +1,38 @@ +/* + * 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.lang.*; +import java.text.*; +import java.io.*; + +public class AltosFlightReader { + String name; + + int serial; + + void init() { } + + AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; } + + void close(boolean interrupted) { } + + void set_channel(int channel) { } + + void update(AltosState state) throws InterruptedException { } +} diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java new file mode 100644 index 00000000..84ba9dca --- /dev/null +++ b/ao-tools/altosui/AltosFlightUI.java @@ -0,0 +1,128 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosFlightUI extends JFrame { + String[] statusNames = { "Height (m)", "State", "RSSI (dBm)", "Speed (m/s)" }; + Object[][] statusData = { { "0", "pad", "-50", "0" } }; + + AltosVoice voice; + AltosFlightReader reader; + AltosDisplayThread thread; + + private Box vbox; + private AltosStatusTable flightStatus; + private AltosInfoTable flightInfo; + + public int width() { + return flightInfo.width(); + } + + public int height() { + return flightStatus.height() + flightInfo.height(); + } + + void stop_display() { + if (thread != null && thread.isAlive()) { + thread.interrupt(); + try { + thread.join(); + } catch (InterruptedException ie) {} + } + thread = null; + } + + void disconnect() { + stop_display(); + } + + public AltosFlightUI(AltosVoice in_voice, AltosFlightReader in_reader, final int serial) { + voice = in_voice; + reader = in_reader; + + 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 AltosStatusTable(); + + vbox = new Box (BoxLayout.Y_AXIS); + vbox.add(flightStatus); + + flightInfo = new AltosInfoTable(); + vbox.add(flightInfo.box()); + + 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); + } + + this.setSize(new Dimension (width(), height())); + this.validate(); + + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + disconnect(); + setVisible(false); + dispose(); + } + }); + + this.setVisible(true); + + thread = new AltosDisplayThread(this, voice, flightStatus, flightInfo, reader); + + thread.start(); + } + + public AltosFlightUI (AltosVoice in_voice, AltosFlightReader in_reader) { + this(in_voice, in_reader, -1); + } +} diff --git a/ao-tools/altosui/AltosLog.java b/ao-tools/altosui/AltosLog.java index f876beba..fed96c28 100644 --- a/ao-tools/altosui/AltosLog.java +++ b/ao-tools/altosui/AltosLog.java @@ -39,9 +39,15 @@ class AltosLog implements Runnable { FileWriter log_file; Thread log_thread; - void close() throws IOException { - if (log_file != null) - log_file.close(); + void close() { + if (log_file != null) { + try { + log_file.close(); + } catch (IOException io) { + } + } + if (log_thread != null) + log_thread.interrupt(); } boolean open (AltosTelemetry telem) throws IOException { @@ -89,10 +95,7 @@ class AltosLog implements Runnable { } catch (InterruptedException ie) { } catch (IOException ie) { } - try { - close(); - } catch (IOException ie) { - } + close(); } public AltosLog (AltosSerial s) { diff --git a/ao-tools/altosui/AltosPreferences.java b/ao-tools/altosui/AltosPreferences.java index 52627563..e2a3df3b 100644 --- a/ao-tools/altosui/AltosPreferences.java +++ b/ao-tools/altosui/AltosPreferences.java @@ -32,7 +32,7 @@ class AltosPreferences { final static String logdirPreference = "LOGDIR"; /* channel preference name */ - final static String channelPreference = "CHANNEL"; + final static String channelPreferenceFormat = "CHANNEL-%d"; /* voice preference name */ final static String voicePreference = "VOICE"; @@ -52,8 +52,8 @@ class AltosPreferences { /* Log directory */ static File logdir; - /* Telemetry channel */ - static int channel; + /* Channel (map serial to channel) */ + static Hashtable channels; /* Voice preference */ static boolean voice; @@ -80,7 +80,7 @@ class AltosPreferences { logdir.mkdirs(); } - channel = preferences.getInt(channelPreference, 0); + channels = new Hashtable(); voice = preferences.getBoolean(voicePreference, true); @@ -151,15 +151,19 @@ class AltosPreferences { return logdir; } - public static void set_channel(int new_channel) { - channel = new_channel; + public static void set_channel(int serial, int new_channel) { + channels.put(serial, new_channel); synchronized (preferences) { - preferences.putInt(channelPreference, channel); + preferences.putInt(String.format(channelPreferenceFormat, serial), new_channel); flush_preferences(); } } - public static int channel() { + public static int channel(int serial) { + if (channels.containsKey(serial)) + return channels.get(serial); + int channel = preferences.getInt(String.format(channelPreferenceFormat, serial), 0); + channels.put(serial, channel); return channel; } diff --git a/ao-tools/altosui/AltosReplayReader.java b/ao-tools/altosui/AltosReplayReader.java new file mode 100644 index 00000000..4e5e1d93 --- /dev/null +++ b/ao-tools/altosui/AltosReplayReader.java @@ -0,0 +1,57 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +/* + * Open an existing telemetry file and replay it in realtime + */ + +public class AltosReplayReader extends AltosFlightReader { + Iterator iterator; + + public AltosRecord read() { + if (iterator.hasNext()) + return iterator.next(); + return null; + } + + public void close (boolean interrupted) { + } + + void update(AltosState state) throws InterruptedException { + /* Make it run in realtime after the rocket leaves the pad */ + if (state.state > Altos.ao_flight_pad) + Thread.sleep((int) (Math.min(state.time_change,10) * 1000)); + } + + public AltosReplayReader(Iterator in_iterator, String in_name) { + iterator = in_iterator; + name = in_name; + } +} diff --git a/ao-tools/altosui/AltosReplayThread.java b/ao-tools/altosui/AltosReplayThread.java deleted file mode 100644 index b418160a..00000000 --- a/ao-tools/altosui/AltosReplayThread.java +++ /dev/null @@ -1,83 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -import altosui.Altos; -import altosui.AltosSerial; -import altosui.AltosSerialMonitor; -import altosui.AltosRecord; -import altosui.AltosTelemetry; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosFlightInfoTableModel; -import altosui.AltosChannelMenu; -import altosui.AltosFlashUI; -import altosui.AltosLogfileChooser; -import altosui.AltosCSVUI; -import altosui.AltosLine; -import altosui.AltosStatusTable; -import altosui.AltosInfoTable; -import altosui.AltosDisplayThread; - -/* - * Open an existing telemetry file and replay it in realtime - */ - -public class AltosReplayThread extends AltosDisplayThread { - Iterator iterator; - String name; - - public AltosRecord read() { - if (iterator.hasNext()) - return iterator.next(); - return null; - } - - public void close (boolean interrupted) { - if (!interrupted) - report(); - } - - void update(AltosState state) throws InterruptedException { - /* Make it run in realtime after the rocket leaves the pad */ - if (state.state > Altos.ao_flight_pad) - Thread.sleep((int) (Math.min(state.time_change,10) * 1000)); - } - - public AltosReplayThread(Frame parent, Iterator in_iterator, - String in_name, AltosVoice voice, - AltosStatusTable status, AltosInfoTable info) { - super(parent, voice, status, info); - iterator = in_iterator; - name = in_name; - } -} diff --git a/ao-tools/altosui/AltosSerial.java b/ao-tools/altosui/AltosSerial.java index a1fc4371..f65e44d6 100644 --- a/ao-tools/altosui/AltosSerial.java +++ b/ao-tools/altosui/AltosSerial.java @@ -193,6 +193,8 @@ public class AltosSerial implements Runnable { print("~\nE 0\n"); flush_output(); set_monitor(monitor_mode); + set_channel(AltosPreferences.channel()); + set_callsign(AltosPreferences.callsign()); } public void set_channel(int channel) { diff --git a/ao-tools/altosui/AltosStatusTable.java b/ao-tools/altosui/AltosStatusTable.java index 3965a57d..0d3a5d14 100644 --- a/ao-tools/altosui/AltosStatusTable.java +++ b/ao-tools/altosui/AltosStatusTable.java @@ -34,14 +34,11 @@ import altosui.AltosFlightInfoTableModel; public class AltosStatusTable extends JTable { private AltosFlightStatusTableModel flightStatusModel; - JFrame frame; - private Font statusFont = new Font("SansSerif", Font.BOLD, 24); - public AltosStatusTable(JFrame in_frame) { + public AltosStatusTable() { super((TableModel) new AltosFlightStatusTableModel()); flightStatusModel = (AltosFlightStatusTableModel) getModel(); - frame = in_frame; setFont(statusFont); diff --git a/ao-tools/altosui/AltosTelemetryReader.java b/ao-tools/altosui/AltosTelemetryReader.java new file mode 100644 index 00000000..0b5509eb --- /dev/null +++ b/ao-tools/altosui/AltosTelemetryReader.java @@ -0,0 +1,62 @@ +/* + * 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.lang.*; +import java.text.*; +import java.io.*; +import java.util.concurrent.*; + +class AltosTelemetryReader extends AltosFlightReader { + AltosDevice device; + AltosSerial serial; + AltosLog log; + + LinkedBlockingQueue telem; + + AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { + AltosLine l = telem.take(); + if (l.line == null) + throw new IOException("IO error"); + return new AltosTelemetry(l.line); + } + + void close(boolean interrupted) { + serial.remove_monitor(telem); + log.close(); + serial.close(); + } + + void set_channel(int channel) { + serial.set_channel(channel); + } + + void set_callsign(String callsign) { + serial.set_callsign(callsign); + } + + public AltosTelemetryReader (AltosDevice in_device) throws FileNotFoundException, IOException { + device = in_device; + serial = new AltosSerial(); + log = new AltosLog(serial); + name = device.getPath(); + + telem = new LinkedBlockingQueue(); + serial.add_monitor(telem); + } +} diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index 28ed42fb..e1bbee30 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -39,7 +39,6 @@ import altosui.AltosPreferences; import altosui.AltosLog; import altosui.AltosVoice; import altosui.AltosFlightInfoTableModel; -import altosui.AltosChannelMenu; import altosui.AltosFlashUI; import altosui.AltosLogfileChooser; import altosui.AltosCSVUI; @@ -51,14 +50,6 @@ import altosui.AltosDisplayThread; import libaltosJNI.*; public class AltosUI extends JFrame { - private int channel = -1; - - private AltosStatusTable flightStatus; - private AltosInfoTable flightInfo; - private AltosSerial serial_line; - private AltosLog altos_log; - private Box vbox; - public AltosVoice voice = new AltosVoice(); public static boolean load_library(Frame frame) { @@ -73,38 +64,41 @@ public class AltosUI extends JFrame { return true; } + void telemetry_window(AltosDevice device) { + try { + AltosFlightReader reader = new AltosTelemetryReader(device); + if (reader != null) + new AltosFlightUI(voice, reader, device.getSerial()); + } catch (FileNotFoundException ee) { + JOptionPane.showMessageDialog(AltosUI.this, + String.format("Cannot open device \"%s\"", + device.getPath()), + "Cannot open target device", + JOptionPane.ERROR_MESSAGE); + } catch (IOException ee) { + JOptionPane.showMessageDialog(AltosUI.this, + device.getPath(), + "Unkonwn I/O error", + JOptionPane.ERROR_MESSAGE); + } + } + public AltosUI() { load_library(null); - String[] statusNames = { "Height (m)", "State", "RSSI (dBm)", "Speed (m/s)" }; - Object[][] statusData = { { "0", "pad", "-50", "0" } }; - java.net.URL imgURL = AltosUI.class.getResource("/altus-metrum-16x16.jpg"); if (imgURL != null) setIconImage(new ImageIcon(imgURL).getImage()); AltosPreferences.init(this); - vbox = Box.createVerticalBox(); - this.add(vbox); - - flightStatus = new AltosStatusTable(this); - - vbox.add(flightStatus); - - flightInfo = new AltosInfoTable(); - vbox.add(flightInfo.box()); - setTitle("AltOS"); createMenu(); - serial_line = new AltosSerial(); - altos_log = new AltosLog(serial_line); int dpi = Toolkit.getDefaultToolkit().getScreenResolution(); - this.setSize(new Dimension (flightInfo.width(), - flightStatus.height() + flightInfo.height())); + this.setSize(new Dimension (300, 100)); this.validate(); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { @@ -113,63 +107,14 @@ public class AltosUI extends JFrame { System.exit(0); } }); - voice.speak("Rocket flight monitor ready."); - } - - class DeviceThread extends AltosDisplayThread { - AltosSerial serial; - LinkedBlockingQueue telem; - - AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { - AltosLine l = telem.take(); - if (l.line == null) - throw new IOException("IO error"); - return new AltosTelemetry(l.line); - } - - void close(boolean interrupted) { - serial.close(); - serial.remove_monitor(telem); - } - - public DeviceThread(AltosSerial s, String in_name, AltosVoice voice, AltosStatusTable status, AltosInfoTable info) { - super(AltosUI.this, voice, status, info); - serial = s; - telem = new LinkedBlockingQueue(); - serial.add_monitor(telem); - name = in_name; - } } private void ConnectToDevice() { AltosDevice device = AltosDeviceDialog.show(AltosUI.this, AltosDevice.product_basestation); - if (device != null) { - try { - stop_display(); - serial_line.open(device); - DeviceThread thread = new DeviceThread(serial_line, device.getPath(), voice, flightStatus, flightInfo); - serial_line.set_channel(AltosPreferences.channel()); - serial_line.set_callsign(AltosPreferences.callsign()); - run_display(thread); - } catch (FileNotFoundException ee) { - JOptionPane.showMessageDialog(AltosUI.this, - String.format("Cannot open device \"%s\"", - device.getPath()), - "Cannot open target device", - JOptionPane.ERROR_MESSAGE); - } catch (IOException ee) { - JOptionPane.showMessageDialog(AltosUI.this, - device.getPath(), - "Unkonwn I/O error", - JOptionPane.ERROR_MESSAGE); - } - } - } - - void DisconnectFromDevice () { - stop_display(); + if (device != null) + telemetry_window(device); } void ConfigureCallsign() { @@ -177,11 +122,8 @@ public class AltosUI extends JFrame { result = JOptionPane.showInputDialog(AltosUI.this, "Configure Callsign", AltosPreferences.callsign()); - if (result != null) { + if (result != null) AltosPreferences.set_callsign(result); - if (serial_line != null) - serial_line.set_callsign(result); - } } void ConfigureTeleMetrum() { @@ -192,25 +134,6 @@ public class AltosUI extends JFrame { new AltosFlashUI(AltosUI.this); } - - Thread display_thread; - - private void stop_display() { - if (display_thread != null && display_thread.isAlive()) { - display_thread.interrupt(); - try { - display_thread.join(); - } catch (InterruptedException ie) {} - } - display_thread = null; - } - - private void run_display(Thread thread) { - stop_display(); - display_thread = thread; - display_thread.start(); - } - /* * Replay a flight from telemetry data */ @@ -218,12 +141,11 @@ public class AltosUI extends JFrame { AltosLogfileChooser chooser = new AltosLogfileChooser( AltosUI.this); AltosRecordIterable iterable = chooser.runDialog(); - if (iterable != null) - run_display(new AltosReplayThread(this, iterable.iterator(), - chooser.filename(), - voice, - flightStatus, - flightInfo)); + if (iterable != null) { + AltosFlightReader reader = new AltosReplayReader(iterable.iterator(), + chooser.filename()); + new AltosFlightUI(voice, reader); + } } /* Connect to TeleMetrum, either directly or through @@ -278,7 +200,7 @@ public class AltosUI extends JFrame { }); menu.add(item); - item = new JMenuItem("Flash Image",KeyEvent.VK_F); + item = new JMenuItem("Flash Image",KeyEvent.VK_I); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { FlashImage(); @@ -286,7 +208,7 @@ public class AltosUI extends JFrame { }); menu.add(item); - item = new JMenuItem("Export Data",KeyEvent.VK_F); + item = new JMenuItem("Export Data",KeyEvent.VK_E); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ExportData(); @@ -294,7 +216,7 @@ public class AltosUI extends JFrame { }); menu.add(item); - item = new JMenuItem("Graph Data",KeyEvent.VK_F); + item = new JMenuItem("Graph Data",KeyEvent.VK_G); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { GraphData(); @@ -307,6 +229,7 @@ public class AltosUI extends JFrame { ActionEvent.CTRL_MASK)); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { + System.out.printf("exiting\n"); System.exit(0); } }); @@ -314,7 +237,7 @@ public class AltosUI extends JFrame { } // Device menu - { + if (false) { menu = new JMenu("Device"); menu.setMnemonic(KeyEvent.VK_D); menubar.add(menu); @@ -327,14 +250,6 @@ public class AltosUI extends JFrame { }); menu.add(item); - item = new JMenuItem("Disconnect from Device",KeyEvent.VK_D); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - DisconnectFromDevice(); - } - }); - menu.add(item); - menu.addSeparator(); item = new JMenuItem("Set Callsign",KeyEvent.VK_S); @@ -403,23 +318,7 @@ public class AltosUI extends JFrame { }); menu.add(item); } - - // Channel menu - { - menu = new AltosChannelMenu(AltosPreferences.channel()); - menu.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int new_channel = Integer.parseInt(e.getActionCommand()); - AltosPreferences.set_channel(new_channel); - serial_line.set_channel(new_channel); - } - }); - menu.setMnemonic(KeyEvent.VK_C); - menubar.add(menu); - } - this.setJMenuBar(menubar); - } static AltosRecordIterable open_logfile(String filename) { @@ -510,6 +409,10 @@ public class AltosUI extends JFrame { } else { AltosUI altosui = new AltosUI(); altosui.setVisible(true); + + AltosDevice[] devices = AltosDevice.list(AltosDevice.product_basestation); + for (int i = 0; i < devices.length; i++) + altosui.telemetry_window(devices[i]); } } } diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index 2f4ed6d8..2322b93f 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -29,7 +29,9 @@ altosui_JAVA = \ AltosFlash.java \ AltosFlashUI.java \ AltosFlightInfoTableModel.java \ + AltosFlightReader.java \ AltosFlightStatusTableModel.java \ + AltosFlightUI.java \ AltosGPS.java \ AltosGreatCircle.java \ AltosHexfile.java \ @@ -44,7 +46,8 @@ altosui_JAVA = \ AltosReader.java \ AltosRecord.java \ AltosRecordIterable.java \ - AltosReplayThread.java \ + AltosTelemetryReader.java \ + AltosReplayReader.java \ AltosRomconfig.java \ AltosRomconfigUI.java \ AltosSerial.java \ -- cgit v1.2.3 From 6b17d276271faa8a420a1c8f6be17faaa0c7043c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 8 Nov 2010 22:07:04 -0800 Subject: altosui: Create buttons for main actions Signed-off-by: Keith Packard --- ao-tools/altosui/AltosUI.java | 97 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 19 deletions(-) diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index e1bbee30..ded9e733 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -83,6 +83,27 @@ public class AltosUI extends JFrame { } } + Container pane; + GridBagLayout gridbag; + + JButton addButton(int x, int y, String label) { + GridBagConstraints c; + JButton b; + + c = new GridBagConstraints(); + c.gridx = x; c.gridy = y; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + b = new JButton(label); + + Dimension ps = b.getPreferredSize(); + + gridbag.setConstraints(b, c); + add(b, c); + return b; + } + public AltosUI() { load_library(null); @@ -93,13 +114,67 @@ public class AltosUI extends JFrame { AltosPreferences.init(this); + pane = getContentPane(); + gridbag = new GridBagLayout(); + pane.setLayout(gridbag); + + JButton b; + + b = addButton(0, 0, "Monitor Flight"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ConnectToDevice(); + } + }); + b = addButton(1, 0, "Save Flight Data"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + SaveFlightData(); + } + }); + b = addButton(2, 0, "Replay Flight"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Replay(); + } + }); + b = addButton(0, 1, "Graph Data"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + GraphData(); + } + }); + b = addButton(1, 1, "Export Data"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ExportData(); + } + }); + b = addButton(2, 1, "Configure TeleMetrum"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ConfigureTeleMetrum(); + } + }); + setTitle("AltOS"); createMenu(); - int dpi = Toolkit.getDefaultToolkit().getScreenResolution(); - this.setSize(new Dimension (300, 100)); - this.validate(); + pane.doLayout(); + pane.validate(); + + doLayout(); + validate(); + + setVisible(true); + + Insets i = getInsets(); + Dimension ps = rootPane.getPreferredSize(); + ps.width += i.left + i.right; + ps.height += i.top + i.bottom; + setPreferredSize(ps); + setSize(ps); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { @Override @@ -184,22 +259,6 @@ public class AltosUI extends JFrame { menu.setMnemonic(KeyEvent.VK_F); menubar.add(menu); - item = new JMenuItem("Replay File",KeyEvent.VK_R); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Replay(); - } - }); - menu.add(item); - - item = new JMenuItem("Save Flight Data",KeyEvent.VK_S); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - SaveFlightData(); - } - }); - menu.add(item); - item = new JMenuItem("Flash Image",KeyEvent.VK_I); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { -- cgit v1.2.3 From 94f4a50d6430cc8280cbdaa9f39d3cb858d0e077 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 8 Nov 2010 22:10:46 -0800 Subject: altosui: Fix channel setting at serial open time Was using the previous non-device-specific preferences API. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosSerial.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ao-tools/altosui/AltosSerial.java b/ao-tools/altosui/AltosSerial.java index f65e44d6..d6848e57 100644 --- a/ao-tools/altosui/AltosSerial.java +++ b/ao-tools/altosui/AltosSerial.java @@ -193,7 +193,7 @@ public class AltosSerial implements Runnable { print("~\nE 0\n"); flush_output(); set_monitor(monitor_mode); - set_channel(AltosPreferences.channel()); + set_channel(AltosPreferences.channel(device.getSerial())); set_callsign(AltosPreferences.callsign()); } -- cgit v1.2.3 From a0a92c605e238277c9881545a7226e53b5dbc295 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 8 Nov 2010 22:17:26 -0800 Subject: altosui: Fix more calls to AltosPreferences.channel() Oops. Two more. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosConfig.java | 2 +- ao-tools/altosui/AltosEepromDownload.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ao-tools/altosui/AltosConfig.java b/ao-tools/altosui/AltosConfig.java index 7b6cd78c..7fecff0e 100644 --- a/ao-tools/altosui/AltosConfig.java +++ b/ao-tools/altosui/AltosConfig.java @@ -122,7 +122,7 @@ public class AltosConfig implements Runnable, ActionListener { void start_serial() throws InterruptedException { if (remote) { - serial_line.set_channel(AltosPreferences.channel()); + serial_line.set_channel(AltosPreferences.channel(device.getSerial())); serial_line.set_callsign(AltosPreferences.callsign()); serial_line.printf("p\n"); serial_line.flush_input(); diff --git a/ao-tools/altosui/AltosEepromDownload.java b/ao-tools/altosui/AltosEepromDownload.java index a7f64904..8efc94d2 100644 --- a/ao-tools/altosui/AltosEepromDownload.java +++ b/ao-tools/altosui/AltosEepromDownload.java @@ -224,7 +224,7 @@ public class AltosEepromDownload implements Runnable { public void run () { if (remote) { - serial_line.set_channel(AltosPreferences.channel()); + serial_line.set_channel(AltosPreferences.channel(device.getSerial())); serial_line.set_callsign(AltosPreferences.callsign()); serial_line.printf("p\nE 0\n"); serial_line.flush_input(); -- cgit v1.2.3 From eb77e806ded99532dc7eaa39c1893f075b028af6 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 9 Nov 2010 10:21:34 -0800 Subject: altosui: Create abstract interface for flight data display This allows the implementation of the flight data display to occur in the flight UI instead of the display thread. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosDisplayThread.java | 16 ++++++---------- ao-tools/altosui/AltosFlightDisplay.java | 24 ++++++++++++++++++++++++ ao-tools/altosui/AltosFlightUI.java | 13 +++++++++++-- ao-tools/altosui/Makefile.am | 1 + 4 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 ao-tools/altosui/AltosFlightDisplay.java diff --git a/ao-tools/altosui/AltosDisplayThread.java b/ao-tools/altosui/AltosDisplayThread.java index b15472ed..957ac0d6 100644 --- a/ao-tools/altosui/AltosDisplayThread.java +++ b/ao-tools/altosui/AltosDisplayThread.java @@ -36,8 +36,7 @@ public class AltosDisplayThread extends Thread { String name; AltosFlightReader reader; int crc_errors; - AltosStatusTable flightStatus; - AltosInfoTable flightInfo; + AltosFlightDisplay display; class IdleThread extends Thread { @@ -182,10 +181,8 @@ public class AltosDisplayThread extends Thread { } void show(AltosState state, int crc_errors) { - if (state != null) { - flightStatus.set(state); - flightInfo.show(state, crc_errors); - } + if (state != null) + display.show(state, crc_errors); } public void run() { @@ -197,7 +194,7 @@ public class AltosDisplayThread extends Thread { idle_thread = new IdleThread(); - flightInfo.clear(); + display.reset(); try { for (;;) { try { @@ -235,11 +232,10 @@ public class AltosDisplayThread extends Thread { } } - public AltosDisplayThread(Frame in_parent, AltosVoice in_voice, AltosStatusTable in_status, AltosInfoTable in_info, AltosFlightReader in_reader) { + public AltosDisplayThread(Frame in_parent, AltosVoice in_voice, AltosFlightDisplay in_display, AltosFlightReader in_reader) { parent = in_parent; voice = in_voice; - flightStatus = in_status; - flightInfo = in_info; + display = in_display; reader = in_reader; } } diff --git a/ao-tools/altosui/AltosFlightDisplay.java b/ao-tools/altosui/AltosFlightDisplay.java new file mode 100644 index 00000000..d18d1d1f --- /dev/null +++ b/ao-tools/altosui/AltosFlightDisplay.java @@ -0,0 +1,24 @@ +/* + * 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; + +public interface AltosFlightDisplay { + void reset(); + + void show(AltosState state, int crc_errors); +} diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 84ba9dca..11fc2442 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -28,7 +28,7 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -public class AltosFlightUI extends JFrame { +public class AltosFlightUI extends JFrame implements AltosFlightDisplay { String[] statusNames = { "Height (m)", "State", "RSSI (dBm)", "Speed (m/s)" }; Object[][] statusData = { { "0", "pad", "-50", "0" } }; @@ -62,6 +62,15 @@ public class AltosFlightUI extends JFrame { stop_display(); } + public void reset() { + flightInfo.clear(); + } + + public void show(AltosState state, int crc_errors) { + flightStatus.set(state); + flightInfo.show(state, crc_errors); + } + public AltosFlightUI(AltosVoice in_voice, AltosFlightReader in_reader, final int serial) { voice = in_voice; reader = in_reader; @@ -117,7 +126,7 @@ public class AltosFlightUI extends JFrame { this.setVisible(true); - thread = new AltosDisplayThread(this, voice, flightStatus, flightInfo, reader); + thread = new AltosDisplayThread(this, voice, this, reader); thread.start(); } diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index 2322b93f..ccb88ed1 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -28,6 +28,7 @@ altosui_JAVA = \ AltosFile.java \ AltosFlash.java \ AltosFlashUI.java \ + AltosFlightDisplay.java \ AltosFlightInfoTableModel.java \ AltosFlightReader.java \ AltosFlightStatusTableModel.java \ -- cgit v1.2.3 From 22d00785188a880700cd372528189a7a15278da9 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 9 Nov 2010 14:40:58 -0800 Subject: altosui: Add tab UI with 'pad' mode. This creates a multi-tab interface for flight monitoring and includes a special tab for 'pad' mode. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosFlightUI.java | 16 ++- ao-tools/altosui/AltosLed.java | 54 ++++++++ ao-tools/altosui/AltosLights.java | 72 +++++++++++ ao-tools/altosui/AltosPad.java | 249 ++++++++++++++++++++++++++++++++++++ ao-tools/altosui/Makefile.am | 24 +++- icon/grayled.png | Bin 0 -> 1528 bytes icon/grayon.png | Bin 0 -> 1873 bytes icon/greenled.png | Bin 0 -> 2281 bytes icon/greenoff.png | Bin 0 -> 1811 bytes icon/redled.png | Bin 0 -> 2103 bytes icon/redoff.png | Bin 0 -> 1694 bytes 11 files changed, 410 insertions(+), 5 deletions(-) create mode 100644 ao-tools/altosui/AltosLed.java create mode 100644 ao-tools/altosui/AltosLights.java create mode 100644 ao-tools/altosui/AltosPad.java create mode 100644 icon/grayled.png create mode 100644 icon/grayon.png create mode 100644 icon/greenled.png create mode 100644 icon/greenoff.png create mode 100644 icon/redled.png create mode 100644 icon/redoff.png diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 11fc2442..a7caf7e9 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -37,6 +37,11 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { AltosDisplayThread thread; private Box vbox; + + JTabbedPane pane; + + AltosPad pad; + private AltosStatusTable flightStatus; private AltosInfoTable flightInfo; @@ -63,10 +68,12 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { } public void reset() { + pad.reset(); flightInfo.clear(); } public void show(AltosState state, int crc_errors) { + pad.show(state, crc_errors); flightStatus.set(state); flightInfo.show(state, crc_errors); } @@ -86,8 +93,15 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { vbox = new Box (BoxLayout.Y_AXIS); vbox.add(flightStatus); + pane = new JTabbedPane(); + + pad = new AltosPad(); + pane.add("Launch Pad", pad); + flightInfo = new AltosInfoTable(); - vbox.add(flightInfo.box()); + pane.add("Table", flightInfo.box()); + + vbox.add(pane); this.add(vbox); diff --git a/ao-tools/altosui/AltosLed.java b/ao-tools/altosui/AltosLed.java new file mode 100644 index 00000000..e08e9960 --- /dev/null +++ b/ao-tools/altosui/AltosLed.java @@ -0,0 +1,54 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosLed extends JLabel { + ImageIcon on, off; + + ImageIcon create_icon(String path) { + java.net.URL imgURL = AltosUI.class.getResource(path); + if (imgURL != null) + return new ImageIcon(imgURL); + System.err.printf("Cannot find icon \"%s\"\n", path); + return null; + } + + public void set(boolean set) { + if (set) + setIcon(on); + else + setIcon(off); + } + + public AltosLed(String on_path, String off_path) { + on = create_icon(on_path); + off = create_icon(off_path); + setIcon(off); + } +} diff --git a/ao-tools/altosui/AltosLights.java b/ao-tools/altosui/AltosLights.java new file mode 100644 index 00000000..2d2a1938 --- /dev/null +++ b/ao-tools/altosui/AltosLights.java @@ -0,0 +1,72 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosLights extends JComponent { + + GridBagLayout gridbag; + + AltosLed red, green; + + ImageIcon create_icon(String path, String description) { + java.net.URL imgURL = AltosUI.class.getResource(path); + if (imgURL != null) + return new ImageIcon(imgURL, description); + System.err.printf("Cannot find icon \"%s\"\n", path); + return null; + } + + public void set (boolean on) { + if (on) { + red.set(false); + green.set(true); + } else { + red.set(true); + green.set(false); + } + } + + public AltosLights() { + GridBagConstraints c; + gridbag = new GridBagLayout(); + setLayout(gridbag); + + c = new GridBagConstraints(); + red = new AltosLed("/redled.png", "/redoff.png"); + c.gridx = 0; c.gridy = 0; + gridbag.setConstraints(red, c); + add(red); + red.set(true); + green = new AltosLed("/greenled.png", "/greenoff.png"); + c.gridx = 1; c.gridy = 0; + gridbag.setConstraints(green, c); + add(green); + green.set(false); + } +} \ No newline at end of file diff --git a/ao-tools/altosui/AltosPad.java b/ao-tools/altosui/AltosPad.java new file mode 100644 index 00000000..133dbed3 --- /dev/null +++ b/ao-tools/altosui/AltosPad.java @@ -0,0 +1,249 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosPad extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + Font label_font; + Font value_font; + + public class LaunchStatus { + JLabel label; + JLabel value; + AltosLights lights; + + void show(AltosState state, int crc_errors) {} + void reset() { + value.setText("0"); + lights.set(false); + } + + public LaunchStatus (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + + lights = new AltosLights(); + c.gridx = 0; c.gridy = y; + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.CENTER; + layout.setConstraints(lights, c); + add(lights); + + label = new JLabel(text); + label.setFont(label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(10, 10, 10, 10); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.WEST; + layout.setConstraints(label, c); + add(label); + + value = new JLabel("4.00"); + value.setFont(label_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.EAST; + layout.setConstraints(value, c); + add(value); + + } + } + + public class LaunchValue { + JLabel label; + JLabel value; + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText("0"); + } + public LaunchValue (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + + label = new JLabel(text); + label.setFont(label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(10, 10, 10, 10); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.WEST; + layout.setConstraints(label, c); + add(label); + + value = new JLabel("4.00"); + value.setFont(label_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.EAST; + layout.setConstraints(value, c); + add(value); + } + } + + class Battery extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.battery)); + lights.set(state.battery > 3.7); + } + public Battery (GridBagLayout layout, int y) { + super(layout, y, "Battery Voltage"); + } + } + + Battery battery; + + class Apogee extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.drogue_sense)); + lights.set(state.drogue_sense > 3.2); + } + public Apogee (GridBagLayout layout, int y) { + super(layout, y, "Apogee Igniter Voltage"); + } + } + + Apogee apogee; + + class Main extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.main_sense)); + lights.set(state.main_sense > 3.2); + } + public Main (GridBagLayout layout, int y) { + super(layout, y, "Main Igniter Voltage"); + } + } + + Main main; + + class GPS extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4d sats", state.gps.nsat)); + lights.set(state.gps_ready); + } + public GPS (GridBagLayout layout, int y) { + super (layout, y, "GPS Status"); + } + } + + GPS gps; + + String pos(double p, String pos, String neg) { + String h = pos; + if (p < 0) { + h = neg; + p = -p; + } + int deg = (int) Math.floor(p); + double min = (p - Math.floor(p)) * 60.0; + return String.format("%s %4d° %9.6f", h, deg, min); + } + + class PadLat extends LaunchValue { + void show (AltosState state, int crc_errors) { + value.setText(pos(state.pad_lat,"N", "S")); + } + public PadLat (GridBagLayout layout, int y) { + super (layout, y, "Pad Latitude"); + } + } + + PadLat pad_lat; + + class PadLon extends LaunchValue { + void show (AltosState state, int crc_errors) { + value.setText(pos(state.pad_lon,"E", "W")); + } + public PadLon (GridBagLayout layout, int y) { + super (layout, y, "Pad Longitude"); + } + } + + PadLon pad_lon; + + class PadAlt extends LaunchValue { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.0f m", state.pad_alt)); + } + public PadAlt (GridBagLayout layout, int y) { + super (layout, y, "Pad Altitude"); + } + } + + PadAlt pad_alt; + + public void reset() { + battery.reset(); + apogee.reset(); + main.reset(); + gps.reset(); + pad_lat.reset(); + pad_lon.reset(); + pad_alt.reset(); + } + + public void show(AltosState state, int crc_errors) { + battery.show(state, crc_errors); + apogee.show(state, crc_errors); + main.show(state, crc_errors); + gps.show(state, crc_errors); + pad_lat.show(state, crc_errors); + pad_lon.show(state, crc_errors); + pad_alt.show(state, crc_errors); + } + + public AltosPad() { + layout = new GridBagLayout(); + + GridBagConstraints c; + + label_font = new Font("Dialog", Font.PLAIN, 24); + value_font = new Font("Monospaced", Font.PLAIN, 24); + setLayout(layout); + + c = new GridBagConstraints(); + /* Elements in pad display: + * + * Battery voltage + * Igniter continuity + * GPS lock status and location + * Pad altitude + * RSSI + */ + battery = new Battery(layout, 0); + apogee = new Apogee(layout, 1); + main = new Main(layout, 2); + gps = new GPS(layout, 3); + pad_lat = new PadLat(layout, 4); + pad_lon = new PadLon(layout, 5); + pad_alt = new PadAlt(layout, 6); + } +} diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index ccb88ed1..ab9cf201 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -39,9 +39,12 @@ altosui_JAVA = \ Altos.java \ AltosInfoTable.java \ AltosKML.java \ + AltosLed.java \ + AltosLights.java \ AltosLine.java \ AltosLogfileChooser.java \ AltosLog.java \ + AltosPad.java \ AltosParse.java \ AltosPreferences.java \ AltosReader.java \ @@ -92,8 +95,21 @@ JAR=altosui.jar FATJAR=altosui-fat.jar # Icons -JAVA_ICON=$(top_srcdir)/icon/altus-metrum-16x16.jpg -WINDOWS_ICON=$(top_srcdir)/icon/altus-metrum.ico +ICONDIR=$(top_srcdir)/icon + +JAVA_ICON=$(ICONDIR)/altus-metrum-16x16.jpg + +ICONS= $(ICONDIR)/redled.png $(ICONDIR)/redoff.png \ + $(ICONDIR)/greenled.png $(ICONDIR)/greenoff.png \ + $(ICONDIR)/grayled.png $(ICONDIR)/grayoff.png + +# icon base names for jar +ICONJAR= -C $(ICONDIR) altus-metrum-16x16.jpg \ + -C $(ICONDIR) redled.png -C $(ICONDIR) redoff.png \ + -C $(ICONDIR) greenled.png -C $(ICONDIR) greenoff.png \ + -C $(ICONDIR) grayon.png -C $(ICONDIR) grayled.png + +WINDOWS_ICON=$(ICONDIR)/altus-metrum.ico # Firmware FIRMWARE_TD=$(top_srcdir)/src/teledongle-v0.2-$(VERSION).ihx @@ -163,13 +179,13 @@ classes/altosui: $(JAR): classaltosui.stamp Manifest.txt $(JAVA_ICON) jar cfm $@ Manifest.txt \ - -C $(top_srcdir)/icon altus-metrum-16x16.jpg \ + $(ICONJAR) \ -C classes altosui \ -C ../libaltos libaltosJNI $(FATJAR): classaltosui.stamp Manifest-fat.txt $(FREETTS_CLASS) $(JFREECHART_CLASS) $(JCOMMON_CLASS) $(LIBALTOS) $(JAVA_ICON) jar cfm $@ Manifest-fat.txt \ - -C $(top_srcdir)/icon altus-metrum-16x16.jpg \ + $(ICONJAR) \ -C classes altosui \ -C ../libaltos libaltosJNI diff --git a/icon/grayled.png b/icon/grayled.png new file mode 100644 index 00000000..bb6005c6 Binary files /dev/null and b/icon/grayled.png differ diff --git a/icon/grayon.png b/icon/grayon.png new file mode 100644 index 00000000..c99b376e Binary files /dev/null and b/icon/grayon.png differ diff --git a/icon/greenled.png b/icon/greenled.png new file mode 100644 index 00000000..d7663961 Binary files /dev/null and b/icon/greenled.png differ diff --git a/icon/greenoff.png b/icon/greenoff.png new file mode 100644 index 00000000..c3cf8491 Binary files /dev/null and b/icon/greenoff.png differ diff --git a/icon/redled.png b/icon/redled.png new file mode 100644 index 00000000..230afae0 Binary files /dev/null and b/icon/redled.png differ diff --git a/icon/redoff.png b/icon/redoff.png new file mode 100644 index 00000000..a251402f Binary files /dev/null and b/icon/redoff.png differ -- cgit v1.2.3 From b0d31910da592e2f67c47c8fc3e15ce8135d5094 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 9 Nov 2010 23:34:32 -0800 Subject: altosui: Add ascent, descent and landed tabs This completes the set of tabs for in-flight status information. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosAscent.java | 250 +++++++++++++++++++++++++++++++ ao-tools/altosui/AltosDescent.java | 211 ++++++++++++++++++++++++++ ao-tools/altosui/AltosDisplayThread.java | 14 +- ao-tools/altosui/AltosFlightUI.java | 52 +++++++ ao-tools/altosui/AltosLanded.java | 208 +++++++++++++++++++++++++ ao-tools/altosui/AltosLights.java | 1 + ao-tools/altosui/AltosPad.java | 18 +-- ao-tools/altosui/AltosState.java | 1 + ao-tools/altosui/Makefile.am | 3 + 9 files changed, 743 insertions(+), 15 deletions(-) create mode 100644 ao-tools/altosui/AltosAscent.java create mode 100644 ao-tools/altosui/AltosDescent.java create mode 100644 ao-tools/altosui/AltosLanded.java diff --git a/ao-tools/altosui/AltosAscent.java b/ao-tools/altosui/AltosAscent.java new file mode 100644 index 00000000..40df7af8 --- /dev/null +++ b/ao-tools/altosui/AltosAscent.java @@ -0,0 +1,250 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosAscent extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + Font label_font; + Font value_font; + + public class AscentValue { + JLabel label; + JTextField value; + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + } + public AscentValue (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + + label = new JLabel(text); + label.setFont(label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 0; c.gridy = y; + c.insets = new Insets(10, 10, 10, 10); + c.anchor = GridBagConstraints.WEST; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(20); + value.setFont(label_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 1; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridwidth = 2; + layout.setConstraints(value, c); + add(value); + } + } + + public class AscentValueHold { + JLabel label; + JTextField value; + JTextField max_value; + double max; + + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + max_value.setText(""); + max = 0; + } + + void show(String format, double v) { + value.setText(String.format(format, v)); + if (v > max) { + max_value.setText(String.format(format, v)); + max = v; + } + } + public AscentValueHold (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + + label = new JLabel(text); + label.setFont(label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 0; c.gridy = y; + c.insets = new Insets(10, 10, 10, 10); + c.anchor = GridBagConstraints.WEST; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(10); + value.setFont(label_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 1; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + layout.setConstraints(value, c); + add(value); + + max_value = new JTextField(10); + max_value.setFont(label_font); + max_value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + layout.setConstraints(max_value, c); + add(max_value); + } + } + + + class Height extends AscentValueHold { + void show (AltosState state, int crc_errors) { + show("%6.0f m", state.height); + } + public Height (GridBagLayout layout, int y) { + super (layout, y, "Height"); + } + } + + Height height; + + class Speed extends AscentValueHold { + void show (AltosState state, int crc_errors) { + double speed = state.speed; + if (!state.ascent) + speed = state.baro_speed; + show("%6.0f m/s", speed); + } + public Speed (GridBagLayout layout, int y) { + super (layout, y, "Speed"); + } + } + + Speed speed; + + class Accel extends AscentValueHold { + void show (AltosState state, int crc_errors) { + show("%6.0f m/s²", state.acceleration); + } + public Accel (GridBagLayout layout, int y) { + super (layout, y, "Acceleration"); + } + } + + Accel accel; + + String pos(double p, String pos, String neg) { + String h = pos; + if (p < 0) { + h = neg; + p = -p; + } + int deg = (int) Math.floor(p); + double min = (p - Math.floor(p)) * 60.0; + return String.format("%s %4d° %9.6f", h, deg, min); + } + + class Lat extends AscentValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + value.setText(pos(state.gps.lat,"N", "S")); + else + value.setText("???"); + } + public Lat (GridBagLayout layout, int y) { + super (layout, y, "Latitude"); + } + } + + Lat lat; + + class Lon extends AscentValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + value.setText(pos(state.gps.lon,"E", "W")); + else + value.setText("???"); + } + public Lon (GridBagLayout layout, int y) { + super (layout, y, "Longitude"); + } + } + + Lon lon; + + public void reset() { + lat.reset(); + lon.reset(); + height.reset(); + speed.reset(); + accel.reset(); + } + + public void show(AltosState state, int crc_errors) { + lat.show(state, crc_errors); + lon.show(state, crc_errors); + height.show(state, crc_errors); + speed.show(state, crc_errors); + accel.show(state, crc_errors); + } + + public void labels(GridBagLayout layout, int y) { + GridBagConstraints c; + JLabel cur, max; + + cur = new JLabel("Current"); + cur.setFont(label_font); + c = new GridBagConstraints(); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(10, 10, 10, 10); + layout.setConstraints(cur, c); + add(cur); + + max = new JLabel("Maximum"); + max.setFont(label_font); + c.gridx = 2; c.gridy = y; + layout.setConstraints(max, c); + add(max); + } + + public AltosAscent() { + layout = new GridBagLayout(); + + label_font = new Font("Dialog", Font.PLAIN, 24); + value_font = new Font("Monospace", Font.PLAIN, 24); + setLayout(layout); + + /* Elements in ascent display: + * + * lat + * lon + * height + */ + labels(layout, 0); + height = new Height(layout, 1); + speed = new Speed(layout, 2); + accel = new Accel(layout, 3); + lat = new Lat(layout, 4); + lon = new Lon(layout, 5); + } +} diff --git a/ao-tools/altosui/AltosDescent.java b/ao-tools/altosui/AltosDescent.java new file mode 100644 index 00000000..0d3d17f0 --- /dev/null +++ b/ao-tools/altosui/AltosDescent.java @@ -0,0 +1,211 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosDescent extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + Font label_font; + Font value_font; + + public class DescentValue { + JLabel label; + JTextField value; + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + } + + void show(String format, double v) { + value.setText(String.format(format, v)); + } + + public DescentValue (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + + label = new JLabel(text); + label.setFont(label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 0; c.gridy = y; + c.insets = new Insets(10, 10, 10, 10); + c.anchor = GridBagConstraints.WEST; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(20); + value.setFont(label_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 1; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.HORIZONTAL; + layout.setConstraints(value, c); + add(value); + } + } + + class Height extends DescentValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m", state.height); + } + public Height (GridBagLayout layout, int y) { + super (layout, y, "Height"); + } + } + + Height height; + + class Speed extends DescentValue { + void show (AltosState state, int crc_errors) { + double speed = state.speed; + if (!state.ascent) + speed = state.baro_speed; + show("%6.0f m/s", speed); + } + public Speed (GridBagLayout layout, int y) { + super (layout, y, "Speed"); + } + } + + Speed speed; + + String pos(double p, String pos, String neg) { + String h = pos; + if (p < 0) { + h = neg; + p = -p; + } + int deg = (int) Math.floor(p); + double min = (p - Math.floor(p)) * 60.0; + return String.format("%s %4d° %9.6f", h, deg, min); + } + + class Lat extends DescentValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + value.setText(pos(state.gps.lat,"N", "S")); + else + value.setText("???"); + } + public Lat (GridBagLayout layout, int y) { + super (layout, y, "Latitude"); + } + } + + Lat lat; + + class Lon extends DescentValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + value.setText(pos(state.gps.lon,"E", "W")); + else + value.setText("???"); + } + public Lon (GridBagLayout layout, int y) { + super (layout, y, "Longitude"); + } + } + + Lon lon; + + class Bearing extends DescentValue { + void show (AltosState state, int crc_errors) { + if (state.from_pad != null) + show("%3.0f°", state.from_pad.bearing); + else + value.setText("???"); + } + public Bearing (GridBagLayout layout, int y) { + super (layout, y, "Bearing"); + } + } + + Bearing bearing; + + class Elevation extends DescentValue { + void show (AltosState state, int crc_errors) { + if (state.from_pad != null) + show("%3.0f°", state.elevation); + else + value.setText("???"); + } + public Elevation (GridBagLayout layout, int y) { + super (layout, y, "Elevation"); + } + } + + Elevation elevation; + + class Range extends DescentValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m", state.range); + } + public Range (GridBagLayout layout, int y) { + super (layout, y, "Range"); + } + } + + Range range; + + public void reset() { + lat.reset(); + lon.reset(); + height.reset(); + speed.reset(); + bearing.reset(); + elevation.reset(); + range.reset(); + } + + public void show(AltosState state, int crc_errors) { + height.show(state, crc_errors); + speed.show(state, crc_errors); + bearing.show(state, crc_errors); + elevation.show(state, crc_errors); + range.show(state, crc_errors); + lat.show(state, crc_errors); + lon.show(state, crc_errors); + } + + public AltosDescent() { + layout = new GridBagLayout(); + + label_font = new Font("Dialog", Font.PLAIN, 24); + value_font = new Font("Monospace", Font.PLAIN, 24); + setLayout(layout); + + /* Elements in descent display */ + speed = new Speed(layout, 0); + height = new Height(layout, 1); + bearing = new Bearing(layout, 2); + elevation = new Elevation(layout, 3); + range = new Range(layout, 4); + lat = new Lat(layout, 5); + lon = new Lon(layout, 6); + } +} diff --git a/ao-tools/altosui/AltosDisplayThread.java b/ao-tools/altosui/AltosDisplayThread.java index 957ac0d6..b5b2777e 100644 --- a/ao-tools/altosui/AltosDisplayThread.java +++ b/ao-tools/altosui/AltosDisplayThread.java @@ -38,6 +38,11 @@ public class AltosDisplayThread extends Thread { int crc_errors; AltosFlightDisplay display; + synchronized void show(AltosState state, int crc_errors) { + if (state != null) + display.show(state, crc_errors); + } + class IdleThread extends Thread { boolean started; @@ -93,6 +98,10 @@ public class AltosDisplayThread extends Thread { (int) (state.from_pad.bearing + 0.5), (int) (state.from_pad.distance + 0.5)); ++reported_landing; + if (state.state != Altos.ao_flight_landed) { + state.state = Altos.ao_flight_landed; + show(state, 0); + } } } @@ -180,11 +189,6 @@ public class AltosDisplayThread extends Thread { return ret; } - void show(AltosState state, int crc_errors) { - if (state != null) - display.show(state, crc_errors); - } - public void run() { boolean interrupted = false; String line; diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index a7caf7e9..558b0395 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -41,10 +41,30 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { JTabbedPane pane; AltosPad pad; + AltosAscent ascent; + AltosDescent descent; + AltosLanded landed; private AltosStatusTable flightStatus; private AltosInfoTable flightInfo; + static final int tab_pad = 1; + static final int tab_ascent = 2; + static final int tab_descent = 3; + static final int tab_landed = 4; + + int cur_tab = 0; + + int which_tab(AltosState state) { + if (state.state < Altos.ao_flight_boost) + return tab_pad; + if (state.state <= Altos.ao_flight_coast) + return tab_ascent; + if (state.state <= Altos.ao_flight_main) + return tab_descent; + return tab_landed; + } + public int width() { return flightInfo.width(); } @@ -69,11 +89,34 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { public void reset() { pad.reset(); + ascent.reset(); + descent.reset(); + landed.reset(); flightInfo.clear(); } public void show(AltosState state, int crc_errors) { + int tab = which_tab(state); pad.show(state, crc_errors); + ascent.show(state, crc_errors); + descent.show(state, crc_errors); + landed.show(state, crc_errors); + if (tab != cur_tab) { + switch (tab) { + case tab_pad: + pane.setSelectedComponent(pad); + break; + case tab_ascent: + pane.setSelectedComponent(ascent); + break; + case tab_descent: + pane.setSelectedComponent(descent); + break; + case tab_landed: + pane.setSelectedComponent(landed); + } + cur_tab = tab; + } flightStatus.set(state); flightInfo.show(state, crc_errors); } @@ -98,6 +141,15 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { pad = new AltosPad(); pane.add("Launch Pad", pad); + ascent = new AltosAscent(); + pane.add("Ascent", ascent); + + descent = new AltosDescent(); + pane.add("Descent", descent); + + landed = new AltosLanded(); + pane.add("Landed", landed); + flightInfo = new AltosInfoTable(); pane.add("Table", flightInfo.box()); diff --git a/ao-tools/altosui/AltosLanded.java b/ao-tools/altosui/AltosLanded.java new file mode 100644 index 00000000..4b74aaa3 --- /dev/null +++ b/ao-tools/altosui/AltosLanded.java @@ -0,0 +1,208 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosLanded extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + Font label_font; + Font value_font; + + public class LandedValue { + JLabel label; + JTextField value; + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + } + + void show(String format, double v) { + value.setText(String.format(format, v)); + } + + public LandedValue (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + + label = new JLabel(text); + label.setFont(label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 0; c.gridy = y; + c.insets = new Insets(10, 10, 10, 10); + c.anchor = GridBagConstraints.WEST; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(20); + value.setFont(label_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 1; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.HORIZONTAL; + layout.setConstraints(value, c); + add(value); + } + } + + String pos(double p, String pos, String neg) { + String h = pos; + if (p < 0) { + h = neg; + p = -p; + } + int deg = (int) Math.floor(p); + double min = (p - Math.floor(p)) * 60.0; + return String.format("%s %4d° %9.6f", h, deg, min); + } + + class Lat extends LandedValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + value.setText(pos(state.gps.lat,"N", "S")); + else + value.setText("???"); + } + public Lat (GridBagLayout layout, int y) { + super (layout, y, "Latitude"); + } + } + + Lat lat; + + class Lon extends LandedValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + value.setText(pos(state.gps.lon,"E", "W")); + else + value.setText("???"); + } + public Lon (GridBagLayout layout, int y) { + super (layout, y, "Longitude"); + } + } + + Lon lon; + + class Bearing extends LandedValue { + void show (AltosState state, int crc_errors) { + if (state.from_pad != null) + show("%3.0f°", state.from_pad.bearing); + else + value.setText("???"); + } + public Bearing (GridBagLayout layout, int y) { + super (layout, y, "Bearing"); + } + } + + Bearing bearing; + + class Distance extends LandedValue { + void show (AltosState state, int crc_errors) { + if (state.from_pad != null) + show("%6.0f m", state.from_pad.distance); + else + value.setText("???"); + } + public Distance (GridBagLayout layout, int y) { + super (layout, y, "Distance"); + } + } + + Distance distance; + + class Height extends LandedValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m", state.max_height); + } + public Height (GridBagLayout layout, int y) { + super (layout, y, "Maximum Height"); + } + } + + Height height; + + class Speed extends LandedValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m/s", state.max_speed); + } + public Speed (GridBagLayout layout, int y) { + super (layout, y, "Maximum Speed"); + } + } + + Speed speed; + + class Accel extends LandedValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m/s²", state.max_acceleration); + } + public Accel (GridBagLayout layout, int y) { + super (layout, y, "Maximum Acceleration"); + } + } + + Accel accel; + + public void reset() { + lat.reset(); + lon.reset(); + bearing.reset(); + distance.reset(); + height.reset(); + speed.reset(); + accel.reset(); + } + + public void show(AltosState state, int crc_errors) { + bearing.show(state, crc_errors); + distance.show(state, crc_errors); + lat.show(state, crc_errors); + lon.show(state, crc_errors); + height.show(state, crc_errors); + speed.show(state, crc_errors); + accel.show(state, crc_errors); + } + + public AltosLanded() { + layout = new GridBagLayout(); + + label_font = new Font("Dialog", Font.PLAIN, 24); + value_font = new Font("Monospace", Font.PLAIN, 24); + setLayout(layout); + + /* Elements in descent display */ + bearing = new Bearing(layout, 0); + distance = new Distance(layout, 1); + lat = new Lat(layout, 2); + lon = new Lon(layout, 3); + height = new Height(layout, 4); + speed = new Speed(layout, 5); + accel = new Accel(layout, 6); + } +} diff --git a/ao-tools/altosui/AltosLights.java b/ao-tools/altosui/AltosLights.java index 2d2a1938..f1ed47c2 100644 --- a/ao-tools/altosui/AltosLights.java +++ b/ao-tools/altosui/AltosLights.java @@ -60,6 +60,7 @@ public class AltosLights extends JComponent { c = new GridBagConstraints(); red = new AltosLed("/redled.png", "/redoff.png"); c.gridx = 0; c.gridy = 0; + c.insets = new Insets (0, 5, 0, 5); gridbag.setConstraints(red, c); add(red); red.set(true); diff --git a/ao-tools/altosui/AltosPad.java b/ao-tools/altosui/AltosPad.java index 133dbed3..7b72be20 100644 --- a/ao-tools/altosui/AltosPad.java +++ b/ao-tools/altosui/AltosPad.java @@ -35,12 +35,12 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { public class LaunchStatus { JLabel label; - JLabel value; + JTextField value; AltosLights lights; void show(AltosState state, int crc_errors) {} void reset() { - value.setText("0"); + value.setText(""); lights.set(false); } @@ -64,12 +64,11 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JLabel("4.00"); + value = new JTextField(10); value.setFont(label_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; - c.anchor = GridBagConstraints.EAST; - c.fill = GridBagConstraints.EAST; + c.anchor = GridBagConstraints.WEST; layout.setConstraints(value, c); add(value); @@ -78,11 +77,11 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { public class LaunchValue { JLabel label; - JLabel value; + JTextField value; void show(AltosState state, int crc_errors) {} void reset() { - value.setText("0"); + value.setText(""); } public LaunchValue (GridBagLayout layout, int y, String text) { GridBagConstraints c = new GridBagConstraints(); @@ -93,16 +92,15 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { c.gridx = 1; c.gridy = y; c.insets = new Insets(10, 10, 10, 10); c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.WEST; layout.setConstraints(label, c); add(label); - value = new JLabel("4.00"); + value = new JTextField(20); value.setFont(label_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; c.anchor = GridBagConstraints.EAST; - c.fill = GridBagConstraints.EAST; + c.fill = GridBagConstraints.HORIZONTAL; layout.setConstraints(value, c); add(value); } diff --git a/ao-tools/altosui/AltosState.java b/ao-tools/altosui/AltosState.java index 1048bb51..86eb636a 100644 --- a/ao-tools/altosui/AltosState.java +++ b/ao-tools/altosui/AltosState.java @@ -35,6 +35,7 @@ public class AltosState { int tick; int state; + boolean landed; boolean ascent; /* going up? */ double ground_altitude; diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index ab9cf201..267bae63 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -10,6 +10,7 @@ CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:../libaltos:$(FREETTS)/ bin_SCRIPTS=altosui altosui_JAVA = \ + AltosAscent.java \ AltosChannelMenu.java \ AltosConfig.java \ AltosConfigUI.java \ @@ -18,6 +19,7 @@ altosui_JAVA = \ AltosCSV.java \ AltosCSVUI.java \ AltosDebug.java \ + AltosDescent.java \ AltosDeviceDialog.java \ AltosDevice.java \ AltosDisplayThread.java \ @@ -39,6 +41,7 @@ altosui_JAVA = \ Altos.java \ AltosInfoTable.java \ AltosKML.java \ + AltosLanded.java \ AltosLed.java \ AltosLights.java \ AltosLine.java \ -- cgit v1.2.3 From 891e629f6ba20654b614f3ca7211a0f1c92670cb Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 10 Nov 2010 16:28:19 -0800 Subject: altos: Use grey leds when unlit - easier to see --- ao-tools/altosui/AltosLights.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ao-tools/altosui/AltosLights.java b/ao-tools/altosui/AltosLights.java index f1ed47c2..08e6b4f8 100644 --- a/ao-tools/altosui/AltosLights.java +++ b/ao-tools/altosui/AltosLights.java @@ -58,13 +58,13 @@ public class AltosLights extends JComponent { setLayout(gridbag); c = new GridBagConstraints(); - red = new AltosLed("/redled.png", "/redoff.png"); + red = new AltosLed("/redled.png", "/grayled.png"); c.gridx = 0; c.gridy = 0; c.insets = new Insets (0, 5, 0, 5); gridbag.setConstraints(red, c); add(red); red.set(true); - green = new AltosLed("/greenled.png", "/greenoff.png"); + green = new AltosLed("/greenled.png", "/grayled.png"); c.gridx = 1; c.gridy = 0; gridbag.setConstraints(green, c); add(green); -- cgit v1.2.3 From b16b873723ee3e5097e6725c59ce191119439ad7 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Thu, 11 Nov 2010 15:38:27 +1000 Subject: use value_font for values --- ao-tools/altosui/AltosAscent.java | 14 +++++++------- ao-tools/altosui/AltosDescent.java | 6 +++--- ao-tools/altosui/AltosLanded.java | 6 +++--- ao-tools/altosui/AltosPad.java | 8 ++++---- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ao-tools/altosui/AltosAscent.java b/ao-tools/altosui/AltosAscent.java index 40df7af8..51fa1a89 100644 --- a/ao-tools/altosui/AltosAscent.java +++ b/ao-tools/altosui/AltosAscent.java @@ -53,8 +53,8 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(20); - value.setFont(label_font); + value = new JTextField(30); + value.setFont(value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 1; c.gridy = y; c.anchor = GridBagConstraints.WEST; @@ -98,16 +98,16 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(10); - value.setFont(label_font); + value = new JTextField(15); + value.setFont(value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 1; c.gridy = y; c.anchor = GridBagConstraints.EAST; layout.setConstraints(value, c); add(value); - max_value = new JTextField(10); - max_value.setFont(label_font); + max_value = new JTextField(15); + max_value.setFont(value_font); max_value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; c.anchor = GridBagConstraints.EAST; @@ -231,7 +231,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { layout = new GridBagLayout(); label_font = new Font("Dialog", Font.PLAIN, 24); - value_font = new Font("Monospace", Font.PLAIN, 24); + value_font = new Font("Monospaced", Font.PLAIN, 24); setLayout(layout); /* Elements in ascent display: diff --git a/ao-tools/altosui/AltosDescent.java b/ao-tools/altosui/AltosDescent.java index 0d3d17f0..0b7c8036 100644 --- a/ao-tools/altosui/AltosDescent.java +++ b/ao-tools/altosui/AltosDescent.java @@ -58,8 +58,8 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(20); - value.setFont(label_font); + value = new JTextField(30); + value.setFont(value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 1; c.gridy = y; c.anchor = GridBagConstraints.WEST; @@ -196,7 +196,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { layout = new GridBagLayout(); label_font = new Font("Dialog", Font.PLAIN, 24); - value_font = new Font("Monospace", Font.PLAIN, 24); + value_font = new Font("Monospaced", Font.PLAIN, 24); setLayout(layout); /* Elements in descent display */ diff --git a/ao-tools/altosui/AltosLanded.java b/ao-tools/altosui/AltosLanded.java index 4b74aaa3..d170ccad 100644 --- a/ao-tools/altosui/AltosLanded.java +++ b/ao-tools/altosui/AltosLanded.java @@ -58,8 +58,8 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(20); - value.setFont(label_font); + value = new JTextField(30); + value.setFont(value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 1; c.gridy = y; c.anchor = GridBagConstraints.WEST; @@ -193,7 +193,7 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay { layout = new GridBagLayout(); label_font = new Font("Dialog", Font.PLAIN, 24); - value_font = new Font("Monospace", Font.PLAIN, 24); + value_font = new Font("Monospaced", Font.PLAIN, 24); setLayout(layout); /* Elements in descent display */ diff --git a/ao-tools/altosui/AltosPad.java b/ao-tools/altosui/AltosPad.java index 7b72be20..da047072 100644 --- a/ao-tools/altosui/AltosPad.java +++ b/ao-tools/altosui/AltosPad.java @@ -64,8 +64,8 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(10); - value.setFont(label_font); + value = new JTextField(15); + value.setFont(value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; c.anchor = GridBagConstraints.WEST; @@ -95,8 +95,8 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(20); - value.setFont(label_font); + value = new JTextField(30); + value.setFont(value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; c.anchor = GridBagConstraints.EAST; -- cgit v1.2.3 From 1f3e091efdfb2fe6f06a066cac60f5d267b94856 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Thu, 11 Nov 2010 15:40:37 +1000 Subject: add --replay command line argument --- ao-tools/altosui/AltosUI.java | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index ded9e733..2861444d 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -456,7 +456,26 @@ public class AltosUI extends JFrame { public static void main(final String[] args) { int process = 0; /* Handle batch-mode */ - if (args.length > 0) { + if (args.length == 2 && args[0].equals("--replay")) { + String filename = args[1]; + FileInputStream in; + try { + in = new FileInputStream(filename); + } catch (Exception e) { + System.out.printf("Failed to open file '%s'\n", filename); + return; + } + AltosRecordIterable recs; + AltosReplayReader reader; + if (filename.endsWith("eeprom")) { + recs = new AltosEepromIterable(in); + } else { + recs = new AltosTelemetryIterable(in); + } + reader = new AltosReplayReader(recs.iterator(), filename); + new AltosFlightUI(new AltosVoice(), reader); + return; + } else if (args.length > 0) { for (int i = 0; i < args.length; i++) { if (args[i].equals("--kml")) process |= process_kml; -- cgit v1.2.3 From 3ffaa5d1c00b28be20fd4a26deb7bd41d953e92a Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Thu, 11 Nov 2010 15:43:05 +1000 Subject: read preferences for --replay --- ao-tools/altosui/AltosFlightUI.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 558b0395..3581c54c 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -122,6 +122,8 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { } public AltosFlightUI(AltosVoice in_voice, AltosFlightReader in_reader, final int serial) { + AltosPreferences.init(this); + voice = in_voice; reader = in_reader; -- cgit v1.2.3 From 8503943e3613f8670b128012b12ff14fb54321d7 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Thu, 11 Nov 2010 15:45:43 +1000 Subject: reduce font size for FlightInfoTable --- ao-tools/altosui/AltosInfoTable.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ao-tools/altosui/AltosInfoTable.java b/ao-tools/altosui/AltosInfoTable.java index 9964ab10..2f326e07 100644 --- a/ao-tools/altosui/AltosInfoTable.java +++ b/ao-tools/altosui/AltosInfoTable.java @@ -37,8 +37,8 @@ public class AltosInfoTable { private AltosFlightInfoTableModel model[]; private Box ibox[]; - private Font infoLabelFont = new Font("SansSerif", Font.PLAIN, 14); - private Font infoValueFont = new Font("Monospaced", Font.PLAIN, 14); + private Font infoLabelFont = new Font("SansSerif", Font.PLAIN, 12); + private Font infoValueFont = new Font("Monospaced", Font.PLAIN, 12); static final int info_columns = 3; static final int info_rows = 17; -- cgit v1.2.3 From 317ec72a34906faad88c6924e634617b074e71db Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Thu, 11 Nov 2010 15:52:01 +1000 Subject: use grayled.png for off --- ao-tools/altosui/AltosLights.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ao-tools/altosui/AltosLights.java b/ao-tools/altosui/AltosLights.java index f1ed47c2..2fa38412 100644 --- a/ao-tools/altosui/AltosLights.java +++ b/ao-tools/altosui/AltosLights.java @@ -58,16 +58,16 @@ public class AltosLights extends JComponent { setLayout(gridbag); c = new GridBagConstraints(); - red = new AltosLed("/redled.png", "/redoff.png"); + red = new AltosLed("/redled.png", "/grayled.png"); c.gridx = 0; c.gridy = 0; c.insets = new Insets (0, 5, 0, 5); gridbag.setConstraints(red, c); add(red); red.set(true); - green = new AltosLed("/greenled.png", "/greenoff.png"); + green = new AltosLed("/greenled.png", "/grayled.png"); c.gridx = 1; c.gridy = 0; gridbag.setConstraints(green, c); add(green); green.set(false); } -} \ No newline at end of file +} -- cgit v1.2.3 From cc0a730de093c49be2a921101d27622b6f592e92 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Thu, 11 Nov 2010 15:57:52 +1000 Subject: add compass bearing to voice output --- ao-tools/altosui/AltosDisplayThread.java | 4 +++- ao-tools/altosui/AltosGreatCircle.java | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/ao-tools/altosui/AltosDisplayThread.java b/ao-tools/altosui/AltosDisplayThread.java index b5b2777e..375965b9 100644 --- a/ao-tools/altosui/AltosDisplayThread.java +++ b/ao-tools/altosui/AltosDisplayThread.java @@ -69,8 +69,10 @@ public class AltosDisplayThread extends Thread { state.state < Altos.ao_flight_landed && state.range >= 0) { - voice.speak("Height %d, bearing %d, elevation %d, range %d.\n", + voice.speak("Height %d, bearing %s %d, elevation %d, range %d.\n", (int) (state.height + 0.5), + state.from_pad.bearing_words( + AltosGreatCircle.BEARING_VOICE), (int) (state.from_pad.bearing + 0.5), (int) (state.elevation + 0.5), (int) (state.range + 0.5)); diff --git a/ao-tools/altosui/AltosGreatCircle.java b/ao-tools/altosui/AltosGreatCircle.java index 07c02c16..aa6ae3b9 100644 --- a/ao-tools/altosui/AltosGreatCircle.java +++ b/ao-tools/altosui/AltosGreatCircle.java @@ -30,6 +30,31 @@ public class AltosGreatCircle { static final double rad = Math.PI / 180; static final double earth_radius = 6371.2 * 1000; /* in meters */ + static int BEARING_LONG = 0; + static int BEARING_SHORT = 1; + static int BEARING_VOICE = 2; + String bearing_words(int length) { + String [][] bearing_string = { + { + "North", "North North East", "North East", "East North East", + "East", "East South East", "South East", "South South East", + "South", "South South West", "South West", "West South West", + "West", "West North West", "North West", "North North West" + }, { + "N", "NNE", "NE", "ENE", + "E", "ESE", "SE", "SSE", + "S", "SSW", "SW", "WSW", + "W", "WNW", "NW", "NNW" + }, { + "north", "nor nor east", "north east", "east nor east", + "east", "east sow east", "south east", "sow sow east", + "south", "sow sow west", "south west", "west sow west", + "west", "west nor west", "north west", "nor nor west " + } + }; + return bearing_string[length][(int)((bearing / 90 * 8 + 1) / 2)%16]; + } + public AltosGreatCircle (double start_lat, double start_lon, double end_lat, double end_lon) { -- cgit v1.2.3 From 75f7698b99a661ed17a91748a99699fa6761772a Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Thu, 11 Nov 2010 16:06:32 +1000 Subject: add compass bearing during descent --- ao-tools/altosui/AltosDescent.java | 50 ++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/ao-tools/altosui/AltosDescent.java b/ao-tools/altosui/AltosDescent.java index 0b7c8036..56d3e4fe 100644 --- a/ao-tools/altosui/AltosDescent.java +++ b/ao-tools/altosui/AltosDescent.java @@ -62,6 +62,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { value.setFont(value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 1; c.gridy = y; + c.gridwidth = 2; c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.HORIZONTAL; layout.setConstraints(value, c); @@ -133,15 +134,54 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { Lon lon; - class Bearing extends DescentValue { + class Bearing { + JLabel label; + JTextField value; + JTextField value_deg; + void reset () { + value.setText(""); + value_deg.setText(""); + } void show (AltosState state, int crc_errors) { - if (state.from_pad != null) - show("%3.0f°", state.from_pad.bearing); - else + if (state.from_pad != null) { + value.setText(state.from_pad.bearing_words( + AltosGreatCircle.BEARING_LONG)); + value_deg.setText(String.format("%3.0f°", state.from_pad.bearing)); + } else { value.setText("???"); + value_deg.setText("???"); + } } public Bearing (GridBagLayout layout, int y) { - super (layout, y, "Bearing"); + GridBagConstraints c = new GridBagConstraints(); + + label = new JLabel("Bearing"); + label.setFont(label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 0; c.gridy = y; + c.insets = new Insets(10, 10, 10, 10); + c.anchor = GridBagConstraints.WEST; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(30); + value.setFont(value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 1; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.HORIZONTAL; + layout.setConstraints(value, c); + add(value); + + value_deg = new JTextField(5); + value_deg.setFont(value_font); + value_deg.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.HORIZONTAL; + + layout.setConstraints(value_deg, c); + add(value_deg); } } -- cgit v1.2.3 From 81e7b43ecad666e2e2310c7c94184f888bc86585 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 12 Nov 2010 02:07:41 +1000 Subject: add site map tab, at least for QRS launches --- ao-tools/altosui/AltosFlightUI.java | 6 ++ ao-tools/altosui/AltosSiteMap.java | 137 ++++++++++++++++++++++++++++++++++++ ao-tools/altosui/Makefile.am | 1 + 3 files changed, 144 insertions(+) create mode 100644 ao-tools/altosui/AltosSiteMap.java diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 3581c54c..816ffa23 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -44,6 +44,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { AltosAscent ascent; AltosDescent descent; AltosLanded landed; + AltosSiteMap sitemap; private AltosStatusTable flightStatus; private AltosInfoTable flightInfo; @@ -93,6 +94,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { descent.reset(); landed.reset(); flightInfo.clear(); + sitemap.reset(); } public void show(AltosState state, int crc_errors) { @@ -119,6 +121,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { } flightStatus.set(state); flightInfo.show(state, crc_errors); + sitemap.show(state, crc_errors); } public AltosFlightUI(AltosVoice in_voice, AltosFlightReader in_reader, final int serial) { @@ -155,6 +158,9 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { flightInfo = new AltosInfoTable(); pane.add("Table", flightInfo.box()); + sitemap = new AltosSiteMap(); + pane.add("Site Map", sitemap); + vbox.add(pane); this.add(vbox); diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java new file mode 100644 index 00000000..22b9aebe --- /dev/null +++ b/ao-tools/altosui/AltosSiteMap.java @@ -0,0 +1,137 @@ +/* + * Copyright © 2010 Anthony Towns + * + * 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.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.lang.Math; +import java.awt.geom.Point2D; +import java.awt.geom.Line2D; + +public class AltosSiteMap extends JComponent implements AltosFlightDisplay { + double lat, lng; + int zoom; + double scale_x, scale_y; + Point2D.Double coord_pt; + Point2D.Double last_pt; + + Graphics2D g2d; + + private void setLocation(double new_lat, double new_lng, int new_zoom) { + lat = new_lat; + lng = new_lng; + zoom = new_zoom; + scale_x = 256/360.0 * Math.pow(2, zoom); + scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + coord_pt = pt(lat, lng, new Point2D.Double(0,0)); + coord_pt.x = 320-coord_pt.x; + coord_pt.y = 320-coord_pt.y; + last_pt = null; + } + + private static double limit(double v, double lo, double hi) { + if (v < lo) + return lo; + if (hi < v) + return hi; + return v; + } + + // based on google js + // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js + // search for fromLatLngToPoint and fromPointToLatLng + private Point2D.Double pt(double lat, double lng) { + return pt(lat, lng, coord_pt); + } + + private Point2D.Double pt(double lat, double lng, Point2D.Double centre) { + Point2D.Double res = new Point2D.Double(); + double e; + + res.x = centre.x + lng*scale_x; + e = limit(Math.sin(Math.toRadians(lat)),-(1-1.0E-15),1-1.0E-15); + res.y = centre.y + 0.5*Math.log((1+e)/(1-e))*-scale_y; + return res; + } + + + public void reset() { + // ? + } + + static Color stateColors[] = { + Color.WHITE, // startup + Color.WHITE, // idle + Color.WHITE, // pad + Color.RED, // boost + Color.PINK, // fast + Color.YELLOW, // coast + Color.CYAN, // drogue + Color.BLUE, // main + Color.BLACK // landed + }; + + public void show(AltosState state, int crc_errors) { + if (!state.gps_ready) + return; + Point2D.Double pt = pt(state.gps.lat, state.gps.lon); + if (last_pt != null && pt != last_pt) { + if (0 <= state.state && state.state < stateColors.length) { + g2d.setColor(stateColors[state.state]); + } + g2d.draw(new Line2D.Double(last_pt, pt)); + } + last_pt = pt; + repaint(); + } + + public AltosSiteMap() { + GridBagLayout layout = new GridBagLayout(); + setLayout(layout); + + GridBagConstraints c = new GridBagConstraints(); + + setLocation(-27.850, 152.960, 15); + String pngfile = "/home/aj/qrs-S27.850,W152.960-15.png"; + + c.gridx = 0; c.gridy = 0; + c.weightx = 1; c.weighty = 1; + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + + try { + BufferedImage myPicture = ImageIO.read(new File(pngfile)); + g2d = myPicture.createGraphics(); + JLabel picLabel = new JLabel(new ImageIcon( myPicture )); + JScrollPane scrollPane = new JScrollPane(picLabel); + layout.setConstraints(scrollPane, c); + add(scrollPane); + } catch (Exception e) { + throw new RuntimeException(e); + }; + } +} + diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index 267bae63..fc532863 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -59,6 +59,7 @@ altosui_JAVA = \ AltosRomconfigUI.java \ AltosSerial.java \ AltosSerialMonitor.java \ + AltosSiteMap.java \ AltosState.java \ AltosStatusTable.java \ AltosTelemetry.java \ -- cgit v1.2.3 From 0327c1da01a3f6ede01f05c1d775651a57fd0c68 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 12 Nov 2010 02:08:58 +1000 Subject: tabs -> spaces --- ao-tools/altosui/AltosSiteMap.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 22b9aebe..6f33aabd 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -78,9 +78,9 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { } - public void reset() { - // ? - } + public void reset() { + // ? + } static Color stateColors[] = { Color.WHITE, // startup @@ -94,7 +94,7 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { Color.BLACK // landed }; - public void show(AltosState state, int crc_errors) { + public void show(AltosState state, int crc_errors) { if (!state.gps_ready) return; Point2D.Double pt = pt(state.gps.lat, state.gps.lon); @@ -106,7 +106,7 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { } last_pt = pt; repaint(); - } + } public AltosSiteMap() { GridBagLayout layout = new GridBagLayout(); -- cgit v1.2.3 From beb6c881ec006241c7d2820c64e5381131d41180 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 12 Nov 2010 03:24:26 +1000 Subject: make infotable scrollable, revert its fontsize to 14 --- ao-tools/altosui/AltosFlightUI.java | 2 +- ao-tools/altosui/AltosInfoTable.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 816ffa23..f56b3d1b 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -156,7 +156,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { pane.add("Landed", landed); flightInfo = new AltosInfoTable(); - pane.add("Table", flightInfo.box()); + pane.add("Table", new JScrollPane(flightInfo.box())); sitemap = new AltosSiteMap(); pane.add("Site Map", sitemap); diff --git a/ao-tools/altosui/AltosInfoTable.java b/ao-tools/altosui/AltosInfoTable.java index 2f326e07..9964ab10 100644 --- a/ao-tools/altosui/AltosInfoTable.java +++ b/ao-tools/altosui/AltosInfoTable.java @@ -37,8 +37,8 @@ public class AltosInfoTable { private AltosFlightInfoTableModel model[]; private Box ibox[]; - private Font infoLabelFont = new Font("SansSerif", Font.PLAIN, 12); - private Font infoValueFont = new Font("Monospaced", Font.PLAIN, 12); + private Font infoLabelFont = new Font("SansSerif", Font.PLAIN, 14); + private Font infoValueFont = new Font("Monospaced", Font.PLAIN, 14); static final int info_columns = 3; static final int info_rows = 17; -- cgit v1.2.3 From 1bcfa22de7821984149db10cb79913efed36b41e Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 12 Nov 2010 23:29:40 +1000 Subject: pull up maps for arbitrary locations --- ao-tools/altosui/AltosSiteMap.java | 54 +++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 6f33aabd..420bfc81 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -40,7 +40,8 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { Graphics2D g2d; - private void setLocation(double new_lat, double new_lng, int new_zoom) { + private void setLocation(double new_lat, double new_lng) { + int new_zoom = 15; lat = new_lat; lng = new_lng; zoom = new_zoom; @@ -50,6 +51,17 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { coord_pt.x = 320-coord_pt.x; coord_pt.y = 320-coord_pt.y; last_pt = null; + + try { + File pngfile = new File(AltosPreferences.logdir(), + FileCoord(lat, lng, zoom)); + System.out.printf("Trying file %s\n", pngfile); + BufferedImage myPicture = ImageIO.read(pngfile); + picLabel.setIcon(new ImageIcon( myPicture )); + g2d = myPicture.createGraphics(); + } catch (Exception e) { + throw new RuntimeException(e); + }; } private static double limit(double v, double lo, double hi) { @@ -60,6 +72,16 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { return v; } + private static String FileCoord(double lat, double lng, int zoom) { + char chlat = lat < 0 ? 'S' : 'N'; + char chlng = lng < 0 ? 'E' : 'W'; + if (lat < 0) lat = -lat; + if (lng < 0) lng = -lng; + return String.format("map-%c%.3f,%c%.3f-%d.png", + chlat, lat, chlng, lng, zoom); + } + + // based on google js // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js // search for fromLatLngToPoint and fromPointToLatLng @@ -95,8 +117,15 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { }; public void show(AltosState state, int crc_errors) { - if (!state.gps_ready) + if (!state.gps_ready && state.pad_lat == 0 && state.pad_lon == 0) return; + double plat = (int)(state.pad_lat*200)/200.0; + double plon = (int)(state.pad_lon*200)/200.0; + + if (last_pt == null) { + setLocation(plat, plon); + } + Point2D.Double pt = pt(state.gps.lat, state.gps.lon); if (last_pt != null && pt != last_pt) { if (0 <= state.state && state.state < stateColors.length) { @@ -107,31 +136,24 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { last_pt = pt; repaint(); } - + + JLabel picLabel = new JLabel(); + public AltosSiteMap() { GridBagLayout layout = new GridBagLayout(); setLayout(layout); GridBagConstraints c = new GridBagConstraints(); - setLocation(-27.850, 152.960, 15); - String pngfile = "/home/aj/qrs-S27.850,W152.960-15.png"; - c.gridx = 0; c.gridy = 0; c.weightx = 1; c.weighty = 1; c.anchor = GridBagConstraints.CENTER; c.fill = GridBagConstraints.BOTH; + picLabel = new JLabel(); + JScrollPane scrollPane = new JScrollPane(picLabel); + layout.setConstraints(scrollPane, c); + add(scrollPane); - try { - BufferedImage myPicture = ImageIO.read(new File(pngfile)); - g2d = myPicture.createGraphics(); - JLabel picLabel = new JLabel(new ImageIcon( myPicture )); - JScrollPane scrollPane = new JScrollPane(picLabel); - layout.setConstraints(scrollPane, c); - add(scrollPane); - } catch (Exception e) { - throw new RuntimeException(e); - }; } } -- cgit v1.2.3 From 991541f57f065f429c6ec425efd6ac731280b2c1 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 12 Nov 2010 23:42:42 +1000 Subject: better error behaviour if no map --- ao-tools/altosui/AltosSiteMap.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 420bfc81..1fb70b35 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -40,7 +40,7 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { Graphics2D g2d; - private void setLocation(double new_lat, double new_lng) { + private boolean setLocation(double new_lat, double new_lng) { int new_zoom = 15; lat = new_lat; lng = new_lng; @@ -60,8 +60,10 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { picLabel.setIcon(new ImageIcon( myPicture )); g2d = myPicture.createGraphics(); } catch (Exception e) { - throw new RuntimeException(e); - }; + // throw new RuntimeException(e); + return false; + } + return true; } private static double limit(double v, double lo, double hi) { @@ -116,14 +118,20 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { Color.BLACK // landed }; + boolean nomaps = false; public void show(AltosState state, int crc_errors) { + if (nomaps) + return; if (!state.gps_ready && state.pad_lat == 0 && state.pad_lon == 0) return; double plat = (int)(state.pad_lat*200)/200.0; double plon = (int)(state.pad_lon*200)/200.0; if (last_pt == null) { - setLocation(plat, plon); + if (!setLocation(plat, plon)) { + nomaps = true; + return; + } } Point2D.Double pt = pt(state.gps.lat, state.gps.lon); -- cgit v1.2.3 From a6f30fae906bd87dff192c5fd4d10df283f99930 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 12 Nov 2010 17:02:22 -0800 Subject: altosui: Add RF calibration to TeleMetrum config dialog I think that's the last user-settable value. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosConfig.java | 6 ++++++ ao-tools/altosui/AltosConfigUI.java | 43 ++++++++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/ao-tools/altosui/AltosConfig.java b/ao-tools/altosui/AltosConfig.java index 7fecff0e..b7474f3b 100644 --- a/ao-tools/altosui/AltosConfig.java +++ b/ao-tools/altosui/AltosConfig.java @@ -83,6 +83,7 @@ public class AltosConfig implements Runnable, ActionListener { int_ref main_deploy; int_ref apogee_delay; int_ref radio_channel; + int_ref radio_calibration; string_ref version; string_ref product; string_ref callsign; @@ -146,6 +147,7 @@ public class AltosConfig implements Runnable, ActionListener { get_int(line, "Main deploy:", main_deploy); get_int(line, "Apogee delay:", apogee_delay); get_int(line, "Radio channel:", radio_channel); + get_int(line, "Radio cal:", radio_calibration); get_string(line, "Callsign:", callsign); get_string(line,"software-version", version); get_string(line,"product", product); @@ -175,6 +177,7 @@ public class AltosConfig implements Runnable, ActionListener { 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) { @@ -188,12 +191,14 @@ public class AltosConfig implements Runnable, ActionListener { main_deploy.set(config_ui.main_deploy()); apogee_delay.set(config_ui.apogee_delay()); radio_channel.set(config_ui.radio_channel()); + radio_calibration.set(config_ui.radio_calibration()); callsign.set(config_ui.callsign()); try { start_serial(); serial_line.printf("c m %d\n", main_deploy.get()); serial_line.printf("c d %d\n", apogee_delay.get()); serial_line.printf("c r %d\n", radio_channel.get()); + serial_line.printf("c f %d\n", radio_calibration.get()); serial_line.printf("c c %s\n", callsign.get()); serial_line.printf("c w\n"); } catch (InterruptedException ie) { @@ -234,6 +239,7 @@ public class AltosConfig implements Runnable, ActionListener { main_deploy = new int_ref(250); apogee_delay = new int_ref(0); radio_channel = new int_ref(0); + radio_calibration = new int_ref(1186611); callsign = new string_ref("N0CALL"); version = new string_ref("unknown"); product = new string_ref("unknown"); diff --git a/ao-tools/altosui/AltosConfigUI.java b/ao-tools/altosui/AltosConfigUI.java index 605ccc8b..37128573 100644 --- a/ao-tools/altosui/AltosConfigUI.java +++ b/ao-tools/altosui/AltosConfigUI.java @@ -57,6 +57,7 @@ public class AltosConfigUI JLabel main_deploy_label; JLabel apogee_delay_label; JLabel radio_channel_label; + JLabel radio_calibration_label; JLabel callsign_label; public boolean dirty; @@ -68,6 +69,7 @@ public class AltosConfigUI JComboBox main_deploy_value; JComboBox apogee_delay_value; JComboBox radio_channel_value; + JTextField radio_calibration_value; JTextField callsign_value; JButton save; @@ -256,7 +258,7 @@ public class AltosConfigUI radio_channel_value.addItemListener(this); pane.add(radio_channel_value, c); - /* Callsign */ + /* Radio Calibration */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 6; c.gridwidth = 3; @@ -264,11 +266,34 @@ public class AltosConfigUI c.anchor = GridBagConstraints.LINE_START; c.insets = il; c.ipady = 5; + radio_calibration_label = new JLabel("RF Calibration:"); + pane.add(radio_calibration_label, c); + + c = new GridBagConstraints(); + c.gridx = 3; c.gridy = 6; + c.gridwidth = 3; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + c.ipady = 5; + radio_calibration_value = new JTextField(String.format("%d", 1186611)); + radio_calibration_value.getDocument().addDocumentListener(this); + pane.add(radio_calibration_value, c); + + /* Callsign */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 7; + c.gridwidth = 3; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + c.ipady = 5; callsign_label = new JLabel("Callsign:"); pane.add(callsign_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 6; + c.gridx = 3; c.gridy = 7; c.gridwidth = 3; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; @@ -281,7 +306,7 @@ public class AltosConfigUI /* Buttons */ c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 7; + c.gridx = 0; c.gridy = 8; c.gridwidth = 6; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; @@ -292,7 +317,7 @@ public class AltosConfigUI save.setActionCommand("save"); c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 7; + c.gridx = 0; c.gridy = 8; c.gridwidth = 6; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.CENTER; @@ -303,7 +328,7 @@ public class AltosConfigUI reset.setActionCommand("reset"); c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 7; + c.gridx = 0; c.gridy = 8; c.gridwidth = 6; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_END; @@ -415,6 +440,14 @@ public class AltosConfigUI return radio_channel_value.getSelectedIndex(); } + public void set_radio_calibration(int new_radio_calibration) { + radio_calibration_value.setText(String.format("%d", new_radio_calibration)); + } + + public int radio_calibration() { + return Integer.parseInt(radio_calibration_value.getText()); + } + public void set_callsign(String new_callsign) { callsign_value.setText(new_callsign); } -- cgit v1.2.3 From 1e7e02987276847274493312202d22222c953149 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 14 Nov 2010 00:57:45 +1000 Subject: AltosTelemetryReader: actually open serial port --- ao-tools/altosui/AltosTelemetryReader.java | 1 + 1 file changed, 1 insertion(+) diff --git a/ao-tools/altosui/AltosTelemetryReader.java b/ao-tools/altosui/AltosTelemetryReader.java index 0b5509eb..03e694f8 100644 --- a/ao-tools/altosui/AltosTelemetryReader.java +++ b/ao-tools/altosui/AltosTelemetryReader.java @@ -58,5 +58,6 @@ class AltosTelemetryReader extends AltosFlightReader { telem = new LinkedBlockingQueue(); serial.add_monitor(telem); + serial.open(device); } } -- cgit v1.2.3 From e68fe9454352087889c560d95797922493117acb Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 14 Nov 2010 00:59:01 +1000 Subject: AltosSiteMap: add targeting circles around landing site --- ao-tools/altosui/AltosSiteMap.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 1fb70b35..25b77792 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -118,6 +118,7 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { Color.BLACK // landed }; + boolean drawn_landed_circle = false; boolean nomaps = false; public void show(AltosState state, int crc_errors) { if (nomaps) @@ -141,6 +142,15 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { } g2d.draw(new Line2D.Double(last_pt, pt)); } + + if (state.state == 8 && !drawn_landed_circle) { + drawn_landed_circle = true; + g2d.setColor(Color.RED); + g2d.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10); + g2d.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40); + g2d.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); + } + last_pt = pt; repaint(); } -- cgit v1.2.3 From 8463ffcaca6bcd31e645aba71c171f548dce96d8 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 13 Nov 2010 15:19:14 -0800 Subject: altosui: Eliminate unncessary import altosui lines Java appears to automatically import every module from the current package. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosCSVUI.java | 3 --- ao-tools/altosui/AltosConfig.java | 14 -------------- ao-tools/altosui/AltosConfigUI.java | 13 ------------- ao-tools/altosui/AltosDataPointReader.java | 5 ----- ao-tools/altosui/AltosDebug.java | 2 -- ao-tools/altosui/AltosDeviceDialog.java | 1 - ao-tools/altosui/AltosEepromDownload.java | 12 ------------ ao-tools/altosui/AltosEepromIterable.java | 8 -------- ao-tools/altosui/AltosEepromMonitor.java | 10 ---------- ao-tools/altosui/AltosEepromRecord.java | 11 ----------- ao-tools/altosui/AltosFile.java | 2 -- ao-tools/altosui/AltosFlash.java | 2 -- ao-tools/altosui/AltosFlashUI.java | 3 --- ao-tools/altosui/AltosGPS.java | 2 -- ao-tools/altosui/AltosGraph.java | 2 -- ao-tools/altosui/AltosGraphDataChooser.java | 5 ----- ao-tools/altosui/AltosGraphTime.java | 3 --- ao-tools/altosui/AltosGraphUI.java | 3 --- ao-tools/altosui/AltosGreatCircle.java | 2 -- ao-tools/altosui/AltosInfoTable.java | 3 --- ao-tools/altosui/AltosLog.java | 3 --- ao-tools/altosui/AltosParse.java | 2 -- ao-tools/altosui/AltosReader.java | 2 -- ao-tools/altosui/AltosRecord.java | 2 -- ao-tools/altosui/AltosRecordIterable.java | 8 -------- ao-tools/altosui/AltosRomconfig.java | 1 - ao-tools/altosui/AltosRomconfigUI.java | 2 -- ao-tools/altosui/AltosSerial.java | 9 ++------- ao-tools/altosui/AltosState.java | 3 --- ao-tools/altosui/AltosStatusTable.java | 3 --- ao-tools/altosui/AltosTelemetry.java | 4 ---- ao-tools/altosui/AltosTelemetryIterable.java | 1 - ao-tools/altosui/AltosUI.java | 19 ------------------- 33 files changed, 2 insertions(+), 163 deletions(-) diff --git a/ao-tools/altosui/AltosCSVUI.java b/ao-tools/altosui/AltosCSVUI.java index eb620ba8..16f25338 100644 --- a/ao-tools/altosui/AltosCSVUI.java +++ b/ao-tools/altosui/AltosCSVUI.java @@ -28,9 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosLogfileChooser; -import altosui.AltosCSV; - public class AltosCSVUI extends JDialog implements Runnable, ActionListener diff --git a/ao-tools/altosui/AltosConfig.java b/ao-tools/altosui/AltosConfig.java index b7474f3b..30f7d541 100644 --- a/ao-tools/altosui/AltosConfig.java +++ b/ao-tools/altosui/AltosConfig.java @@ -28,20 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.Altos; -import altosui.AltosSerial; -import altosui.AltosSerialMonitor; -import altosui.AltosRecord; -import altosui.AltosTelemetry; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosFlightStatusTableModel; -import altosui.AltosFlightInfoTableModel; -import altosui.AltosConfigUI; - import libaltosJNI.*; public class AltosConfig implements Runnable, ActionListener { diff --git a/ao-tools/altosui/AltosConfigUI.java b/ao-tools/altosui/AltosConfigUI.java index 37128573..9e3856b0 100644 --- a/ao-tools/altosui/AltosConfigUI.java +++ b/ao-tools/altosui/AltosConfigUI.java @@ -29,19 +29,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.Altos; -import altosui.AltosSerial; -import altosui.AltosSerialMonitor; -import altosui.AltosRecord; -import altosui.AltosTelemetry; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosFlightStatusTableModel; -import altosui.AltosFlightInfoTableModel; - import libaltosJNI.*; public class AltosConfigUI diff --git a/ao-tools/altosui/AltosDataPointReader.java b/ao-tools/altosui/AltosDataPointReader.java index 4d7831e4..7704310b 100644 --- a/ao-tools/altosui/AltosDataPointReader.java +++ b/ao-tools/altosui/AltosDataPointReader.java @@ -10,11 +10,6 @@ import java.lang.UnsupportedOperationException; import java.util.NoSuchElementException; import java.util.Iterator; -import altosui.AltosDataPoint; -import altosui.AltosRecordIterable; -import altosui.AltosRecord; -import altosui.AltosState; - class AltosDataPointReader implements Iterable { Iterator iter; AltosState state; diff --git a/ao-tools/altosui/AltosDebug.java b/ao-tools/altosui/AltosDebug.java index 3f469d48..9c10129d 100644 --- a/ao-tools/altosui/AltosDebug.java +++ b/ao-tools/altosui/AltosDebug.java @@ -22,8 +22,6 @@ import java.io.*; import java.util.concurrent.LinkedBlockingQueue; import java.util.LinkedList; import java.util.Iterator; -import altosui.AltosSerial; -import altosui.AltosRomconfig; public class AltosDebug extends AltosSerial { diff --git a/ao-tools/altosui/AltosDeviceDialog.java b/ao-tools/altosui/AltosDeviceDialog.java index ec78e288..2966ad1e 100644 --- a/ao-tools/altosui/AltosDeviceDialog.java +++ b/ao-tools/altosui/AltosDeviceDialog.java @@ -26,7 +26,6 @@ import libaltosJNI.libaltos; import libaltosJNI.altos_device; import libaltosJNI.SWIGTYPE_p_altos_file; import libaltosJNI.SWIGTYPE_p_altos_list; -import altosui.AltosDevice; public class AltosDeviceDialog extends JDialog implements ActionListener { diff --git a/ao-tools/altosui/AltosEepromDownload.java b/ao-tools/altosui/AltosEepromDownload.java index 8efc94d2..bd9e4b48 100644 --- a/ao-tools/altosui/AltosEepromDownload.java +++ b/ao-tools/altosui/AltosEepromDownload.java @@ -28,18 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.Altos; -import altosui.AltosSerial; -import altosui.AltosSerialMonitor; -import altosui.AltosRecord; -import altosui.AltosTelemetry; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosEepromMonitor; - import libaltosJNI.*; public class AltosEepromDownload implements Runnable { diff --git a/ao-tools/altosui/AltosEepromIterable.java b/ao-tools/altosui/AltosEepromIterable.java index 2f1e7e90..fc683321 100644 --- a/ao-tools/altosui/AltosEepromIterable.java +++ b/ao-tools/altosui/AltosEepromIterable.java @@ -28,14 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosRecord; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosEepromMonitor; - /* * AltosRecords with an index field so they can be sorted by tick while preserving * the original ordering for elements with matching ticks diff --git a/ao-tools/altosui/AltosEepromMonitor.java b/ao-tools/altosui/AltosEepromMonitor.java index b88fdd29..7ff00ead 100644 --- a/ao-tools/altosui/AltosEepromMonitor.java +++ b/ao-tools/altosui/AltosEepromMonitor.java @@ -28,16 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosSerial; -import altosui.AltosSerialMonitor; -import altosui.AltosRecord; -import altosui.AltosTelemetry; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; - public class AltosEepromMonitor extends JDialog { Container pane; diff --git a/ao-tools/altosui/AltosEepromRecord.java b/ao-tools/altosui/AltosEepromRecord.java index 95cbe015..5a673817 100644 --- a/ao-tools/altosui/AltosEepromRecord.java +++ b/ao-tools/altosui/AltosEepromRecord.java @@ -28,17 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosSerial; -import altosui.AltosSerialMonitor; -import altosui.AltosRecord; -import altosui.AltosTelemetry; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosEepromMonitor; - public class AltosEepromRecord { public int cmd; public int tick; diff --git a/ao-tools/altosui/AltosFile.java b/ao-tools/altosui/AltosFile.java index 7f65381f..06360572 100644 --- a/ao-tools/altosui/AltosFile.java +++ b/ao-tools/altosui/AltosFile.java @@ -20,8 +20,6 @@ package altosui; import java.lang.*; import java.io.File; import java.util.*; -import altosui.AltosTelemetry; -import altosui.AltosPreferences; class AltosFile extends File { diff --git a/ao-tools/altosui/AltosFlash.java b/ao-tools/altosui/AltosFlash.java index a3e431cd..25b4a06e 100644 --- a/ao-tools/altosui/AltosFlash.java +++ b/ao-tools/altosui/AltosFlash.java @@ -28,8 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosHexfile; - public class AltosFlash { File file; FileInputStream input; diff --git a/ao-tools/altosui/AltosFlashUI.java b/ao-tools/altosui/AltosFlashUI.java index 86f57a5f..70c8c549 100644 --- a/ao-tools/altosui/AltosFlashUI.java +++ b/ao-tools/altosui/AltosFlashUI.java @@ -28,9 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosHexfile; -import altosui.AltosFlash; - public class AltosFlashUI extends JDialog implements Runnable, ActionListener diff --git a/ao-tools/altosui/AltosGPS.java b/ao-tools/altosui/AltosGPS.java index acb6fb2c..83821842 100644 --- a/ao-tools/altosui/AltosGPS.java +++ b/ao-tools/altosui/AltosGPS.java @@ -19,8 +19,6 @@ package altosui; import java.lang.*; import java.text.*; -import altosui.AltosParse; - public class AltosGPS { public class AltosGPSSat { diff --git a/ao-tools/altosui/AltosGraph.java b/ao-tools/altosui/AltosGraph.java index fa3b87c1..58c27979 100644 --- a/ao-tools/altosui/AltosGraph.java +++ b/ao-tools/altosui/AltosGraph.java @@ -9,8 +9,6 @@ import java.io.*; import org.jfree.chart.JFreeChart; import org.jfree.chart.ChartUtilities; -import altosui.AltosDataPoint; - abstract class AltosGraph { public String filename; public abstract void addData(AltosDataPoint d); diff --git a/ao-tools/altosui/AltosGraphDataChooser.java b/ao-tools/altosui/AltosGraphDataChooser.java index caa14118..d128f4d5 100644 --- a/ao-tools/altosui/AltosGraphDataChooser.java +++ b/ao-tools/altosui/AltosGraphDataChooser.java @@ -27,11 +27,6 @@ import java.util.*; import java.text.*; import java.util.prefs.*; -import altosui.AltosPreferences; -import altosui.AltosDataPointReader; -import altosui.AltosEepromIterable; -import altosui.AltosTelemetryIterable; - public class AltosGraphDataChooser extends JFileChooser { JFrame frame; String filename; diff --git a/ao-tools/altosui/AltosGraphTime.java b/ao-tools/altosui/AltosGraphTime.java index ab01b888..a5451280 100644 --- a/ao-tools/altosui/AltosGraphTime.java +++ b/ao-tools/altosui/AltosGraphTime.java @@ -24,9 +24,6 @@ import org.jfree.data.xy.XYSeriesCollection; import org.jfree.ui.RectangleAnchor; import org.jfree.ui.TextAnchor; -import altosui.AltosDataPoint; -import altosui.AltosGraph; - class AltosGraphTime extends AltosGraph { static interface Element { void attachGraph(AltosGraphTime g); diff --git a/ao-tools/altosui/AltosGraphUI.java b/ao-tools/altosui/AltosGraphUI.java index 25643c76..908aa3b4 100644 --- a/ao-tools/altosui/AltosGraphUI.java +++ b/ao-tools/altosui/AltosGraphUI.java @@ -17,9 +17,6 @@ import org.jfree.chart.axis.AxisLocation; import org.jfree.ui.ApplicationFrame; import org.jfree.ui.RefineryUtilities; -import altosui.AltosDataPoint; -import altosui.AltosGraphTime; - public class AltosGraphUI extends JFrame { static final private Color red = new Color(194,31,31); diff --git a/ao-tools/altosui/AltosGreatCircle.java b/ao-tools/altosui/AltosGreatCircle.java index aa6ae3b9..fb1b6ab3 100644 --- a/ao-tools/altosui/AltosGreatCircle.java +++ b/ao-tools/altosui/AltosGreatCircle.java @@ -17,8 +17,6 @@ package altosui; -import altosui.AltosGPS; - import java.lang.Math; public class AltosGreatCircle { diff --git a/ao-tools/altosui/AltosInfoTable.java b/ao-tools/altosui/AltosInfoTable.java index 2f326e07..28924410 100644 --- a/ao-tools/altosui/AltosInfoTable.java +++ b/ao-tools/altosui/AltosInfoTable.java @@ -28,9 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosFlightInfoTableModel; -import altosui.AltosState; - public class AltosInfoTable { private Box box; private JTable table[]; diff --git a/ao-tools/altosui/AltosLog.java b/ao-tools/altosui/AltosLog.java index fed96c28..137147d5 100644 --- a/ao-tools/altosui/AltosLog.java +++ b/ao-tools/altosui/AltosLog.java @@ -22,9 +22,6 @@ import java.lang.*; import java.util.*; import java.text.ParseException; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosSerial; -import altosui.AltosFile; -import altosui.AltosLine; /* * This creates a thread to capture telemetry data and write it to diff --git a/ao-tools/altosui/AltosParse.java b/ao-tools/altosui/AltosParse.java index 4d82de78..fbfcaaee 100644 --- a/ao-tools/altosui/AltosParse.java +++ b/ao-tools/altosui/AltosParse.java @@ -20,8 +20,6 @@ package altosui; import java.text.*; import java.lang.*; -import altosui.Altos; - public class AltosParse { static boolean isdigit(char c) { return '0' <= c && c <= '9'; diff --git a/ao-tools/altosui/AltosReader.java b/ao-tools/altosui/AltosReader.java index 5be8795d..b9280a0c 100644 --- a/ao-tools/altosui/AltosReader.java +++ b/ao-tools/altosui/AltosReader.java @@ -21,8 +21,6 @@ import java.io.*; import java.util.*; import java.text.*; -import altosui.AltosRecord; - public class AltosReader { public AltosRecord read() throws IOException, ParseException { return null; } public void close() { } diff --git a/ao-tools/altosui/AltosRecord.java b/ao-tools/altosui/AltosRecord.java index 00484767..1160a273 100644 --- a/ao-tools/altosui/AltosRecord.java +++ b/ao-tools/altosui/AltosRecord.java @@ -21,8 +21,6 @@ import java.lang.*; import java.text.*; import java.util.HashMap; import java.io.*; -import altosui.AltosConvert; -import altosui.AltosGPS; public class AltosRecord { int version; diff --git a/ao-tools/altosui/AltosRecordIterable.java b/ao-tools/altosui/AltosRecordIterable.java index 147ecc14..a7df92d1 100644 --- a/ao-tools/altosui/AltosRecordIterable.java +++ b/ao-tools/altosui/AltosRecordIterable.java @@ -28,14 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosRecord; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosEepromMonitor; - public abstract class AltosRecordIterable implements Iterable { public abstract Iterator iterator(); public void write_comments(PrintStream out) { } diff --git a/ao-tools/altosui/AltosRomconfig.java b/ao-tools/altosui/AltosRomconfig.java index 22d2dbd3..55056b5e 100644 --- a/ao-tools/altosui/AltosRomconfig.java +++ b/ao-tools/altosui/AltosRomconfig.java @@ -17,7 +17,6 @@ package altosui; import java.io.*; -import altosui.AltosHexfile; public class AltosRomconfig { public boolean valid; diff --git a/ao-tools/altosui/AltosRomconfigUI.java b/ao-tools/altosui/AltosRomconfigUI.java index 2134975d..e1dc974e 100644 --- a/ao-tools/altosui/AltosRomconfigUI.java +++ b/ao-tools/altosui/AltosRomconfigUI.java @@ -28,8 +28,6 @@ import java.util.*; import java.text.*; import java.util.prefs.*; -import altosui.AltosRomconfig; - public class AltosRomconfigUI extends JDialog implements ActionListener diff --git a/ao-tools/altosui/AltosSerial.java b/ao-tools/altosui/AltosSerial.java index d6848e57..6787e0c8 100644 --- a/ao-tools/altosui/AltosSerial.java +++ b/ao-tools/altosui/AltosSerial.java @@ -26,13 +26,8 @@ import java.io.*; import java.util.concurrent.LinkedBlockingQueue; import java.util.LinkedList; import java.util.Iterator; -import altosui.AltosSerialMonitor; -import altosui.AltosLine; -import libaltosJNI.libaltos; -import libaltosJNI.altos_device; -import libaltosJNI.SWIGTYPE_p_altos_file; -import libaltosJNI.SWIGTYPE_p_altos_list; -import libaltosJNI.libaltosConstants; + +import libaltosJNI.*; /* * This class reads from the serial port and places each received diff --git a/ao-tools/altosui/AltosState.java b/ao-tools/altosui/AltosState.java index 86eb636a..ec499d5a 100644 --- a/ao-tools/altosui/AltosState.java +++ b/ao-tools/altosui/AltosState.java @@ -21,9 +21,6 @@ package altosui; -import altosui.AltosRecord; -import altosui.AltosGPS; - public class AltosState { AltosRecord data; diff --git a/ao-tools/altosui/AltosStatusTable.java b/ao-tools/altosui/AltosStatusTable.java index 0d3a5d14..02c6232f 100644 --- a/ao-tools/altosui/AltosStatusTable.java +++ b/ao-tools/altosui/AltosStatusTable.java @@ -28,9 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosFlightStatusTableModel; -import altosui.AltosFlightInfoTableModel; - public class AltosStatusTable extends JTable { private AltosFlightStatusTableModel flightStatusModel; diff --git a/ao-tools/altosui/AltosTelemetry.java b/ao-tools/altosui/AltosTelemetry.java index be22dac6..bdb6466a 100644 --- a/ao-tools/altosui/AltosTelemetry.java +++ b/ao-tools/altosui/AltosTelemetry.java @@ -20,10 +20,6 @@ package altosui; import java.lang.*; import java.text.*; import java.util.HashMap; -import altosui.AltosConvert; -import altosui.AltosRecord; -import altosui.AltosGPS; -import altosui.AltosCRCException; /* * Telemetry data contents diff --git a/ao-tools/altosui/AltosTelemetryIterable.java b/ao-tools/altosui/AltosTelemetryIterable.java index 0a125c98..a71ab872 100644 --- a/ao-tools/altosui/AltosTelemetryIterable.java +++ b/ao-tools/altosui/AltosTelemetryIterable.java @@ -20,7 +20,6 @@ package altosui; import java.io.*; import java.util.*; import java.text.*; -import altosui.AltosTelemetry; public class AltosTelemetryIterable extends AltosRecordIterable { LinkedList records; diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index 2861444d..9ab451de 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -28,25 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.Altos; -import altosui.AltosSerial; -import altosui.AltosSerialMonitor; -import altosui.AltosRecord; -import altosui.AltosTelemetry; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosFlightInfoTableModel; -import altosui.AltosFlashUI; -import altosui.AltosLogfileChooser; -import altosui.AltosCSVUI; -import altosui.AltosLine; -import altosui.AltosStatusTable; -import altosui.AltosInfoTable; -import altosui.AltosDisplayThread; - import libaltosJNI.*; public class AltosUI extends JFrame { -- cgit v1.2.3 From dcfa56498d1b65a213b8aba9cbd6c4806532383c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 13 Nov 2010 16:07:04 -0800 Subject: altosui: Open serial device at 'new' time. Prohibit duplicate opens. With the per-serial UI, there's never a reason to create a serial device without opening it right away. This eliminates the bug caused by not opening the serial device for telemetry reception. Serial devices can now be opened only once; this eliminates errors when trying to reflash or configure devices while receiving telemetry. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosConfig.java | 9 +++++++-- ao-tools/altosui/AltosDebug.java | 11 ++++++++--- ao-tools/altosui/AltosEepromDownload.java | 9 +++++++-- ao-tools/altosui/AltosFlash.java | 13 +++++-------- ao-tools/altosui/AltosFlashUI.java | 11 ++++++++--- ao-tools/altosui/AltosSerial.java | 28 +++++++++++++++++----------- ao-tools/altosui/AltosTelemetryReader.java | 5 +++-- ao-tools/altosui/AltosUI.java | 6 ++++++ ao-tools/altosui/Makefile.am | 1 + 9 files changed, 62 insertions(+), 31 deletions(-) diff --git a/ao-tools/altosui/AltosConfig.java b/ao-tools/altosui/AltosConfig.java index 30f7d541..09e204a9 100644 --- a/ao-tools/altosui/AltosConfig.java +++ b/ao-tools/altosui/AltosConfig.java @@ -231,10 +231,9 @@ public class AltosConfig implements Runnable, ActionListener { product = new string_ref("unknown"); device = AltosDeviceDialog.show(owner, AltosDevice.product_any); - serial_line = new AltosSerial(); if (device != null) { try { - serial_line.open(device); + serial_line = new AltosSerial(device); if (!device.matchProduct(AltosDevice.product_telemetrum)) remote = true; config_thread = new Thread(this); @@ -245,6 +244,12 @@ public class AltosConfig implements Runnable, ActionListener { device.getPath()), "Cannot open target device", JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(owner, + String.format("Device \"%s\" already in use", + device.getPath()), + "Device in use", + JOptionPane.ERROR_MESSAGE); } catch (IOException ee) { JOptionPane.showMessageDialog(owner, device.getPath(), diff --git a/ao-tools/altosui/AltosDebug.java b/ao-tools/altosui/AltosDebug.java index 9c10129d..9aa35d3f 100644 --- a/ao-tools/altosui/AltosDebug.java +++ b/ao-tools/altosui/AltosDebug.java @@ -19,9 +19,10 @@ package altosui; import java.lang.*; import java.io.*; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.LinkedList; -import java.util.Iterator; +import java.util.concurrent.*; +import java.util.*; + +import libaltosJNI.*; public class AltosDebug extends AltosSerial { @@ -259,4 +260,8 @@ public class AltosDebug extends AltosSerial { public void reset() { printf ("R\n"); } + + public AltosDebug (altos_device in_device) throws FileNotFoundException, AltosSerialInUseException { + super(in_device); + } } \ No newline at end of file diff --git a/ao-tools/altosui/AltosEepromDownload.java b/ao-tools/altosui/AltosEepromDownload.java index bd9e4b48..8996b924 100644 --- a/ao-tools/altosui/AltosEepromDownload.java +++ b/ao-tools/altosui/AltosEepromDownload.java @@ -244,12 +244,11 @@ public class AltosEepromDownload implements Runnable { frame = given_frame; device = AltosDeviceDialog.show(frame, AltosDevice.product_any); - serial_line = new AltosSerial(); remote = false; if (device != null) { try { - serial_line.open(device); + serial_line = new AltosSerial(device); if (!device.matchProduct(AltosDevice.product_telemetrum)) remote = true; eeprom_thread = new Thread(this); @@ -260,6 +259,12 @@ public class AltosEepromDownload implements Runnable { device.getPath()), "Cannot open target device", JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(frame, + String.format("Device \"%s\" already in use", + device.getPath()), + "Device in use", + JOptionPane.ERROR_MESSAGE); } catch (IOException ee) { JOptionPane.showMessageDialog(frame, device.getPath(), diff --git a/ao-tools/altosui/AltosFlash.java b/ao-tools/altosui/AltosFlash.java index 25b4a06e..fa2465d3 100644 --- a/ao-tools/altosui/AltosFlash.java +++ b/ao-tools/altosui/AltosFlash.java @@ -329,17 +329,14 @@ public class AltosFlash { return rom_config; } - public void open() throws IOException, FileNotFoundException, InterruptedException { + public AltosFlash(File in_file, AltosDevice in_debug_dongle) + throws IOException, FileNotFoundException, AltosSerialInUseException, InterruptedException { + file = in_file; + debug_dongle = in_debug_dongle; + debug = new AltosDebug(in_debug_dongle); input = new FileInputStream(file); image = new AltosHexfile(input); - debug.open(debug_dongle); if (!debug.check_connection()) throw new IOException("Debug port not connected"); } - - public AltosFlash(File in_file, AltosDevice in_debug_dongle) { - file = in_file; - debug_dongle = in_debug_dongle; - debug = new AltosDebug(); - } } \ No newline at end of file diff --git a/ao-tools/altosui/AltosFlashUI.java b/ao-tools/altosui/AltosFlashUI.java index 70c8c549..b09cb594 100644 --- a/ao-tools/altosui/AltosFlashUI.java +++ b/ao-tools/altosui/AltosFlashUI.java @@ -65,10 +65,9 @@ public class AltosFlashUI } public void run() { - flash = new AltosFlash(file, debug_dongle); - flash.addActionListener(this); try { - flash.open(); + flash = new AltosFlash(file, debug_dongle); + flash.addActionListener(this); AltosRomconfigUI romconfig_ui = new AltosRomconfigUI (frame); romconfig_ui.set(flash.romconfig()); @@ -88,6 +87,12 @@ public class AltosFlashUI "Cannot open image", file.toString(), JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(frame, + String.format("Device \"%s\" already in use", + debug_dongle.getPath()), + "Device in use", + JOptionPane.ERROR_MESSAGE); } catch (IOException e) { JOptionPane.showMessageDialog(frame, e.getMessage(), diff --git a/ao-tools/altosui/AltosSerial.java b/ao-tools/altosui/AltosSerial.java index 6787e0c8..99a92fdb 100644 --- a/ao-tools/altosui/AltosSerial.java +++ b/ao-tools/altosui/AltosSerial.java @@ -23,9 +23,8 @@ package altosui; import java.lang.*; import java.io.*; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.LinkedList; -import java.util.Iterator; +import java.util.concurrent.*; +import java.util.*; import libaltosJNI.*; @@ -37,6 +36,9 @@ import libaltosJNI.*; public class AltosSerial implements Runnable { + static List devices_opened = Collections.synchronizedList(new LinkedList()); + + altos_device device; SWIGTYPE_p_altos_file altos; LinkedList> monitors; LinkedBlockingQueue reply_queue; @@ -141,10 +143,6 @@ public class AltosSerial implements Runnable { set_monitor(false); } - public boolean opened() { - return altos != null; - } - public void close() { if (altos != null) { libaltos.altos_close(altos); @@ -161,6 +159,9 @@ public class AltosSerial implements Runnable { libaltos.altos_free(altos); altos = null; } + synchronized (devices_opened) { + devices_opened.remove(device.getPath()); + } } public void putc(char c) { @@ -178,7 +179,12 @@ public class AltosSerial implements Runnable { print(String.format(format, arguments)); } - public void open(altos_device device) throws FileNotFoundException { + private void open() throws FileNotFoundException, AltosSerialInUseException { + synchronized (devices_opened) { + if (devices_opened.contains(device.getPath())) + throw new AltosSerialInUseException(device); + devices_opened.add(device.getPath()); + } close(); altos = libaltos.altos_open(device); if (altos == null) @@ -220,12 +226,12 @@ public class AltosSerial implements Runnable { } } - public AltosSerial() { - altos = null; - input_thread = null; + public AltosSerial(altos_device in_device) throws FileNotFoundException, AltosSerialInUseException { + device = in_device; line = ""; monitor_mode = false; monitors = new LinkedList> (); reply_queue = new LinkedBlockingQueue (); + open(); } } diff --git a/ao-tools/altosui/AltosTelemetryReader.java b/ao-tools/altosui/AltosTelemetryReader.java index 0b5509eb..ff02c722 100644 --- a/ao-tools/altosui/AltosTelemetryReader.java +++ b/ao-tools/altosui/AltosTelemetryReader.java @@ -50,9 +50,10 @@ class AltosTelemetryReader extends AltosFlightReader { serial.set_callsign(callsign); } - public AltosTelemetryReader (AltosDevice in_device) throws FileNotFoundException, IOException { + public AltosTelemetryReader (AltosDevice in_device) + throws FileNotFoundException, AltosSerialInUseException, IOException { device = in_device; - serial = new AltosSerial(); + serial = new AltosSerial(device); log = new AltosLog(serial); name = device.getPath(); diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index 9ab451de..0d8f0e8d 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -56,6 +56,12 @@ public class AltosUI extends JFrame { device.getPath()), "Cannot open target device", JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(AltosUI.this, + String.format("Device \"%s\" already in use", + device.getPath()), + "Device in use", + JOptionPane.ERROR_MESSAGE); } catch (IOException ee) { JOptionPane.showMessageDialog(AltosUI.this, device.getPath(), diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index 267bae63..f4c743df 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -58,6 +58,7 @@ altosui_JAVA = \ AltosRomconfig.java \ AltosRomconfigUI.java \ AltosSerial.java \ + AltosSerialInUseException.java \ AltosSerialMonitor.java \ AltosState.java \ AltosStatusTable.java \ -- cgit v1.2.3 From 11c95f687b1f68d35fa1a0af2c4e7982b8bb226a Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 13 Nov 2010 17:09:51 -0800 Subject: altosui: Replace flight status table with labels, fix resize. There's no reason to use a table for the flight status data, replace that with a selection of widgets instead. Also, set all of the grid bag constraints for the various flight status displays so that resize does something sensible. Adds a scrollbar to the table display so that it can shrink. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosAscent.java | 13 ++- ao-tools/altosui/AltosDescent.java | 78 +++++++++------- ao-tools/altosui/AltosFlightStatus.java | 157 ++++++++++++++++++++++++++++++++ ao-tools/altosui/AltosFlightUI.java | 10 +- ao-tools/altosui/AltosLanded.java | 6 +- ao-tools/altosui/AltosPad.java | 20 ++-- ao-tools/altosui/AltosStatusTable.java | 67 -------------- ao-tools/altosui/Makefile.am | 3 +- 8 files changed, 237 insertions(+), 117 deletions(-) create mode 100644 ao-tools/altosui/AltosFlightStatus.java delete mode 100644 ao-tools/altosui/AltosStatusTable.java diff --git a/ao-tools/altosui/AltosAscent.java b/ao-tools/altosui/AltosAscent.java index 51fa1a89..7525b655 100644 --- a/ao-tools/altosui/AltosAscent.java +++ b/ao-tools/altosui/AltosAscent.java @@ -43,6 +43,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { } public AscentValue (GridBagLayout layout, int y, String text) { GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; label = new JLabel(text); label.setFont(label_font); @@ -50,6 +51,8 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { c.gridx = 0; c.gridy = y; c.insets = new Insets(10, 10, 10, 10); c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; layout.setConstraints(label, c); add(label); @@ -58,8 +61,9 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 1; c.gridy = y; c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.HORIZONTAL; + c.fill = GridBagConstraints.BOTH; c.gridwidth = 2; + c.weightx = 1; layout.setConstraints(value, c); add(value); } @@ -88,6 +92,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { } public AscentValueHold (GridBagLayout layout, int y, String text) { GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; label = new JLabel(text); label.setFont(label_font); @@ -95,6 +100,8 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { c.gridx = 0; c.gridy = y; c.insets = new Insets(10, 10, 10, 10); c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; layout.setConstraints(label, c); add(label); @@ -103,6 +110,8 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 1; c.gridy = y; c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; layout.setConstraints(value, c); add(value); @@ -111,6 +120,8 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { max_value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; layout.setConstraints(max_value, c); add(max_value); } diff --git a/ao-tools/altosui/AltosDescent.java b/ao-tools/altosui/AltosDescent.java index 56d3e4fe..b69e36b6 100644 --- a/ao-tools/altosui/AltosDescent.java +++ b/ao-tools/altosui/AltosDescent.java @@ -48,6 +48,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { public DescentValue (GridBagLayout layout, int y, String text) { GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; label = new JLabel(text); label.setFont(label_font); @@ -55,6 +56,8 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { c.gridx = 0; c.gridy = y; c.insets = new Insets(10, 10, 10, 10); c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; layout.setConstraints(label, c); add(label); @@ -64,7 +67,8 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { c.gridx = 1; c.gridy = y; c.gridwidth = 2; c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.HORIZONTAL; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; layout.setConstraints(value, c); add(value); } @@ -138,50 +142,54 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { JLabel label; JTextField value; JTextField value_deg; - void reset () { + void reset () { value.setText(""); value_deg.setText(""); - } + } void show (AltosState state, int crc_errors) { if (state.from_pad != null) { - value.setText(state.from_pad.bearing_words( - AltosGreatCircle.BEARING_LONG)); + value.setText(state.from_pad.bearing_words( + AltosGreatCircle.BEARING_LONG)); value_deg.setText(String.format("%3.0f°", state.from_pad.bearing)); } else { value.setText("???"); value_deg.setText("???"); - } + } } public Bearing (GridBagLayout layout, int y) { - GridBagConstraints c = new GridBagConstraints(); - - label = new JLabel("Bearing"); - label.setFont(label_font); - label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = 0; c.gridy = y; - c.insets = new Insets(10, 10, 10, 10); - c.anchor = GridBagConstraints.WEST; - layout.setConstraints(label, c); - add(label); - - value = new JTextField(30); - value.setFont(value_font); - value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 1; c.gridy = y; - c.anchor = GridBagConstraints.EAST; - c.fill = GridBagConstraints.HORIZONTAL; - layout.setConstraints(value, c); - add(value); - - value_deg = new JTextField(5); - value_deg.setFont(value_font); - value_deg.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 2; c.gridy = y; - c.anchor = GridBagConstraints.EAST; - c.fill = GridBagConstraints.HORIZONTAL; - - layout.setConstraints(value_deg, c); - add(value_deg); + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + label = new JLabel("Bearing"); + label.setFont(label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 0; c.gridy = y; + c.insets = new Insets(10, 10, 10, 10); + c.anchor = GridBagConstraints.WEST; + c.weightx = 0; + c.fill = GridBagConstraints.VERTICAL; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(30); + value.setFont(value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 1; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.weightx = 1; + c.fill = GridBagConstraints.BOTH; + layout.setConstraints(value, c); + add(value); + + value_deg = new JTextField(5); + value_deg.setFont(value_font); + value_deg.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.weightx = 1; + c.fill = GridBagConstraints.BOTH; + layout.setConstraints(value_deg, c); + add(value_deg); } } diff --git a/ao-tools/altosui/AltosFlightStatus.java b/ao-tools/altosui/AltosFlightStatus.java new file mode 100644 index 00000000..b99a5325 --- /dev/null +++ b/ao-tools/altosui/AltosFlightStatus.java @@ -0,0 +1,157 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosFlightStatus extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + + private Font status_font; + + public class FlightValue { + JLabel label; + JTextField value; + + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + } + public FlightValue (GridBagLayout layout, int x, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(5, 5, 5, 5); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + + label = new JLabel(text); + label.setFont(status_font); + label.setHorizontalAlignment(SwingConstants.CENTER); + c.gridx = x; c.gridy = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(""); + value.setFont(status_font); + value.setHorizontalAlignment(SwingConstants.CENTER); + c.gridx = x; c.gridy = 1; + layout.setConstraints(value, c); + add(value); + } + } + + class Call extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(state.data.callsign); + } + public Call (GridBagLayout layout, int x) { + super (layout, x, "Callsign"); + } + } + + Call call; + + class Serial extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(String.format("%d", state.data.serial)); + } + public Serial (GridBagLayout layout, int x) { + super (layout, x, "Serial"); + } + } + + Serial serial; + + class Flight extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(String.format("%d", state.data.flight)); + } + public Flight (GridBagLayout layout, int x) { + super (layout, x, "Flight"); + } + } + + Flight flight; + + class FlightState extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(state.data.state()); + } + public FlightState (GridBagLayout layout, int x) { + super (layout, x, "State"); + } + } + + FlightState flight_state; + + class RSSI extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(String.format("%d", state.data.rssi)); + } + public RSSI (GridBagLayout layout, int x) { + super (layout, x, "RSSI (dBm)"); + } + } + + RSSI rssi; + + public void reset () { + call.reset(); + serial.reset(); + flight.reset(); + flight_state.reset(); + rssi.reset(); + } + + public void show (AltosState state, int crc_errors) { + call.show(state, crc_errors); + serial.show(state, crc_errors); + flight.show(state, crc_errors); + flight_state.show(state, crc_errors); + rssi.show(state, crc_errors); + } + + public int height() { + Dimension d = layout.preferredLayoutSize(this); + return d.height; + } + + public AltosFlightStatus() { + layout = new GridBagLayout(); + + status_font = new Font("SansSerif", Font.BOLD, 24); + setLayout(layout); + + call = new Call(layout, 0); + serial = new Serial(layout, 1); + flight = new Flight(layout, 2); + flight_state = new FlightState(layout, 3); + rssi = new RSSI(layout, 4); + } +} diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 3581c54c..ae31048d 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -45,7 +45,8 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { AltosDescent descent; AltosLanded landed; - private AltosStatusTable flightStatus; + private AltosFlightStatus flightStatus; + private JScrollPane flightInfoPane; private AltosInfoTable flightInfo; static final int tab_pad = 1; @@ -117,7 +118,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { } cur_tab = tab; } - flightStatus.set(state); + flightStatus.show(state, crc_errors); flightInfo.show(state, crc_errors); } @@ -133,7 +134,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { setTitle(String.format("AltOS %s", reader.name)); - flightStatus = new AltosStatusTable(); + flightStatus = new AltosFlightStatus(); vbox = new Box (BoxLayout.Y_AXIS); vbox.add(flightStatus); @@ -153,7 +154,8 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { pane.add("Landed", landed); flightInfo = new AltosInfoTable(); - pane.add("Table", flightInfo.box()); + flightInfoPane = new JScrollPane(flightInfo.box()); + pane.add("Table", flightInfoPane); vbox.add(pane); diff --git a/ao-tools/altosui/AltosLanded.java b/ao-tools/altosui/AltosLanded.java index d170ccad..465c9dce 100644 --- a/ao-tools/altosui/AltosLanded.java +++ b/ao-tools/altosui/AltosLanded.java @@ -48,6 +48,7 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay { public LandedValue (GridBagLayout layout, int y, String text) { GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; label = new JLabel(text); label.setFont(label_font); @@ -55,6 +56,8 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay { c.gridx = 0; c.gridy = y; c.insets = new Insets(10, 10, 10, 10); c.anchor = GridBagConstraints.WEST; + c.weightx = 0; + c.fill = GridBagConstraints.VERTICAL; layout.setConstraints(label, c); add(label); @@ -63,7 +66,8 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay { value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 1; c.gridy = y; c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.fill = GridBagConstraints.BOTH; layout.setConstraints(value, c); add(value); } diff --git a/ao-tools/altosui/AltosPad.java b/ao-tools/altosui/AltosPad.java index da047072..650ed012 100644 --- a/ao-tools/altosui/AltosPad.java +++ b/ao-tools/altosui/AltosPad.java @@ -46,11 +46,13 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { public LaunchStatus (GridBagLayout layout, int y, String text) { GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; lights = new AltosLights(); c.gridx = 0; c.gridy = y; c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; layout.setConstraints(lights, c); add(lights); @@ -60,7 +62,8 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { c.gridx = 1; c.gridy = y; c.insets = new Insets(10, 10, 10, 10); c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; layout.setConstraints(label, c); add(label); @@ -69,6 +72,8 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; layout.setConstraints(value, c); add(value); @@ -85,13 +90,16 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { } public LaunchValue (GridBagLayout layout, int y, String text) { GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(10, 10, 10, 10); + c.weighty = 1; label = new JLabel(text); label.setFont(label_font); label.setHorizontalAlignment(SwingConstants.LEFT); c.gridx = 1; c.gridy = y; - c.insets = new Insets(10, 10, 10, 10); c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; layout.setConstraints(label, c); add(label); @@ -100,7 +108,8 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; c.anchor = GridBagConstraints.EAST; - c.fill = GridBagConstraints.HORIZONTAL; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; layout.setConstraints(value, c); add(value); } @@ -221,13 +230,10 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { public AltosPad() { layout = new GridBagLayout(); - GridBagConstraints c; - label_font = new Font("Dialog", Font.PLAIN, 24); value_font = new Font("Monospaced", Font.PLAIN, 24); setLayout(layout); - c = new GridBagConstraints(); /* Elements in pad display: * * Battery voltage diff --git a/ao-tools/altosui/AltosStatusTable.java b/ao-tools/altosui/AltosStatusTable.java deleted file mode 100644 index 02c6232f..00000000 --- a/ao-tools/altosui/AltosStatusTable.java +++ /dev/null @@ -1,67 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosStatusTable extends JTable { - private AltosFlightStatusTableModel flightStatusModel; - - private Font statusFont = new Font("SansSerif", Font.BOLD, 24); - - public AltosStatusTable() { - super((TableModel) new AltosFlightStatusTableModel()); - flightStatusModel = (AltosFlightStatusTableModel) getModel(); - - setFont(statusFont); - - TableColumnModel tcm = getColumnModel(); - - for (int i = 0; i < flightStatusModel.getColumnCount(); i++) { - DefaultTableCellRenderer r = new DefaultTableCellRenderer(); - r.setFont(statusFont); - r.setHorizontalAlignment(SwingConstants.CENTER); - tcm.getColumn(i).setCellRenderer(r); - } - - setRowHeight(rowHeight()); - setShowGrid(false); - } - - public int rowHeight() { - FontMetrics statusMetrics = getFontMetrics(statusFont); - return (statusMetrics.getHeight() + statusMetrics.getLeading()) * 15 / 10; - } - - public int height() { - return rowHeight * 4; - } - - public void set(AltosState state) { - flightStatusModel.set(state); - } -} diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index f4c743df..d11ea3e2 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -33,7 +33,7 @@ altosui_JAVA = \ AltosFlightDisplay.java \ AltosFlightInfoTableModel.java \ AltosFlightReader.java \ - AltosFlightStatusTableModel.java \ + AltosFlightStatus.java \ AltosFlightUI.java \ AltosGPS.java \ AltosGreatCircle.java \ @@ -61,7 +61,6 @@ altosui_JAVA = \ AltosSerialInUseException.java \ AltosSerialMonitor.java \ AltosState.java \ - AltosStatusTable.java \ AltosTelemetry.java \ AltosTelemetryIterable.java \ AltosUI.java \ -- cgit v1.2.3 From 511903704f7e1b22e88dd3e3cc35fd3c0583820e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 14 Nov 2010 03:26:57 -0800 Subject: altosui: With --replay option, exit when replay window is closed Otherwise, the application hangs around forever. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosFlightUI.java | 10 ++++++++- ao-tools/altosui/AltosUI.java | 41 +++++++++++++++++++------------------ 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index ae31048d..5134a24e 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -56,6 +56,8 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { int cur_tab = 0; + boolean exit_on_close = false; + int which_tab(AltosState state) { if (state.state < Altos.ao_flight_boost) return tab_pad; @@ -122,8 +124,12 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { flightInfo.show(state, crc_errors); } + public void set_exit_on_close() { + exit_on_close = true; + } + public AltosFlightUI(AltosVoice in_voice, AltosFlightReader in_reader, final int serial) { - AltosPreferences.init(this); + AltosPreferences.init(this); voice = in_voice; reader = in_reader; @@ -191,6 +197,8 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { disconnect(); setVisible(false); dispose(); + if (exit_on_close) + System.exit(0); } }); diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index 0d8f0e8d..a2e416ba 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -443,26 +443,27 @@ public class AltosUI extends JFrame { public static void main(final String[] args) { int process = 0; /* Handle batch-mode */ - if (args.length == 2 && args[0].equals("--replay")) { - String filename = args[1]; - FileInputStream in; - try { - in = new FileInputStream(filename); - } catch (Exception e) { - System.out.printf("Failed to open file '%s'\n", filename); - return; - } - AltosRecordIterable recs; - AltosReplayReader reader; - if (filename.endsWith("eeprom")) { - recs = new AltosEepromIterable(in); - } else { - recs = new AltosTelemetryIterable(in); - } - reader = new AltosReplayReader(recs.iterator(), filename); - new AltosFlightUI(new AltosVoice(), reader); - return; - } else if (args.length > 0) { + if (args.length == 2 && args[0].equals("--replay")) { + String filename = args[1]; + FileInputStream in; + try { + in = new FileInputStream(filename); + } catch (Exception e) { + System.out.printf("Failed to open file '%s'\n", filename); + return; + } + AltosRecordIterable recs; + AltosReplayReader reader; + if (filename.endsWith("eeprom")) { + recs = new AltosEepromIterable(in); + } else { + recs = new AltosTelemetryIterable(in); + } + reader = new AltosReplayReader(recs.iterator(), filename); + AltosFlightUI flight_ui = new AltosFlightUI(new AltosVoice(), reader); + flight_ui.set_exit_on_close(); + return; + } else if (args.length > 0) { for (int i = 0; i < args.length; i++) { if (args[i].equals("--kml")) process |= process_kml; -- cgit v1.2.3 From 524644d8d8ce3f8a5a914ecfc7e2a8d474d89095 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 15 Nov 2010 22:04:44 +0800 Subject: altosui: oops, missed a file in the previous commit AltosSerialInUseException.java just defines a new exception, thanks to java for making this live in a separate file. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosSerialInUseException.java | 28 +++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 ao-tools/altosui/AltosSerialInUseException.java diff --git a/ao-tools/altosui/AltosSerialInUseException.java b/ao-tools/altosui/AltosSerialInUseException.java new file mode 100644 index 00000000..4b108c7c --- /dev/null +++ b/ao-tools/altosui/AltosSerialInUseException.java @@ -0,0 +1,28 @@ +/* + * 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 libaltosJNI.*; + +public class AltosSerialInUseException extends Exception { + public altos_device device; + + public AltosSerialInUseException (altos_device in_device) { + device = in_device; + } +} -- cgit v1.2.3 From 257e97137325f5dbbd6aa034f20fd6937b67df90 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 15 Nov 2010 22:38:35 +0800 Subject: altosui: eliminate menu bar, moving elements to buttons. This adds a new 'configure AltosUI' dialog to set the log directory and voice preferences. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosConfigureUI.java | 120 +++++++++++++++++++++++++ ao-tools/altosui/AltosUI.java | 158 +++++---------------------------- ao-tools/altosui/Makefile.am | 1 + 3 files changed, 144 insertions(+), 135 deletions(-) create mode 100644 ao-tools/altosui/AltosConfigureUI.java diff --git a/ao-tools/altosui/AltosConfigureUI.java b/ao-tools/altosui/AltosConfigureUI.java new file mode 100644 index 00000000..88c180f1 --- /dev/null +++ b/ao-tools/altosui/AltosConfigureUI.java @@ -0,0 +1,120 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosConfigureUI extends JDialog { + JFrame owner; + AltosVoice voice; + Container pane; + + JRadioButton enable_voice; + JButton test_voice; + JButton close; + + JButton configure_log; + JTextField log_directory; + + public AltosConfigureUI(JFrame in_owner, AltosVoice in_voice) { + super(in_owner, "Configure AltosUI", false); + + GridBagConstraints c; + + Insets insets = new Insets(4, 4, 4, 4); + + owner = in_owner; + voice = in_voice; + pane = getContentPane(); + pane.setLayout(new GridBagLayout()); + + c = new GridBagConstraints(); + c.insets = insets; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + + /* Enable Voice */ + c.gridx = 0; + c.gridy = 0; + enable_voice = new JRadioButton("Enable Voice", AltosPreferences.voice()); + enable_voice.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JRadioButton item = (JRadioButton) e.getSource(); + boolean enabled = item.isSelected(); + AltosPreferences.set_voice(enabled); + if (enabled) + voice.speak_always("Enable voice."); + else + voice.speak_always("Disable voice."); + } + }); + pane.add(enable_voice, c); + c.gridx = 1; + c.gridy = 0; + test_voice = new JButton("Test Voice"); + test_voice.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + voice.speak("That's one small step for man; one giant leap for mankind."); + } + }); + pane.add(test_voice, c); + + close = new JButton("Close"); + close.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setVisible(false); + } + }); + c.gridx = 0; + c.gridy = 3; + c.gridwidth = 2; + pane.add(close, c); + + configure_log = new JButton("Configure Log"); + configure_log.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + AltosPreferences.ConfigureLog(); + log_directory.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.fill = GridBagConstraints.BOTH; + pane.add(log_directory, c); + + pack(); + setLocationRelativeTo(owner); + setVisible(true); + } +} diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index a2e416ba..bedf2459 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -144,9 +144,28 @@ public class AltosUI extends JFrame { } }); - setTitle("AltOS"); + b = addButton(0, 2, "Configure AltosUI"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ConfigureAltosUI(); + } + }); + + b = addButton(1, 2, "Flash Image"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + FlashImage(); + } + }); + + b = addButton(2, 2, "Quit"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.exit(0); + } + }); - createMenu(); + setTitle("AltOS"); pane.doLayout(); pane.validate(); @@ -232,139 +251,8 @@ public class AltosUI extends JFrame { new AltosGraphUI(AltosUI.this); } - /* Create the AltosUI menus - */ - private void createMenu() { - JMenuBar menubar = new JMenuBar(); - JMenu menu; - JMenuItem item; - JRadioButtonMenuItem radioitem; - - // File menu - { - menu = new JMenu("File"); - menu.setMnemonic(KeyEvent.VK_F); - menubar.add(menu); - - item = new JMenuItem("Flash Image",KeyEvent.VK_I); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - FlashImage(); - } - }); - menu.add(item); - - item = new JMenuItem("Export Data",KeyEvent.VK_E); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ExportData(); - } - }); - menu.add(item); - - item = new JMenuItem("Graph Data",KeyEvent.VK_G); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - GraphData(); - } - }); - menu.add(item); - - item = new JMenuItem("Quit",KeyEvent.VK_Q); - item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, - ActionEvent.CTRL_MASK)); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - System.out.printf("exiting\n"); - System.exit(0); - } - }); - menu.add(item); - } - - // Device menu - if (false) { - menu = new JMenu("Device"); - menu.setMnemonic(KeyEvent.VK_D); - menubar.add(menu); - - item = new JMenuItem("Connect to Device",KeyEvent.VK_C); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ConnectToDevice(); - } - }); - menu.add(item); - - menu.addSeparator(); - - item = new JMenuItem("Set Callsign",KeyEvent.VK_S); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ConfigureCallsign(); - } - }); - - menu.add(item); - - item = new JMenuItem("Configure TeleMetrum device",KeyEvent.VK_T); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ConfigureTeleMetrum(); - } - }); - - menu.add(item); - } - // Log menu - { - menu = new JMenu("Log"); - menu.setMnemonic(KeyEvent.VK_L); - menubar.add(menu); - - item = new JMenuItem("New Log",KeyEvent.VK_N); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - } - }); - menu.add(item); - - item = new JMenuItem("Configure Log",KeyEvent.VK_C); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - AltosPreferences.ConfigureLog(); - } - }); - menu.add(item); - } - // Voice menu - { - menu = new JMenu("Voice", true); - menu.setMnemonic(KeyEvent.VK_V); - menubar.add(menu); - - radioitem = new JRadioButtonMenuItem("Enable Voice", AltosPreferences.voice()); - radioitem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - JRadioButtonMenuItem item = (JRadioButtonMenuItem) e.getSource(); - boolean enabled = item.isSelected(); - AltosPreferences.set_voice(enabled); - if (enabled) - voice.speak_always("Enable voice."); - else - voice.speak_always("Disable voice."); - } - }); - menu.add(radioitem); - item = new JMenuItem("Test Voice",KeyEvent.VK_T); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - voice.speak("That's one small step for man; one giant leap for mankind."); - } - }); - menu.add(item); - } - this.setJMenuBar(menubar); + private void ConfigureAltosUI() { + new AltosConfigureUI(AltosUI.this, voice); } static AltosRecordIterable open_logfile(String filename) { diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index d11ea3e2..8d0fe16e 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -14,6 +14,7 @@ altosui_JAVA = \ AltosChannelMenu.java \ AltosConfig.java \ AltosConfigUI.java \ + AltosConfigureUI.java \ AltosConvert.java \ AltosCRCException.java \ AltosCSV.java \ -- cgit v1.2.3 From 39e371561469d8e5059638ffa4e7075f391de268 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 15 Nov 2010 23:14:51 +0800 Subject: altosui: add reboot button to telemetrum configuration UI This lets you reconfigure and reboot telemetrum, including over the radio link. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosConfig.java | 20 +++++++-- ao-tools/altosui/AltosConfigUI.java | 90 +++++++++++++++++++++---------------- 2 files changed, 68 insertions(+), 42 deletions(-) diff --git a/ao-tools/altosui/AltosConfig.java b/ao-tools/altosui/AltosConfig.java index 09e204a9..a0fdb623 100644 --- a/ao-tools/altosui/AltosConfig.java +++ b/ao-tools/altosui/AltosConfig.java @@ -198,12 +198,26 @@ public class AltosConfig implements Runnable, ActionListener { public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); - if (cmd.equals("save")) { + if (cmd.equals("Save")) { save_data(); set_ui(); - } else if (cmd.equals("reset")) { + } else if (cmd.equals("Reset")) { set_ui(); - } else if (cmd.equals("close")) { + } else if (cmd.equals("Reboot")) { + if (serial_line != null) { + try { + start_serial(); + serial_line.printf("r eboot\n"); + } catch (InterruptedException ie) { + } finally { + try { + stop_serial(); + } catch (InterruptedException ie) { + } + } + serial_line.close(); + } + } else if (cmd.equals("Close")) { if (serial_line != null) serial_line.close(); } diff --git a/ao-tools/altosui/AltosConfigUI.java b/ao-tools/altosui/AltosConfigUI.java index 9e3856b0..e04933eb 100644 --- a/ao-tools/altosui/AltosConfigUI.java +++ b/ao-tools/altosui/AltosConfigUI.java @@ -61,6 +61,7 @@ public class AltosConfigUI JButton save; JButton reset; + JButton reboot; JButton close; ActionListener listener; @@ -92,7 +93,7 @@ public class AltosConfigUI public void windowClosing(WindowEvent e) { ui.actionPerformed(new ActionEvent(e.getSource(), ActionEvent.ACTION_PERFORMED, - "close")); + "Close")); } } @@ -112,7 +113,7 @@ public class AltosConfigUI /* Product */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 0; - c.gridwidth = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -120,8 +121,8 @@ public class AltosConfigUI pane.add(product_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 0; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 0; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; @@ -132,7 +133,7 @@ public class AltosConfigUI /* Version */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 1; - c.gridwidth = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -141,8 +142,8 @@ public class AltosConfigUI pane.add(version_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 1; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 1; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; @@ -154,7 +155,7 @@ public class AltosConfigUI /* Serial */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 2; - c.gridwidth = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -163,8 +164,8 @@ public class AltosConfigUI pane.add(serial_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 2; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 2; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; @@ -176,7 +177,7 @@ public class AltosConfigUI /* Main deploy */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 3; - c.gridwidth = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -185,8 +186,8 @@ public class AltosConfigUI pane.add(main_deploy_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 3; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; @@ -200,7 +201,7 @@ public class AltosConfigUI /* Apogee delay */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 4; - c.gridwidth = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -209,8 +210,8 @@ public class AltosConfigUI pane.add(apogee_delay_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 4; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 4; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; @@ -224,7 +225,7 @@ public class AltosConfigUI /* Radio channel */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 5; - c.gridwidth = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -233,8 +234,8 @@ public class AltosConfigUI pane.add(radio_channel_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 5; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 5; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; @@ -248,7 +249,7 @@ public class AltosConfigUI /* Radio Calibration */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 6; - c.gridwidth = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -257,8 +258,8 @@ public class AltosConfigUI pane.add(radio_calibration_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 6; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 6; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; @@ -271,7 +272,7 @@ public class AltosConfigUI /* Callsign */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 7; - c.gridwidth = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -280,8 +281,8 @@ public class AltosConfigUI pane.add(callsign_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 7; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 7; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; @@ -294,36 +295,47 @@ public class AltosConfigUI /* Buttons */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 8; - c.gridwidth = 6; + c.gridwidth = 2; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; save = new JButton("Save"); pane.add(save, c); save.addActionListener(this); - save.setActionCommand("save"); + save.setActionCommand("Save"); c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 8; - c.gridwidth = 6; + c.gridx = 2; c.gridy = 8; + c.gridwidth = 2; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.CENTER; c.insets = il; reset = new JButton("Reset"); pane.add(reset, c); reset.addActionListener(this); - reset.setActionCommand("reset"); + reset.setActionCommand("Reset"); c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 8; - c.gridwidth = 6; + c.gridx = 4; c.gridy = 8; + c.gridwidth = 2; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.insets = il; + reboot = new JButton("Reboot"); + pane.add(reboot, c); + reboot.addActionListener(this); + reboot.setActionCommand("Reboot"); + + c = new GridBagConstraints(); + c.gridx = 6; c.gridy = 8; + c.gridwidth = 2; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_END; c.insets = il; close = new JButton("Close"); pane.add(close, c); close.addActionListener(this); - close.setActionCommand("close"); + close.setActionCommand("Close"); addWindowListener(new ConfigListener(this)); } @@ -336,12 +348,12 @@ public class AltosConfigUI } /* If any values have been changed, confirm before closing */ - public boolean check_dirty() { + public boolean check_dirty(String operation) { if (dirty) { - Object[] options = { "Close anyway", "Keep editing" }; + Object[] options = { String.format("%s anyway", operation), "Keep editing" }; int i; i = JOptionPane.showOptionDialog(this, - "Configuration modified, close anyway?", + String.format("Configuration modified. %s anyway?", operation), "Configuration Modified", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, @@ -356,11 +368,11 @@ public class AltosConfigUI public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); - if (cmd.equals("close")) - if (!check_dirty()) + if (cmd.equals("Close") || cmd.equals("Reboot")) + if (!check_dirty(cmd)) return; listener.actionPerformed(e); - if (cmd.equals("close")) { + if (cmd.equals("Close") || cmd.equals("Reboot")) { setVisible(false); dispose(); } -- cgit v1.2.3 From fcca333cda64be35f0c9fb0109eef1be3709dddd Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 16 Nov 2010 21:49:59 +0800 Subject: altosui: Add callsign configuration in AltosUI configuration dialog This callsign is used during packet communication. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosConfigUI.java | 2 +- ao-tools/altosui/AltosConfigureUI.java | 56 ++++++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/ao-tools/altosui/AltosConfigUI.java b/ao-tools/altosui/AltosConfigUI.java index e04933eb..ca89f58d 100644 --- a/ao-tools/altosui/AltosConfigUI.java +++ b/ao-tools/altosui/AltosConfigUI.java @@ -288,7 +288,7 @@ public class AltosConfigUI c.anchor = GridBagConstraints.LINE_START; c.insets = ir; c.ipady = 5; - callsign_value = new JTextField("N0CALL"); + callsign_value = new JTextField(AltosPreferences.callsign()); callsign_value.getDocument().addDocumentListener(this); pane.add(callsign_value, c); diff --git a/ao-tools/altosui/AltosConfigureUI.java b/ao-tools/altosui/AltosConfigureUI.java index 88c180f1..64c17eaf 100644 --- a/ao-tools/altosui/AltosConfigureUI.java +++ b/ao-tools/altosui/AltosConfigureUI.java @@ -22,13 +22,17 @@ 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.*; import java.util.concurrent.LinkedBlockingQueue; -public class AltosConfigureUI extends JDialog { +public class AltosConfigureUI + extends JDialog + implements DocumentListener +{ JFrame owner; AltosVoice voice; Container pane; @@ -40,6 +44,22 @@ public class AltosConfigureUI extends JDialog { JButton configure_log; JTextField log_directory; + JLabel callsign_label; + JTextField callsign_value; + + /* DocumentListener interface methods */ + public void changedUpdate(DocumentEvent e) { + AltosPreferences.set_callsign(callsign_value.getText()); + } + + public void insertUpdate(DocumentEvent e) { + changedUpdate(e); + } + + public void removeUpdate(DocumentEvent e) { + changedUpdate(e); + } + public AltosConfigureUI(JFrame in_owner, AltosVoice in_voice) { super(in_owner, "Configure AltosUI", false); @@ -83,17 +103,6 @@ public class AltosConfigureUI extends JDialog { }); pane.add(test_voice, c); - close = new JButton("Close"); - close.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - setVisible(false); - } - }); - c.gridx = 0; - c.gridy = 3; - c.gridwidth = 2; - pane.add(close, c); - configure_log = new JButton("Configure Log"); configure_log.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { @@ -113,6 +122,29 @@ public class AltosConfigureUI extends JDialog { c.fill = GridBagConstraints.BOTH; pane.add(log_directory, c); + callsign_label = new JLabel("Callsign"); + c.gridx = 0; + c.gridy = 3; + pane.add(callsign_label, c); + + callsign_value = new JTextField(AltosPreferences.callsign()); + callsign_value.getDocument().addDocumentListener(this); + c.gridx = 1; + c.gridy = 3; + pane.add(callsign_value, c); + + close = new JButton("Close"); + close.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setVisible(false); + } + }); + c.gridx = 0; + c.gridy = 4; + c.gridwidth = 2; + c.fill = GridBagConstraints.NONE; + pane.add(close, c); + pack(); setLocationRelativeTo(owner); setVisible(true); -- cgit v1.2.3 From d0eb41619544ead6d9dab3a8d024a12936c9cdd0 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 16 Nov 2010 22:20:00 +0800 Subject: altosui: Cleanup flight UI layout Use common constants for fonts and insets Shrink fonts so that the window is < 600 pixels tall. Signed-off-by: Keith Packard --- ao-tools/altosui/Altos.java | 6 +++ ao-tools/altosui/AltosAscent.java | 24 +++++------- ao-tools/altosui/AltosDescent.java | 18 ++++----- ao-tools/altosui/AltosFlightInfoTableModel.java | 2 +- ao-tools/altosui/AltosFlightStatus.java | 7 +--- ao-tools/altosui/AltosFlightUI.java | 2 +- ao-tools/altosui/AltosLanded.java | 4 +- ao-tools/altosui/AltosPad.java | 50 +++++++++++++++++-------- 8 files changed, 64 insertions(+), 49 deletions(-) diff --git a/ao-tools/altosui/Altos.java b/ao-tools/altosui/Altos.java index 997550e0..197e98db 100644 --- a/ao-tools/altosui/Altos.java +++ b/ao-tools/altosui/Altos.java @@ -67,6 +67,12 @@ public class Altos { static boolean map_initialized = false; + static final int tab_elt_pad = 5; + + static final Font label_font = new Font("Dialog", Font.PLAIN, 22); + static final Font value_font = new Font("Monospaced", Font.PLAIN, 22); + static final Font status_font = new Font("SansSerif", Font.BOLD, 24); + static void initialize_map() { string_to_state.put("startup", ao_flight_startup); diff --git a/ao-tools/altosui/AltosAscent.java b/ao-tools/altosui/AltosAscent.java index 7525b655..8e1b6347 100644 --- a/ao-tools/altosui/AltosAscent.java +++ b/ao-tools/altosui/AltosAscent.java @@ -30,8 +30,6 @@ import java.util.concurrent.LinkedBlockingQueue; public class AltosAscent extends JComponent implements AltosFlightDisplay { GridBagLayout layout; - Font label_font; - Font value_font; public class AscentValue { JLabel label; @@ -46,10 +44,10 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { c.weighty = 1; label = new JLabel(text); - label.setFont(label_font); + label.setFont(Altos.label_font); label.setHorizontalAlignment(SwingConstants.LEFT); c.gridx = 0; c.gridy = y; - c.insets = new Insets(10, 10, 10, 10); + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.VERTICAL; c.weightx = 0; @@ -57,7 +55,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { add(label); value = new JTextField(30); - value.setFont(value_font); + value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 1; c.gridy = y; c.anchor = GridBagConstraints.WEST; @@ -95,10 +93,10 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { c.weighty = 1; label = new JLabel(text); - label.setFont(label_font); + label.setFont(Altos.label_font); label.setHorizontalAlignment(SwingConstants.LEFT); c.gridx = 0; c.gridy = y; - c.insets = new Insets(10, 10, 10, 10); + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.VERTICAL; c.weightx = 0; @@ -106,7 +104,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { add(label); value = new JTextField(15); - value.setFont(value_font); + value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 1; c.gridy = y; c.anchor = GridBagConstraints.EAST; @@ -116,7 +114,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { add(value); max_value = new JTextField(15); - max_value.setFont(value_font); + max_value.setFont(Altos.value_font); max_value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; c.anchor = GridBagConstraints.EAST; @@ -224,15 +222,15 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { JLabel cur, max; cur = new JLabel("Current"); - cur.setFont(label_font); + cur.setFont(Altos.label_font); c = new GridBagConstraints(); c.gridx = 1; c.gridy = y; - c.insets = new Insets(10, 10, 10, 10); + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); layout.setConstraints(cur, c); add(cur); max = new JLabel("Maximum"); - max.setFont(label_font); + max.setFont(Altos.label_font); c.gridx = 2; c.gridy = y; layout.setConstraints(max, c); add(max); @@ -241,8 +239,6 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { public AltosAscent() { layout = new GridBagLayout(); - label_font = new Font("Dialog", Font.PLAIN, 24); - value_font = new Font("Monospaced", Font.PLAIN, 24); setLayout(layout); /* Elements in ascent display: diff --git a/ao-tools/altosui/AltosDescent.java b/ao-tools/altosui/AltosDescent.java index b69e36b6..ceb78e57 100644 --- a/ao-tools/altosui/AltosDescent.java +++ b/ao-tools/altosui/AltosDescent.java @@ -30,8 +30,6 @@ import java.util.concurrent.LinkedBlockingQueue; public class AltosDescent extends JComponent implements AltosFlightDisplay { GridBagLayout layout; - Font label_font; - Font value_font; public class DescentValue { JLabel label; @@ -51,10 +49,10 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { c.weighty = 1; label = new JLabel(text); - label.setFont(label_font); + label.setFont(Altos.label_font); label.setHorizontalAlignment(SwingConstants.LEFT); c.gridx = 0; c.gridy = y; - c.insets = new Insets(10, 10, 10, 10); + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.VERTICAL; c.weightx = 0; @@ -62,7 +60,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { add(label); value = new JTextField(30); - value.setFont(value_font); + value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 1; c.gridy = y; c.gridwidth = 2; @@ -161,10 +159,10 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { c.weighty = 1; label = new JLabel("Bearing"); - label.setFont(label_font); + label.setFont(Altos.label_font); label.setHorizontalAlignment(SwingConstants.LEFT); c.gridx = 0; c.gridy = y; - c.insets = new Insets(10, 10, 10, 10); + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); c.anchor = GridBagConstraints.WEST; c.weightx = 0; c.fill = GridBagConstraints.VERTICAL; @@ -172,7 +170,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { add(label); value = new JTextField(30); - value.setFont(value_font); + value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 1; c.gridy = y; c.anchor = GridBagConstraints.EAST; @@ -182,7 +180,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { add(value); value_deg = new JTextField(5); - value_deg.setFont(value_font); + value_deg.setFont(Altos.value_font); value_deg.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; c.anchor = GridBagConstraints.EAST; @@ -243,8 +241,6 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { public AltosDescent() { layout = new GridBagLayout(); - label_font = new Font("Dialog", Font.PLAIN, 24); - value_font = new Font("Monospaced", Font.PLAIN, 24); setLayout(layout); /* Elements in descent display */ diff --git a/ao-tools/altosui/AltosFlightInfoTableModel.java b/ao-tools/altosui/AltosFlightInfoTableModel.java index 2a22e3e5..3355ff52 100644 --- a/ao-tools/altosui/AltosFlightInfoTableModel.java +++ b/ao-tools/altosui/AltosFlightInfoTableModel.java @@ -46,7 +46,7 @@ public class AltosFlightInfoTableModel extends AbstractTableModel { public int getColumnCount() { return columnNames.length; } public String getColumnName(int col) { return columnNames[col]; } - public int getRowCount() { return 20; } + public int getRowCount() { return 17; } int current_row = 0; int prev_num_rows = 0; diff --git a/ao-tools/altosui/AltosFlightStatus.java b/ao-tools/altosui/AltosFlightStatus.java index b99a5325..59c9e9db 100644 --- a/ao-tools/altosui/AltosFlightStatus.java +++ b/ao-tools/altosui/AltosFlightStatus.java @@ -31,8 +31,6 @@ import java.util.concurrent.LinkedBlockingQueue; public class AltosFlightStatus extends JComponent implements AltosFlightDisplay { GridBagLayout layout; - private Font status_font; - public class FlightValue { JLabel label; JTextField value; @@ -51,14 +49,14 @@ public class AltosFlightStatus extends JComponent implements AltosFlightDisplay c.weighty = 1; label = new JLabel(text); - label.setFont(status_font); + label.setFont(Altos.status_font); label.setHorizontalAlignment(SwingConstants.CENTER); c.gridx = x; c.gridy = 0; layout.setConstraints(label, c); add(label); value = new JTextField(""); - value.setFont(status_font); + value.setFont(Altos.status_font); value.setHorizontalAlignment(SwingConstants.CENTER); c.gridx = x; c.gridy = 1; layout.setConstraints(value, c); @@ -145,7 +143,6 @@ public class AltosFlightStatus extends JComponent implements AltosFlightDisplay public AltosFlightStatus() { layout = new GridBagLayout(); - status_font = new Font("SansSerif", Font.BOLD, 24); setLayout(layout); call = new Call(layout, 0); diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 5134a24e..78b005c0 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -187,7 +187,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { this.setJMenuBar(menubar); } - this.setSize(new Dimension (width(), height())); + this.setSize(this.getPreferredSize()); this.validate(); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); diff --git a/ao-tools/altosui/AltosLanded.java b/ao-tools/altosui/AltosLanded.java index 465c9dce..0656ea6c 100644 --- a/ao-tools/altosui/AltosLanded.java +++ b/ao-tools/altosui/AltosLanded.java @@ -196,8 +196,8 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay { public AltosLanded() { layout = new GridBagLayout(); - label_font = new Font("Dialog", Font.PLAIN, 24); - value_font = new Font("Monospaced", Font.PLAIN, 24); + label_font = new Font("Dialog", Font.PLAIN, 22); + value_font = new Font("Monospaced", Font.PLAIN, 22); setLayout(layout); /* Elements in descent display */ diff --git a/ao-tools/altosui/AltosPad.java b/ao-tools/altosui/AltosPad.java index 650ed012..8b258c7d 100644 --- a/ao-tools/altosui/AltosPad.java +++ b/ao-tools/altosui/AltosPad.java @@ -60,7 +60,7 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { label.setFont(label_font); label.setHorizontalAlignment(SwingConstants.LEFT); c.gridx = 1; c.gridy = y; - c.insets = new Insets(10, 10, 10, 10); + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.VERTICAL; c.weightx = 0; @@ -90,7 +90,7 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { } public LaunchValue (GridBagLayout layout, int y, String text) { GridBagConstraints c = new GridBagConstraints(); - c.insets = new Insets(10, 10, 10, 10); + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); c.weighty = 1; label = new JLabel(text); @@ -151,17 +151,32 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { Main main; - class GPS extends LaunchStatus { + class GPSLocked extends LaunchStatus { void show (AltosState state, int crc_errors) { value.setText(String.format("%4d sats", state.gps.nsat)); + lights.set(state.gps.locked); + } + public GPSLocked (GridBagLayout layout, int y) { + super (layout, y, "GPS Locked"); + } + } + + GPSLocked gps_locked; + + class GPSReady extends LaunchStatus { + void show (AltosState state, int crc_errors) { + if (state.gps_ready) + value.setText("Ready"); + else + value.setText(String.format("Waiting %d", state.gps_waiting)); lights.set(state.gps_ready); } - public GPS (GridBagLayout layout, int y) { - super (layout, y, "GPS Status"); + public GPSReady (GridBagLayout layout, int y) { + super (layout, y, "GPS Ready"); } } - GPS gps; + GPSReady gps_ready; String pos(double p, String pos, String neg) { String h = pos; @@ -211,7 +226,8 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { battery.reset(); apogee.reset(); main.reset(); - gps.reset(); + gps_locked.reset(); + gps_ready.reset(); pad_lat.reset(); pad_lon.reset(); pad_alt.reset(); @@ -221,7 +237,8 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { battery.show(state, crc_errors); apogee.show(state, crc_errors); main.show(state, crc_errors); - gps.show(state, crc_errors); + gps_locked.show(state, crc_errors); + gps_ready.show(state, crc_errors); pad_lat.show(state, crc_errors); pad_lon.show(state, crc_errors); pad_alt.show(state, crc_errors); @@ -230,24 +247,27 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { public AltosPad() { layout = new GridBagLayout(); - label_font = new Font("Dialog", Font.PLAIN, 24); - value_font = new Font("Monospaced", Font.PLAIN, 24); + label_font = new Font("Dialog", Font.PLAIN, 22); + value_font = new Font("Monospaced", Font.PLAIN, 22); setLayout(layout); /* Elements in pad display: * * Battery voltage * Igniter continuity - * GPS lock status and location + * GPS lock status + * GPS ready status + * GPS location * Pad altitude * RSSI */ battery = new Battery(layout, 0); apogee = new Apogee(layout, 1); main = new Main(layout, 2); - gps = new GPS(layout, 3); - pad_lat = new PadLat(layout, 4); - pad_lon = new PadLon(layout, 5); - pad_alt = new PadAlt(layout, 6); + gps_locked = new GPSLocked(layout, 3); + gps_ready = new GPSReady(layout, 4); + pad_lat = new PadLat(layout, 5); + pad_lon = new PadLon(layout, 6); + pad_alt = new PadAlt(layout, 7); } } -- cgit v1.2.3 From 1a4b6e96f823035b113f01d1bdfd61afc1f33e25 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 16 Nov 2010 22:46:29 +0800 Subject: altosui: Add igniter status to ascent and descent tabs Monitor igniters during all phases of the flight. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosAscent.java | 77 ++++++++++++++++++++++ ao-tools/altosui/AltosDescent.java | 130 +++++++++++++++++++++++++++++-------- ao-tools/altosui/AltosPad.java | 12 ++-- 3 files changed, 183 insertions(+), 36 deletions(-) diff --git a/ao-tools/altosui/AltosAscent.java b/ao-tools/altosui/AltosAscent.java index 8e1b6347..38ced95e 100644 --- a/ao-tools/altosui/AltosAscent.java +++ b/ao-tools/altosui/AltosAscent.java @@ -31,6 +31,53 @@ import java.util.concurrent.LinkedBlockingQueue; public class AltosAscent extends JComponent implements AltosFlightDisplay { GridBagLayout layout; + public class AscentStatus { + JLabel label; + JTextField value; + AltosLights lights; + + void show(AltosState state, int crc_errors) {} + void reset() { + value.setText(""); + lights.set(false); + } + + public AscentStatus (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + lights = new AltosLights(); + c.gridx = 0; c.gridy = y; + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(lights, c); + add(lights); + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(15); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + + } + } + public class AscentValue { JLabel label; JTextField value; @@ -173,6 +220,30 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { return String.format("%s %4d° %9.6f", h, deg, min); } + class Apogee extends AscentStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.drogue_sense)); + lights.set(state.drogue_sense > 3.2); + } + public Apogee (GridBagLayout layout, int y) { + super(layout, y, "Apogee Igniter Voltage"); + } + } + + Apogee apogee; + + class Main extends AscentStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.main_sense)); + lights.set(state.main_sense > 3.2); + } + public Main (GridBagLayout layout, int y) { + super(layout, y, "Main Igniter Voltage"); + } + } + + Main main; + class Lat extends AscentValue { void show (AltosState state, int crc_errors) { if (state.gps != null) @@ -204,6 +275,8 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { public void reset() { lat.reset(); lon.reset(); + main.reset(); + apogee.reset(); height.reset(); speed.reset(); accel.reset(); @@ -213,6 +286,8 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { lat.show(state, crc_errors); lon.show(state, crc_errors); height.show(state, crc_errors); + main.show(state, crc_errors); + apogee.show(state, crc_errors); speed.show(state, crc_errors); accel.show(state, crc_errors); } @@ -253,5 +328,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { accel = new Accel(layout, 3); lat = new Lat(layout, 4); lon = new Lon(layout, 5); + apogee = new Apogee(layout, 6); + main = new Main(layout, 7); } } diff --git a/ao-tools/altosui/AltosDescent.java b/ao-tools/altosui/AltosDescent.java index ceb78e57..aacd2998 100644 --- a/ao-tools/altosui/AltosDescent.java +++ b/ao-tools/altosui/AltosDescent.java @@ -31,6 +31,52 @@ import java.util.concurrent.LinkedBlockingQueue; public class AltosDescent extends JComponent implements AltosFlightDisplay { GridBagLayout layout; + public class DescentStatus { + JLabel label; + JTextField value; + AltosLights lights; + + void show(AltosState state, int crc_errors) {} + void reset() { + value.setText(""); + lights.set(false); + } + + public DescentStatus (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + lights = new AltosLights(); + c.gridx = 0; c.gridy = y; + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(lights, c); + add(lights); + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(15); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + + } + } public class DescentValue { JLabel label; JTextField value; @@ -44,14 +90,14 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { value.setText(String.format(format, v)); } - public DescentValue (GridBagLayout layout, int y, String text) { + public DescentValue (GridBagLayout layout, int x, int y, String text) { GridBagConstraints c = new GridBagConstraints(); c.weighty = 1; label = new JLabel(text); label.setFont(Altos.label_font); label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = 0; c.gridy = y; + c.gridx = x + 0; c.gridy = y; c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.VERTICAL; @@ -59,11 +105,10 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(30); + value = new JTextField(17); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 1; c.gridy = y; - c.gridwidth = 2; + c.gridx = x + 1; c.gridy = y; c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.BOTH; c.weightx = 1; @@ -76,8 +121,8 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { void show (AltosState state, int crc_errors) { show("%6.0f m", state.height); } - public Height (GridBagLayout layout, int y) { - super (layout, y, "Height"); + public Height (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Height"); } } @@ -90,8 +135,8 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { speed = state.baro_speed; show("%6.0f m/s", speed); } - public Speed (GridBagLayout layout, int y) { - super (layout, y, "Speed"); + public Speed (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Speed"); } } @@ -115,8 +160,8 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { else value.setText("???"); } - public Lat (GridBagLayout layout, int y) { - super (layout, y, "Latitude"); + public Lat (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Latitude"); } } @@ -129,13 +174,37 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { else value.setText("???"); } - public Lon (GridBagLayout layout, int y) { - super (layout, y, "Longitude"); + public Lon (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Longitude"); } } Lon lon; + class Apogee extends DescentStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.drogue_sense)); + lights.set(state.drogue_sense > 3.2); + } + public Apogee (GridBagLayout layout, int y) { + super(layout, y, "Apogee Igniter Voltage"); + } + } + + Apogee apogee; + + class Main extends DescentStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.main_sense)); + lights.set(state.main_sense > 3.2); + } + public Main (GridBagLayout layout, int y) { + super(layout, y, "Main Igniter Voltage"); + } + } + + Main main; + class Bearing { JLabel label; JTextField value; @@ -154,14 +223,14 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { value_deg.setText("???"); } } - public Bearing (GridBagLayout layout, int y) { + public Bearing (GridBagLayout layout, int x, int y) { GridBagConstraints c = new GridBagConstraints(); c.weighty = 1; label = new JLabel("Bearing"); label.setFont(Altos.label_font); label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = 0; c.gridy = y; + c.gridx = x + 0; c.gridy = y; c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); c.anchor = GridBagConstraints.WEST; c.weightx = 0; @@ -172,9 +241,10 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { value = new JTextField(30); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 1; c.gridy = y; + c.gridx = x + 1; c.gridy = y; c.anchor = GridBagConstraints.EAST; c.weightx = 1; + c.gridwidth = 2; c.fill = GridBagConstraints.BOTH; layout.setConstraints(value, c); add(value); @@ -182,7 +252,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { value_deg = new JTextField(5); value_deg.setFont(Altos.value_font); value_deg.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 2; c.gridy = y; + c.gridx = x + 3; c.gridy = y; c.anchor = GridBagConstraints.EAST; c.weightx = 1; c.fill = GridBagConstraints.BOTH; @@ -200,8 +270,8 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { else value.setText("???"); } - public Elevation (GridBagLayout layout, int y) { - super (layout, y, "Elevation"); + public Elevation (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Elevation"); } } @@ -211,8 +281,8 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { void show (AltosState state, int crc_errors) { show("%6.0f m", state.range); } - public Range (GridBagLayout layout, int y) { - super (layout, y, "Range"); + public Range (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Range"); } } @@ -226,6 +296,8 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { bearing.reset(); elevation.reset(); range.reset(); + main.reset(); + apogee.reset(); } public void show(AltosState state, int crc_errors) { @@ -236,6 +308,8 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { range.show(state, crc_errors); lat.show(state, crc_errors); lon.show(state, crc_errors); + main.show(state, crc_errors); + apogee.show(state, crc_errors); } public AltosDescent() { @@ -244,12 +318,12 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { setLayout(layout); /* Elements in descent display */ - speed = new Speed(layout, 0); - height = new Height(layout, 1); - bearing = new Bearing(layout, 2); - elevation = new Elevation(layout, 3); - range = new Range(layout, 4); - lat = new Lat(layout, 5); - lon = new Lon(layout, 6); + speed = new Speed(layout, 0, 0); height = new Height(layout, 2, 0); + elevation = new Elevation(layout, 0, 1); range = new Range(layout, 2, 1); + bearing = new Bearing(layout, 0, 2); + lat = new Lat(layout, 0, 3); + lon = new Lon(layout, 0, 4); + apogee = new Apogee(layout, 5); + main = new Main(layout, 6); } } diff --git a/ao-tools/altosui/AltosPad.java b/ao-tools/altosui/AltosPad.java index 8b258c7d..77289f89 100644 --- a/ao-tools/altosui/AltosPad.java +++ b/ao-tools/altosui/AltosPad.java @@ -30,8 +30,6 @@ import java.util.concurrent.LinkedBlockingQueue; public class AltosPad extends JComponent implements AltosFlightDisplay { GridBagLayout layout; - Font label_font; - Font value_font; public class LaunchStatus { JLabel label; @@ -57,7 +55,7 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { add(lights); label = new JLabel(text); - label.setFont(label_font); + label.setFont(Altos.label_font); label.setHorizontalAlignment(SwingConstants.LEFT); c.gridx = 1; c.gridy = y; c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); @@ -68,7 +66,7 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { add(label); value = new JTextField(15); - value.setFont(value_font); + value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; c.anchor = GridBagConstraints.WEST; @@ -94,7 +92,7 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { c.weighty = 1; label = new JLabel(text); - label.setFont(label_font); + label.setFont(Altos.label_font); label.setHorizontalAlignment(SwingConstants.LEFT); c.gridx = 1; c.gridy = y; c.anchor = GridBagConstraints.WEST; @@ -104,7 +102,7 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { add(label); value = new JTextField(30); - value.setFont(value_font); + value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; c.anchor = GridBagConstraints.EAST; @@ -247,8 +245,6 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { public AltosPad() { layout = new GridBagLayout(); - label_font = new Font("Dialog", Font.PLAIN, 22); - value_font = new Font("Monospaced", Font.PLAIN, 22); setLayout(layout); /* Elements in pad display: -- cgit v1.2.3 From 483346a03c94b200692f5e6d59f3feee4dcf2ace Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 19 Nov 2010 12:09:46 +1000 Subject: altosui: tile site maps --- ao-tools/altosui/AltosFlightUI.java | 4 +- ao-tools/altosui/AltosSiteMap.java | 138 +++--------------------- ao-tools/altosui/AltosSiteMapTile.java | 190 +++++++++++++++++++++++++++++++++ ao-tools/altosui/Makefile.am | 1 + 4 files changed, 206 insertions(+), 127 deletions(-) create mode 100644 ao-tools/altosui/AltosSiteMapTile.java diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 1372cc00..658d6f6f 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -48,6 +48,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { private AltosFlightStatus flightStatus; private JScrollPane flightInfoPane; + private JScrollPane sitemapPane; private AltosInfoTable flightInfo; static final int tab_pad = 1; @@ -167,7 +168,8 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { pane.add("Table", flightInfoPane); sitemap = new AltosSiteMap(); - pane.add("Site Map", sitemap); + sitemapPane = new JScrollPane(sitemap); + pane.add("Site Map", sitemapPane); vbox.add(pane); diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 25b77792..df1cc246 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -32,146 +32,32 @@ import java.awt.geom.Point2D; import java.awt.geom.Line2D; public class AltosSiteMap extends JComponent implements AltosFlightDisplay { - double lat, lng; - int zoom; - double scale_x, scale_y; - Point2D.Double coord_pt; - Point2D.Double last_pt; - - Graphics2D g2d; - - private boolean setLocation(double new_lat, double new_lng) { - int new_zoom = 15; - lat = new_lat; - lng = new_lng; - zoom = new_zoom; - scale_x = 256/360.0 * Math.pow(2, zoom); - scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - coord_pt = pt(lat, lng, new Point2D.Double(0,0)); - coord_pt.x = 320-coord_pt.x; - coord_pt.y = 320-coord_pt.y; - last_pt = null; - - try { - File pngfile = new File(AltosPreferences.logdir(), - FileCoord(lat, lng, zoom)); - System.out.printf("Trying file %s\n", pngfile); - BufferedImage myPicture = ImageIO.read(pngfile); - picLabel.setIcon(new ImageIcon( myPicture )); - g2d = myPicture.createGraphics(); - } catch (Exception e) { - // throw new RuntimeException(e); - return false; - } - return true; - } - - private static double limit(double v, double lo, double hi) { - if (v < lo) - return lo; - if (hi < v) - return hi; - return v; - } - - private static String FileCoord(double lat, double lng, int zoom) { - char chlat = lat < 0 ? 'S' : 'N'; - char chlng = lng < 0 ? 'E' : 'W'; - if (lat < 0) lat = -lat; - if (lng < 0) lng = -lng; - return String.format("map-%c%.3f,%c%.3f-%d.png", - chlat, lat, chlng, lng, zoom); - } - - - // based on google js - // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js - // search for fromLatLngToPoint and fromPointToLatLng - private Point2D.Double pt(double lat, double lng) { - return pt(lat, lng, coord_pt); - } - - private Point2D.Double pt(double lat, double lng, Point2D.Double centre) { - Point2D.Double res = new Point2D.Double(); - double e; - - res.x = centre.x + lng*scale_x; - e = limit(Math.sin(Math.toRadians(lat)),-(1-1.0E-15),1-1.0E-15); - res.y = centre.y + 0.5*Math.log((1+e)/(1-e))*-scale_y; - return res; - } - - public void reset() { - // ? + // nothing } - - static Color stateColors[] = { - Color.WHITE, // startup - Color.WHITE, // idle - Color.WHITE, // pad - Color.RED, // boost - Color.PINK, // fast - Color.YELLOW, // coast - Color.CYAN, // drogue - Color.BLUE, // main - Color.BLACK // landed - }; - - boolean drawn_landed_circle = false; - boolean nomaps = false; public void show(AltosState state, int crc_errors) { - if (nomaps) - return; - if (!state.gps_ready && state.pad_lat == 0 && state.pad_lon == 0) - return; - double plat = (int)(state.pad_lat*200)/200.0; - double plon = (int)(state.pad_lon*200)/200.0; - - if (last_pt == null) { - if (!setLocation(plat, plon)) { - nomaps = true; - return; - } - } - - Point2D.Double pt = pt(state.gps.lat, state.gps.lon); - if (last_pt != null && pt != last_pt) { - if (0 <= state.state && state.state < stateColors.length) { - g2d.setColor(stateColors[state.state]); - } - g2d.draw(new Line2D.Double(last_pt, pt)); - } - - if (state.state == 8 && !drawn_landed_circle) { - drawn_landed_circle = true; - g2d.setColor(Color.RED); - g2d.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10); - g2d.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40); - g2d.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); + for (int x = 0; x < mapTiles.length; x++) { + mapTiles[x].show(state, crc_errors); } - - last_pt = pt; - repaint(); } - - JLabel picLabel = new JLabel(); + + AltosSiteMapTile [] mapTiles = new AltosSiteMapTile[9]; public AltosSiteMap() { + GridBagLayout layout = new GridBagLayout(); setLayout(layout); GridBagConstraints c = new GridBagConstraints(); - - c.gridx = 0; c.gridy = 0; - c.weightx = 1; c.weighty = 1; c.anchor = GridBagConstraints.CENTER; c.fill = GridBagConstraints.BOTH; - picLabel = new JLabel(); - JScrollPane scrollPane = new JScrollPane(picLabel); - layout.setConstraints(scrollPane, c); - add(scrollPane); + for (int x = 0; x < 9; x++) { + c.gridx = x % 3; c.gridy = x / 3; + mapTiles[x] = new AltosSiteMapTile((x%3)-1, (x/3)-1); + layout.setConstraints(mapTiles[x], c); + add(mapTiles[x]); + } } } diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java new file mode 100644 index 00000000..d84941ae --- /dev/null +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -0,0 +1,190 @@ +/* + * Copyright © 2010 Anthony Towns + * + * 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.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.lang.Math; +import java.awt.geom.Point2D; +import java.awt.geom.Line2D; + +public class AltosSiteMapTile extends JLabel { + double lat, lng; + int zoom; + double scale_x, scale_y; + Point2D.Double coord_pt; + Point2D.Double last_pt; + + Graphics2D g2d; + + int off_x; + int off_y; + + int px_size = 512; + + private boolean setLocation(double new_lat, double new_lng) { + int new_zoom = 16; + lat = new_lat; + lng = new_lng; + zoom = new_zoom; + scale_x = 256/360.0 * Math.pow(2, zoom); + scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + coord_pt = pt(lat, lng, new Point2D.Double(0,0)); + coord_pt.x = px_size/2-coord_pt.x - off_x * px_size; + coord_pt.y = px_size/2-coord_pt.y - off_y * px_size; + last_pt = null; + + Point2D.Double map_latlng; + map_latlng = latlng(new Point2D.Double(px_size/2, px_size/2)); + + BufferedImage myPicture; + File pngfile = new File(AltosPreferences.logdir(), + FileCoord(map_latlng, zoom)); + try { + myPicture = ImageIO.read(pngfile); + System.out.printf("# Found file %s\n", pngfile); + } catch (Exception e) { + // throw new RuntimeException(e); + System.out.printf("# Failed to find file %s\n", pngfile); + System.out.printf(" wget -O '%s' 'http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32'\n", pngfile, map_latlng.x, map_latlng.y, zoom, px_size, px_size); + myPicture = new BufferedImage(px_size, px_size, + BufferedImage.TYPE_INT_RGB); + } + setIcon(new ImageIcon( myPicture )); + g2d = myPicture.createGraphics(); + return true; + } + + private static double limit(double v, double lo, double hi) { + if (v < lo) + return lo; + if (hi < v) + return hi; + return v; + } + + private static String FileCoord(Point2D.Double latlng, int zoom) { + double lat, lng; + lat = latlng.x; + lng = latlng.y; + return FileCoord(lat, lng, zoom); + } + private static String FileCoord(double lat, double lng, int zoom) { + char chlat = lat < 0 ? 'S' : 'N'; + char chlng = lng < 0 ? 'E' : 'W'; + if (lat < 0) lat = -lat; + if (lng < 0) lng = -lng; + return String.format("map-%c%.3f,%c%.3f-%d.png", + chlat, lat, chlng, lng, zoom); + } + + + // based on google js + // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js + // search for fromLatLngToPoint and fromPointToLatLng + private Point2D.Double pt(double lat, double lng) { + return pt(lat, lng, coord_pt); + } + + private Point2D.Double pt(double lat, double lng, Point2D.Double centre) { + Point2D.Double res = new Point2D.Double(); + double e; + + res.x = centre.x + lng*scale_x; + e = limit(Math.sin(Math.toRadians(lat)),-(1-1.0E-15),1-1.0E-15); + res.y = centre.y + 0.5*Math.log((1+e)/(1-e))*-scale_y; + return res; + } + + private Point2D.Double latlng(Point2D.Double pt) { + return latlng(pt, coord_pt); + } + private Point2D.Double latlng(Point2D.Double pt, Point2D.Double centre) { + double lat, lng; + double rads; + + lng = (pt.x - centre.x)/scale_x; + rads = 2 * Math.atan(Math.exp((pt.y-centre.y)/-scale_y)); + lat = Math.toDegrees(rads - Math.PI/2); + + return new Point2D.Double(lat,lng); + } + + static Color stateColors[] = { + Color.WHITE, // startup + Color.WHITE, // idle + Color.WHITE, // pad + Color.RED, // boost + Color.PINK, // fast + Color.YELLOW, // coast + Color.CYAN, // drogue + Color.BLUE, // main + Color.BLACK // landed + }; + + boolean drawn_landed_circle = false; + boolean nomaps = false; + public void show(AltosState state, int crc_errors) { + if (nomaps) + return; + if (!state.gps_ready && state.pad_lat == 0 && state.pad_lon == 0) + return; + double plat = (int)(state.pad_lat*200)/200.0; + double plon = (int)(state.pad_lon*200)/200.0; + + if (last_pt == null) { + if (!setLocation(plat, plon)) { + nomaps = true; + return; + } + } + + Point2D.Double pt = pt(state.gps.lat, state.gps.lon); + if (last_pt != null && pt != last_pt) { + if (0 <= state.state && state.state < stateColors.length) { + g2d.setColor(stateColors[state.state]); + } + g2d.draw(new Line2D.Double(last_pt, pt)); + } + + if (state.state == 8 && !drawn_landed_circle) { + drawn_landed_circle = true; + g2d.setColor(Color.RED); + g2d.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10); + g2d.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40); + g2d.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); + } + + last_pt = pt; + repaint(); + } + + public AltosSiteMapTile(int x_tile_offset, int y_tile_offset) { + off_x = x_tile_offset; + off_y = y_tile_offset; + } +} + diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index 1c24ce13..b6b2e572 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -62,6 +62,7 @@ altosui_JAVA = \ AltosSerialInUseException.java \ AltosSerialMonitor.java \ AltosSiteMap.java \ + AltosSiteMapTile.java \ AltosState.java \ AltosTelemetry.java \ AltosTelemetryIterable.java \ -- cgit v1.2.3 From fda93afcd8aa4133b0e5f008b824d072e338d0ed Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 19 Nov 2010 13:02:05 +1000 Subject: AltosSiteMapTile: autoscale to about 2 nmi per tile --- ao-tools/altosui/AltosSiteMapTile.java | 37 +++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index d84941ae..919de825 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -32,7 +32,6 @@ import java.awt.geom.Point2D; import java.awt.geom.Line2D; public class AltosSiteMapTile extends JLabel { - double lat, lng; int zoom; double scale_x, scale_y; Point2D.Double coord_pt; @@ -45,16 +44,20 @@ public class AltosSiteMapTile extends JLabel { int px_size = 512; - private boolean setLocation(double new_lat, double new_lng) { - int new_zoom = 16; - lat = new_lat; - lng = new_lng; - zoom = new_zoom; + private boolean setLocation(double lat, double lng) { + Point2D.Double north_1nm; + for (zoom = 3; zoom < 22; zoom++) { + coord_pt = pt(lat, lng, new Point2D.Double(0,0), zoom); + north_1nm = pt(lat+1/60.0, lng, new Point2D.Double(0,0), zoom); + if (coord_pt.y - north_1nm.y > px_size/2) + break; + } + coord_pt.x = px_size/2 - ((long)coord_pt.x/px_size + off_x) * px_size; + coord_pt.y = px_size/2 - ((long)coord_pt.y/px_size + off_y) * px_size; + scale_x = 256/360.0 * Math.pow(2, zoom); scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - coord_pt = pt(lat, lng, new Point2D.Double(0,0)); - coord_pt.x = px_size/2-coord_pt.x - off_x * px_size; - coord_pt.y = px_size/2-coord_pt.y - off_y * px_size; + last_pt = null; Point2D.Double map_latlng; @@ -97,7 +100,7 @@ public class AltosSiteMapTile extends JLabel { char chlng = lng < 0 ? 'E' : 'W'; if (lat < 0) lat = -lat; if (lng < 0) lng = -lng; - return String.format("map-%c%.3f,%c%.3f-%d.png", + return String.format("map-%c%.6f,%c%.6f-%d.png", chlat, lat, chlng, lng, zoom); } @@ -106,10 +109,20 @@ public class AltosSiteMapTile extends JLabel { // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js // search for fromLatLngToPoint and fromPointToLatLng private Point2D.Double pt(double lat, double lng) { - return pt(lat, lng, coord_pt); + return pt(lat, lng, coord_pt, scale_x, scale_y); + } + + private static Point2D.Double pt(double lat, double lng, + Point2D.Double centre, int zoom) + { + double scale_x = 256/360.0 * Math.pow(2, zoom); + double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + return pt(lat, lng, centre, scale_x, scale_y); } - private Point2D.Double pt(double lat, double lng, Point2D.Double centre) { + private static Point2D.Double pt(double lat, double lng, + Point2D.Double centre, double scale_x, double scale_y) + { Point2D.Double res = new Point2D.Double(); double e; -- cgit v1.2.3 From fa45336062523838ba8abb08427cdc4d9c7de7a8 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 19 Nov 2010 13:17:29 +1000 Subject: AltosSiteMapTile: adjust centering calculation --- ao-tools/altosui/AltosSiteMapTile.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index 919de825..6f5ddede 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -52,8 +52,8 @@ public class AltosSiteMapTile extends JLabel { if (coord_pt.y - north_1nm.y > px_size/2) break; } - coord_pt.x = px_size/2 - ((long)coord_pt.x/px_size + off_x) * px_size; - coord_pt.y = px_size/2 - ((long)coord_pt.y/px_size + off_y) * px_size; + coord_pt.x = -px_size * Math.floor(coord_pt.x/px_size + off_x); + coord_pt.y = -px_size * Math.floor(coord_pt.y/px_size + off_y); scale_x = 256/360.0 * Math.pow(2, zoom); scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); -- cgit v1.2.3 From 90b9bc4475011bead7117ed72fa5efa0f77b2813 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 19 Nov 2010 13:30:00 +1000 Subject: AltosSiteMapTile: adjust scale to 1 nmi per tile --- ao-tools/altosui/AltosSiteMapTile.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index 6f5ddede..c14fb93f 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -45,11 +45,13 @@ public class AltosSiteMapTile extends JLabel { int px_size = 512; private boolean setLocation(double lat, double lng) { - Point2D.Double north_1nm; + Point2D.Double north_step; + double step_nm = 0.5; for (zoom = 3; zoom < 22; zoom++) { coord_pt = pt(lat, lng, new Point2D.Double(0,0), zoom); - north_1nm = pt(lat+1/60.0, lng, new Point2D.Double(0,0), zoom); - if (coord_pt.y - north_1nm.y > px_size/2) + north_step = pt(lat+step_nm/60.0, lng, + new Point2D.Double(0,0), zoom); + if (coord_pt.y - north_step.y > px_size/2) break; } coord_pt.x = -px_size * Math.floor(coord_pt.x/px_size + off_x); -- cgit v1.2.3 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 From 71c41eadd12c3ece5fffce7669e4991778046d4e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 20 Nov 2010 00:09:03 -0800 Subject: altosui: Initialize display thread state in constructor instead of run Some state will get set before run is called, initializing it there can be too late. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosDisplayThread.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ao-tools/altosui/AltosDisplayThread.java b/ao-tools/altosui/AltosDisplayThread.java index 375965b9..3e719130 100644 --- a/ao-tools/altosui/AltosDisplayThread.java +++ b/ao-tools/altosui/AltosDisplayThread.java @@ -116,10 +116,6 @@ public class AltosDisplayThread extends Thread { } public void run () { - - reported_landing = 0; - state = null; - report_interval = 10000; try { for (;;) { set_report_time(); @@ -159,6 +155,12 @@ public class AltosDisplayThread extends Thread { } else if (spoken) set_report_time(); } + + public IdleThread() { + state = null; + reported_landing = 0; + report_interval = 10000; + } } boolean tell(AltosState state, AltosState old_state) { -- cgit v1.2.3 From 9a99cabc1c34c657fc95246192ba6d330f5f22d3 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 20 Nov 2010 00:13:58 -0800 Subject: altosui: Fix channel changing in flight UI to actually work Replacing the menu with a combo box required reworking the way events are delivered from that widget back to the channel changing function. Just delete the old magic and use the JComboBox action listener directly. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosChannelMenu.java | 20 -------------------- ao-tools/altosui/AltosFlightUI.java | 5 +++-- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/ao-tools/altosui/AltosChannelMenu.java b/ao-tools/altosui/AltosChannelMenu.java index 8069c853..abbb86f4 100644 --- a/ao-tools/altosui/AltosChannelMenu.java +++ b/ao-tools/altosui/AltosChannelMenu.java @@ -30,31 +30,11 @@ import java.util.concurrent.LinkedBlockingQueue; public class AltosChannelMenu extends JComboBox implements ActionListener { int channel; - LinkedList listeners; - - public void addActionListener(ActionListener l) { - listeners.add(l); - } - - public void actionPerformed(ActionEvent e) { - channel = getSelectedIndex(); - - ActionEvent newe = new ActionEvent(this, channel, e.getActionCommand()); - - ListIterator i = listeners.listIterator(); - - while (i.hasNext()) { - ActionListener listener = i.next(); - listener.actionPerformed(newe); - } - setMaximumSize(getPreferredSize()); - } public AltosChannelMenu(int current_channel) { channel = current_channel; - listeners = new LinkedList(); for (int c = 0; c <= 9; c++) addItem(String.format("Channel %1d (%7.3fMHz)", c, 434.550 + c * 0.1)); setSelectedIndex(channel); diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 56ab7ebc..ac88aa15 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -127,6 +127,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { } Container bag; + JComboBox channels; public AltosFlightUI(AltosVoice in_voice, AltosFlightReader in_reader, final int serial) { AltosPreferences.init(this); @@ -147,10 +148,10 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { if (serial >= 0) { // Channel menu - JComboBox channels = new AltosChannelMenu(AltosPreferences.channel(serial)); + channels = new AltosChannelMenu(AltosPreferences.channel(serial)); channels.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - int channel = Integer.parseInt(e.getActionCommand()); + int channel = channels.getSelectedIndex(); reader.set_channel(channel); AltosPreferences.set_channel(serial, channel); } -- cgit v1.2.3 From 58f8d069ce9488e2987b8e92caa69fe68cda7569 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sat, 20 Nov 2010 21:06:37 +1000 Subject: AltosSiteMap: add autoscroll and grabndrag scroll --- ao-tools/altosui/AltosFlightUI.java | 4 +--- ao-tools/altosui/AltosSiteMap.java | 38 +++++++++++++++++++++++++++++++--- ao-tools/altosui/AltosSiteMapTile.java | 32 +++++++++++++++++++++------- 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 6db6c67b..c85fc977 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -46,7 +46,6 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { private AltosFlightStatus flightStatus; private JScrollPane flightInfoPane; - private JScrollPane sitemapPane; private AltosInfoTable flightInfo; static final int tab_pad = 1; @@ -192,8 +191,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { pane.add("Table", flightInfoPane); sitemap = new AltosSiteMap(); - sitemapPane = new JScrollPane(sitemap); - pane.add("Site Map", sitemapPane); + pane.add("Site Map", sitemap); c.gridx = 0; c.gridy = 2; diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index df1cc246..1db83959 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -21,6 +21,7 @@ import java.awt.*; import java.awt.image.*; import java.awt.event.*; import javax.swing.*; +import javax.swing.event.MouseInputAdapter; import javax.imageio.ImageIO; import javax.swing.table.*; import java.io.*; @@ -31,7 +32,7 @@ import java.lang.Math; import java.awt.geom.Point2D; import java.awt.geom.Line2D; -public class AltosSiteMap extends JComponent implements AltosFlightDisplay { +public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { public void reset() { // nothing } @@ -43,10 +44,40 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { AltosSiteMapTile [] mapTiles = new AltosSiteMapTile[9]; + class GrabNDrag extends MouseInputAdapter { + private JComponent scroll; + private Point startPt = new Point(); + + public GrabNDrag(JComponent parent) { + scroll = parent; + } + + public void mousePressed(MouseEvent e) { + startPt.setLocation(e.getPoint()); + } + public void mouseDragged(MouseEvent e) { + int xd = e.getX() - startPt.x; + int yd = e.getY() - startPt.y; + + Rectangle r = scroll.getVisibleRect(); + r.x -= xd; + r.y -= yd; + scroll.scrollRectToVisible(r); + } + } + public AltosSiteMap() { + JComponent comp = new JComponent() { + GrabNDrag scroller = new GrabNDrag(this); + { + addMouseMotionListener(scroller); + addMouseListener(scroller); + setAutoscrolls(true); + } + }; GridBagLayout layout = new GridBagLayout(); - setLayout(layout); + comp.setLayout(layout); GridBagConstraints c = new GridBagConstraints(); c.anchor = GridBagConstraints.CENTER; @@ -56,8 +87,9 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { c.gridx = x % 3; c.gridy = x / 3; mapTiles[x] = new AltosSiteMapTile((x%3)-1, (x/3)-1); layout.setConstraints(mapTiles[x], c); - add(mapTiles[x]); + comp.add(mapTiles[x]); } + setViewportView(comp); } } diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index c14fb93f..df57aa7d 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -65,21 +65,19 @@ public class AltosSiteMapTile extends JLabel { Point2D.Double map_latlng; map_latlng = latlng(new Point2D.Double(px_size/2, px_size/2)); - BufferedImage myPicture; File pngfile = new File(AltosPreferences.logdir(), FileCoord(map_latlng, zoom)); try { + BufferedImage myPicture; myPicture = ImageIO.read(pngfile); + setIcon(new ImageIcon( myPicture )); System.out.printf("# Found file %s\n", pngfile); + g2d = myPicture.createGraphics(); } catch (Exception e) { // throw new RuntimeException(e); System.out.printf("# Failed to find file %s\n", pngfile); System.out.printf(" wget -O '%s' 'http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32'\n", pngfile, map_latlng.x, map_latlng.y, zoom, px_size, px_size); - myPicture = new BufferedImage(px_size, px_size, - BufferedImage.TYPE_INT_RGB); } - setIcon(new ImageIcon( myPicture )); - g2d = myPicture.createGraphics(); return true; } @@ -167,8 +165,8 @@ public class AltosSiteMapTile extends JLabel { return; if (!state.gps_ready && state.pad_lat == 0 && state.pad_lon == 0) return; - double plat = (int)(state.pad_lat*200)/200.0; - double plon = (int)(state.pad_lon*200)/200.0; + double plat = state.pad_lat; + double plon = state.pad_lon; if (last_pt == null) { if (!setLocation(plat, plon)) { @@ -185,6 +183,19 @@ public class AltosSiteMapTile extends JLabel { g2d.draw(new Line2D.Double(last_pt, pt)); } + if (0 <= pt.x && pt.x < px_size) { + if (0 <= pt.y && pt.y < px_size) { + int dx = 500, dy = 250; + if (last_pt != null && state.state > 2) { + dx = Math.min(200, 20 + (int) Math.abs(last_pt.x - pt.x)); + dy = Math.min(100, 10 + (int) Math.abs(last_pt.y - pt.y)); + } + Rectangle r = new Rectangle((int)pt.x-dx, (int)pt.y-dy, + dx*2, dy*2); + scrollRectToVisible(r); + } + } + if (state.state == 8 && !drawn_landed_circle) { drawn_landed_circle = true; g2d.setColor(Color.RED); @@ -198,6 +209,13 @@ public class AltosSiteMapTile extends JLabel { } public AltosSiteMapTile(int x_tile_offset, int y_tile_offset) { + BufferedImage myPicture = new BufferedImage(px_size, px_size, + BufferedImage.TYPE_INT_RGB); + setIcon(new ImageIcon( myPicture )); + g2d = myPicture.createGraphics(); + g2d.setColor(Color.GRAY); + g2d.fillRect(0, 0, px_size, px_size); + off_x = x_tile_offset; off_y = y_tile_offset; } -- cgit v1.2.3 From 20f714bbe3137de8fb7491b39985021fd1774930 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sat, 20 Nov 2010 22:49:51 +1000 Subject: AltosSiteMapTile: seperate map and drawing layers --- ao-tools/altosui/AltosSiteMapTile.java | 94 ++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index df57aa7d..ca68412a 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -31,18 +31,33 @@ import java.lang.Math; import java.awt.geom.Point2D; import java.awt.geom.Line2D; -public class AltosSiteMapTile extends JLabel { +public class AltosSiteMapTile extends JLayeredPane { int zoom; double scale_x, scale_y; Point2D.Double coord_pt; Point2D.Double last_pt; + JLabel mapLabel; + JLabel draw; Graphics2D g2d; int off_x; int off_y; - int px_size = 512; + static final int px_size = 512; + + private void loadMap() { + Point2D.Double map_latlng = latlng(px_size/2, px_size/2); + File pngfile = new File(AltosPreferences.logdir(), + FileCoord(map_latlng, zoom)); + try { + mapLabel.setIcon(new ImageIcon(ImageIO.read(pngfile))); + } catch (Exception e) { + // throw new RuntimeException(e); + System.out.printf("# Failed to find file %s\n", pngfile); + System.out.printf(" wget -O '%s' 'http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32'\n", pngfile, map_latlng.x, map_latlng.y, zoom, px_size, px_size); + } + } private boolean setLocation(double lat, double lng) { Point2D.Double north_step; @@ -62,22 +77,6 @@ public class AltosSiteMapTile extends JLabel { last_pt = null; - Point2D.Double map_latlng; - map_latlng = latlng(new Point2D.Double(px_size/2, px_size/2)); - - File pngfile = new File(AltosPreferences.logdir(), - FileCoord(map_latlng, zoom)); - try { - BufferedImage myPicture; - myPicture = ImageIO.read(pngfile); - setIcon(new ImageIcon( myPicture )); - System.out.printf("# Found file %s\n", pngfile); - g2d = myPicture.createGraphics(); - } catch (Exception e) { - // throw new RuntimeException(e); - System.out.printf("# Failed to find file %s\n", pngfile); - System.out.printf(" wget -O '%s' 'http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32'\n", pngfile, map_latlng.x, map_latlng.y, zoom, px_size, px_size); - } return true; } @@ -132,6 +131,9 @@ public class AltosSiteMapTile extends JLabel { return res; } + private Point2D.Double latlng(double x, double y) { + return latlng(new Point2D.Double(x,y), coord_pt); + } private Point2D.Double latlng(Point2D.Double pt) { return latlng(pt, coord_pt); } @@ -159,24 +161,22 @@ public class AltosSiteMapTile extends JLabel { }; boolean drawn_landed_circle = false; - boolean nomaps = false; public void show(AltosState state, int crc_errors) { - if (nomaps) - return; - if (!state.gps_ready && state.pad_lat == 0 && state.pad_lon == 0) - return; - double plat = state.pad_lat; - double plon = state.pad_lon; + if (!state.gps_ready) { + if (state.pad_lat == 0 && state.pad_lon == 0) + return; + if (state.ngps < 3) + return; + } if (last_pt == null) { - if (!setLocation(plat, plon)) { - nomaps = true; - return; - } + setLocation(state.pad_lat, state.pad_lon); + loadMap(); + last_pt = pt; } Point2D.Double pt = pt(state.gps.lat, state.gps.lon); - if (last_pt != null && pt != last_pt) { + if (pt != last_pt) { if (0 <= state.state && state.state < stateColors.length) { g2d.setColor(stateColors[state.state]); } @@ -186,7 +186,7 @@ public class AltosSiteMapTile extends JLabel { if (0 <= pt.x && pt.x < px_size) { if (0 <= pt.y && pt.y < px_size) { int dx = 500, dy = 250; - if (last_pt != null && state.state > 2) { + if (state.state > 2) { dx = Math.min(200, 20 + (int) Math.abs(last_pt.x - pt.x)); dy = Math.min(100, 10 + (int) Math.abs(last_pt.y - pt.y)); } @@ -207,14 +207,32 @@ public class AltosSiteMapTile extends JLabel { last_pt = pt; repaint(); } - + + public static Graphics2D fillLabel(JLabel l, Color c) { + BufferedImage img = new BufferedImage(px_size, px_size, + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = img.createGraphics(); + g.setColor(c); + g.fillRect(0, 0, px_size, px_size); + l.setIcon(new ImageIcon(img)); + return g; + } + public AltosSiteMapTile(int x_tile_offset, int y_tile_offset) { - BufferedImage myPicture = new BufferedImage(px_size, px_size, - BufferedImage.TYPE_INT_RGB); - setIcon(new ImageIcon( myPicture )); - g2d = myPicture.createGraphics(); - g2d.setColor(Color.GRAY); - g2d.fillRect(0, 0, px_size, px_size); + setPreferredSize(new Dimension(px_size, px_size)); + + mapLabel = new JLabel(); + fillLabel(mapLabel, Color.GRAY); + mapLabel.setOpaque(true); + mapLabel.setBounds(0, 0, px_size, px_size); + add(mapLabel, new Integer(0)); + + draw = new JLabel(); + g2d = fillLabel(draw, new Color(127, 127, 127, 0)); + draw.setBounds(0, 0, px_size, px_size); + draw.setOpaque(false); + + add(draw, new Integer(1)); off_x = x_tile_offset; off_y = y_tile_offset; -- cgit v1.2.3 From 25ffe1cc7823895886b4777f310b4bda1c80133b Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 00:07:16 +1000 Subject: AltosSiteMap: automatic fetching of map data --- ao-tools/altosui/AltosSiteMapLabel.java | 142 ++++++++++++++++++++++++++++++++ ao-tools/altosui/AltosSiteMapTile.java | 32 +------ ao-tools/altosui/Makefile.am | 1 + 3 files changed, 147 insertions(+), 28 deletions(-) create mode 100644 ao-tools/altosui/AltosSiteMapLabel.java diff --git a/ao-tools/altosui/AltosSiteMapLabel.java b/ao-tools/altosui/AltosSiteMapLabel.java new file mode 100644 index 00000000..1a371c5b --- /dev/null +++ b/ao-tools/altosui/AltosSiteMapLabel.java @@ -0,0 +1,142 @@ +/* + * Copyright © 2010 Anthony Towns + * + * 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.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.net.URL; +import java.net.URLConnection; + +public class AltosSiteMapLabel extends JLabel { + public static boolean fetchMap(File file, String url) { + URL u; + try { + u = new URL(url); + } catch (java.net.MalformedURLException e) { + return false; + } + + byte[] data; + try { + URLConnection uc = u.openConnection(); + int contentLength = uc.getContentLength(); + InputStream in = new BufferedInputStream(uc.getInputStream()); + int bytesRead = 0; + int offset = 0; + data = new byte[contentLength]; + while (offset < contentLength) { + bytesRead = in.read(data, offset, data.length - offset); + if (bytesRead == -1) + break; + offset += bytesRead; + } + in.close(); + + if (offset != contentLength) { + return false; + } + } catch (IOException e) { + return false; + } + + try { + FileOutputStream out = new FileOutputStream(file); + out.write(data); + out.flush(); + out.close(); + } catch (FileNotFoundException e) { + return false; + } catch (IOException e) { + if (file.exists()) { + file.delete(); + } + return false; + } + return true; + } + + public void fetchAndLoadMap(final File pngfile, final String url) { + System.out.printf("# Trying to fetch %s...\n", pngfile); + + Thread thread = new Thread() { + public void run() { + try { + if (fetchMap(pngfile, url)) { + setIcon(new ImageIcon(ImageIO.read(pngfile))); + } + } catch (Exception e) { + System.out.printf("# Failed to fetch file %s\n", pngfile); + System.out.printf(" wget -O '%s' ''\n", pngfile, url); + } + } + }; + thread.start(); + } + + public void fetchMap(double lat, double lng, int zoom, int px_size) { + File pngfile = MapFile(lat, lng, zoom); + String url = MapURL(lat, lng, zoom, px_size); + + if (!pngfile.exists()) { + fetchMap(pngfile, url); + } + } + + public void loadMap(double lat, double lng, int zoom, int px_size) { + File pngfile = MapFile(lat, lng, zoom); + String url = MapURL(lat, lng, zoom, px_size); + + if (!pngfile.exists()) { + fetchAndLoadMap(pngfile, url); + return; + } + + try { + setIcon(new ImageIcon(ImageIO.read(pngfile))); + return; + } catch (IOException e) { + System.out.printf("# IO error trying to load %s\n", pngfile); + return; + } + } + + private static File MapFile(double lat, double lng, int zoom) { + char chlat = lat < 0 ? 'S' : 'N'; + char chlng = lng < 0 ? 'E' : 'W'; + if (lat < 0) lat = -lat; + if (lng < 0) lng = -lng; + return new File(AltosPreferences.logdir(), + String.format("map-%c%.6f,%c%.6f-%d.png", + chlat, lat, chlng, lng, zoom)); + } + + private static String MapURL(double lat, double lng, + int zoom, int px_size) + { + return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size); + } +} + diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index ca68412a..56bc98af 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -37,7 +37,7 @@ public class AltosSiteMapTile extends JLayeredPane { Point2D.Double coord_pt; Point2D.Double last_pt; - JLabel mapLabel; + AltosSiteMapLabel mapLabel; JLabel draw; Graphics2D g2d; @@ -48,15 +48,7 @@ public class AltosSiteMapTile extends JLayeredPane { private void loadMap() { Point2D.Double map_latlng = latlng(px_size/2, px_size/2); - File pngfile = new File(AltosPreferences.logdir(), - FileCoord(map_latlng, zoom)); - try { - mapLabel.setIcon(new ImageIcon(ImageIO.read(pngfile))); - } catch (Exception e) { - // throw new RuntimeException(e); - System.out.printf("# Failed to find file %s\n", pngfile); - System.out.printf(" wget -O '%s' 'http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32'\n", pngfile, map_latlng.x, map_latlng.y, zoom, px_size, px_size); - } + mapLabel.loadMap(map_latlng.x, map_latlng.y, zoom, px_size); } private boolean setLocation(double lat, double lng) { @@ -88,22 +80,6 @@ public class AltosSiteMapTile extends JLayeredPane { return v; } - private static String FileCoord(Point2D.Double latlng, int zoom) { - double lat, lng; - lat = latlng.x; - lng = latlng.y; - return FileCoord(lat, lng, zoom); - } - private static String FileCoord(double lat, double lng, int zoom) { - char chlat = lat < 0 ? 'S' : 'N'; - char chlng = lng < 0 ? 'E' : 'W'; - if (lat < 0) lat = -lat; - if (lng < 0) lng = -lng; - return String.format("map-%c%.6f,%c%.6f-%d.png", - chlat, lat, chlng, lng, zoom); - } - - // based on google js // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js // search for fromLatLngToPoint and fromPointToLatLng @@ -172,7 +148,7 @@ public class AltosSiteMapTile extends JLayeredPane { if (last_pt == null) { setLocation(state.pad_lat, state.pad_lon); loadMap(); - last_pt = pt; + last_pt = pt(state.pad_lat, state.pad_lon); } Point2D.Double pt = pt(state.gps.lat, state.gps.lon); @@ -221,7 +197,7 @@ public class AltosSiteMapTile extends JLayeredPane { public AltosSiteMapTile(int x_tile_offset, int y_tile_offset) { setPreferredSize(new Dimension(px_size, px_size)); - mapLabel = new JLabel(); + mapLabel = new AltosSiteMapLabel(); fillLabel(mapLabel, Color.GRAY); mapLabel.setOpaque(true); mapLabel.setBounds(0, 0, px_size, px_size); diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index 41afdf27..334608f6 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -63,6 +63,7 @@ altosui_JAVA = \ AltosSerialInUseException.java \ AltosSerialMonitor.java \ AltosSiteMap.java \ + AltosSiteMapLabel.java \ AltosSiteMapTile.java \ AltosState.java \ AltosTelemetry.java \ -- cgit v1.2.3 From 51e403145d28ac913e36d205077a613845596be2 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 00:17:51 +1000 Subject: AltosSiteMapTile: draw boost circle as well as landed --- ao-tools/altosui/AltosSiteMapTile.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index 56bc98af..6035a794 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -137,6 +137,7 @@ public class AltosSiteMapTile extends JLayeredPane { }; boolean drawn_landed_circle = false; + boolean drawn_boost_circle = false; public void show(AltosState state, int crc_errors) { if (!state.gps_ready) { if (state.pad_lat == 0 && state.pad_lon == 0) @@ -172,9 +173,16 @@ public class AltosSiteMapTile extends JLayeredPane { } } + if (state.state == 3 && !drawn_boost_circle) { + drawn_boost_circle = true; + g2d.setColor(Color.RED); + g2d.drawOval((int)last_pt.x-5, (int)last_pt.y-5, 10, 10); + g2d.drawOval((int)last_pt.x-20, (int)last_pt.y-20, 40, 40); + g2d.drawOval((int)last_pt.x-35, (int)last_pt.y-35, 70, 70); + } if (state.state == 8 && !drawn_landed_circle) { drawn_landed_circle = true; - g2d.setColor(Color.RED); + g2d.setColor(Color.BLACK); g2d.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10); g2d.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40); g2d.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); -- cgit v1.2.3 From 89f44c5587ea4f927d5e398b6af919df0d6561c3 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 01:27:01 +1000 Subject: AltosAscent/Descent: tidy up layout --- ao-tools/altosui/AltosAscent.java | 15 +-- ao-tools/altosui/AltosDescent.java | 195 ++++++++++++++++++------------------- 2 files changed, 101 insertions(+), 109 deletions(-) diff --git a/ao-tools/altosui/AltosAscent.java b/ao-tools/altosui/AltosAscent.java index 38ced95e..9bba6633 100644 --- a/ao-tools/altosui/AltosAscent.java +++ b/ao-tools/altosui/AltosAscent.java @@ -69,6 +69,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; + c.gridwidth = 2; c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.BOTH; c.weightx = 1; @@ -93,7 +94,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { label = new JLabel(text); label.setFont(Altos.label_font); label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = 0; c.gridy = y; + c.gridx = 1; c.gridy = y; c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.VERTICAL; @@ -104,7 +105,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { value = new JTextField(30); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 1; c.gridy = y; + c.gridx = 2; c.gridy = y; c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.BOTH; c.gridwidth = 2; @@ -142,7 +143,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { label = new JLabel(text); label.setFont(Altos.label_font); label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = 0; c.gridy = y; + c.gridx = 1; c.gridy = y; c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.VERTICAL; @@ -153,7 +154,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { value = new JTextField(15); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 1; c.gridy = y; + c.gridx = 2; c.gridy = y; c.anchor = GridBagConstraints.EAST; c.fill = GridBagConstraints.BOTH; c.weightx = 1; @@ -163,7 +164,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { max_value = new JTextField(15); max_value.setFont(Altos.value_font); max_value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 2; c.gridy = y; + c.gridx = 3; c.gridy = y; c.anchor = GridBagConstraints.EAST; c.fill = GridBagConstraints.BOTH; c.weightx = 1; @@ -299,14 +300,14 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { cur = new JLabel("Current"); cur.setFont(Altos.label_font); c = new GridBagConstraints(); - c.gridx = 1; c.gridy = y; + c.gridx = 2; c.gridy = y; c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); layout.setConstraints(cur, c); add(cur); max = new JLabel("Maximum"); max.setFont(Altos.label_font); - c.gridx = 2; c.gridy = y; + c.gridx = 3; c.gridy = y; layout.setConstraints(max, c); add(max); } diff --git a/ao-tools/altosui/AltosDescent.java b/ao-tools/altosui/AltosDescent.java index aacd2998..379946e1 100644 --- a/ao-tools/altosui/AltosDescent.java +++ b/ao-tools/altosui/AltosDescent.java @@ -31,12 +31,12 @@ import java.util.concurrent.LinkedBlockingQueue; public class AltosDescent extends JComponent implements AltosFlightDisplay { GridBagLayout layout; - public class DescentStatus { + public abstract class DescentStatus { JLabel label; JTextField value; AltosLights lights; - void show(AltosState state, int crc_errors) {} + abstract void show(AltosState state, int crc_errors); void reset() { value.setText(""); lights.set(false); @@ -69,6 +69,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; + c.gridwidth = 2; c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.BOTH; c.weightx = 1; @@ -77,15 +78,17 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { } } - public class DescentValue { + + public abstract class DescentValue { JLabel label; JTextField value; - void show(AltosState state, int crc_errors) {} void reset() { value.setText(""); } + abstract void show(AltosState state, int crc_errors); + void show(String format, double v) { value.setText(String.format(format, v)); } @@ -97,7 +100,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { label = new JLabel(text); label.setFont(Altos.label_font); label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = x + 0; c.gridy = y; + c.gridx = x + 1; c.gridy = y; c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.VERTICAL; @@ -108,7 +111,8 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { value = new JTextField(17); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = x + 1; c.gridy = y; + c.gridx = x + 2; c.gridy = y; + c.gridwidth = 2; c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.BOTH; c.weightx = 1; @@ -117,12 +121,70 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { } } - class Height extends DescentValue { + public abstract class DescentDualValue { + JLabel label; + JTextField value1; + JTextField value2; + + void reset() { + value1.setText(""); + value2.setText(""); + } + + abstract void show(AltosState state, int crc_errors); + void show(String v1, String v2) { + value1.setText(v1); + value2.setText(v2); + } + void show(String f1, double v1, String f2, double v2) { + value1.setText(String.format(f1, v1)); + value2.setText(String.format(f2, v2)); + } + + public DescentDualValue (GridBagLayout layout, int x, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = x + 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value1 = new JTextField(17); + value1.setFont(Altos.value_font); + value1.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = x + 2; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value1, c); + add(value1); + + value2 = new JTextField(17); + value2.setFont(Altos.value_font); + value2.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = x + 3; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value2, c); + add(value2); + } + } + + class Height extends DescentDualValue { void show (AltosState state, int crc_errors) { - show("%6.0f m", state.height); + show("%6.0f m", state.height, + "%3.0f°", state.elevation); } public Height (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Height"); + super (layout, x, y, "Height/Elevation"); } } @@ -153,33 +215,20 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { return String.format("%s %4d° %9.6f", h, deg, min); } - class Lat extends DescentValue { + class LatLon extends DescentDualValue { void show (AltosState state, int crc_errors) { if (state.gps != null) - value.setText(pos(state.gps.lat,"N", "S")); + show(pos(state.gps.lat,"N", "S"), + pos(state.gps.lon,"W", "E")); else - value.setText("???"); + show("???", "???"); } - public Lat (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Latitude"); + public LatLon (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Lat/Long"); } } - Lat lat; - - class Lon extends DescentValue { - void show (AltosState state, int crc_errors) { - if (state.gps != null) - value.setText(pos(state.gps.lon,"E", "W")); - else - value.setText("???"); - } - public Lon (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Longitude"); - } - } - - Lon lon; + LatLon latlon; class Apogee extends DescentStatus { void show (AltosState state, int crc_errors) { @@ -205,78 +254,23 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { Main main; - class Bearing { - JLabel label; - JTextField value; - JTextField value_deg; - void reset () { - value.setText(""); - value_deg.setText(""); - } + class Bearing extends DescentDualValue { void show (AltosState state, int crc_errors) { if (state.from_pad != null) { - value.setText(state.from_pad.bearing_words( - AltosGreatCircle.BEARING_LONG)); - value_deg.setText(String.format("%3.0f°", state.from_pad.bearing)); + show( state.from_pad.bearing_words( + AltosGreatCircle.BEARING_LONG), + String.format("%3.0f°", state.from_pad.bearing)); } else { - value.setText("???"); - value_deg.setText("???"); + show("???", "???"); } } public Bearing (GridBagLayout layout, int x, int y) { - GridBagConstraints c = new GridBagConstraints(); - c.weighty = 1; - - label = new JLabel("Bearing"); - label.setFont(Altos.label_font); - label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = x + 0; c.gridy = y; - c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); - c.anchor = GridBagConstraints.WEST; - c.weightx = 0; - c.fill = GridBagConstraints.VERTICAL; - layout.setConstraints(label, c); - add(label); - - value = new JTextField(30); - value.setFont(Altos.value_font); - value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = x + 1; c.gridy = y; - c.anchor = GridBagConstraints.EAST; - c.weightx = 1; - c.gridwidth = 2; - c.fill = GridBagConstraints.BOTH; - layout.setConstraints(value, c); - add(value); - - value_deg = new JTextField(5); - value_deg.setFont(Altos.value_font); - value_deg.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = x + 3; c.gridy = y; - c.anchor = GridBagConstraints.EAST; - c.weightx = 1; - c.fill = GridBagConstraints.BOTH; - layout.setConstraints(value_deg, c); - add(value_deg); - } + super (layout, x, y, "Bearing"); + } } Bearing bearing; - class Elevation extends DescentValue { - void show (AltosState state, int crc_errors) { - if (state.from_pad != null) - show("%3.0f°", state.elevation); - else - value.setText("???"); - } - public Elevation (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Elevation"); - } - } - - Elevation elevation; - class Range extends DescentValue { void show (AltosState state, int crc_errors) { show("%6.0f m", state.range); @@ -289,12 +283,10 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { Range range; public void reset() { - lat.reset(); - lon.reset(); + latlon.reset(); height.reset(); speed.reset(); bearing.reset(); - elevation.reset(); range.reset(); main.reset(); apogee.reset(); @@ -304,10 +296,8 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { height.show(state, crc_errors); speed.show(state, crc_errors); bearing.show(state, crc_errors); - elevation.show(state, crc_errors); range.show(state, crc_errors); - lat.show(state, crc_errors); - lon.show(state, crc_errors); + latlon.show(state, crc_errors); main.show(state, crc_errors); apogee.show(state, crc_errors); } @@ -318,11 +308,12 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { setLayout(layout); /* Elements in descent display */ - speed = new Speed(layout, 0, 0); height = new Height(layout, 2, 0); - elevation = new Elevation(layout, 0, 1); range = new Range(layout, 2, 1); - bearing = new Bearing(layout, 0, 2); - lat = new Lat(layout, 0, 3); - lon = new Lon(layout, 0, 4); + speed = new Speed(layout, 0, 0); + height = new Height(layout, 0, 1); + range = new Range(layout, 0, 2); + bearing = new Bearing(layout, 0, 3); + latlon = new LatLon(layout, 0, 4); + apogee = new Apogee(layout, 5); main = new Main(layout, 6); } -- cgit v1.2.3 From 6f8bc2ad20b715343e0510563ab0f14787ef3e07 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 01:34:52 +1000 Subject: AltosDescent: switch elev from height to range --- ao-tools/altosui/AltosDescent.java | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ao-tools/altosui/AltosDescent.java b/ao-tools/altosui/AltosDescent.java index 379946e1..930b8968 100644 --- a/ao-tools/altosui/AltosDescent.java +++ b/ao-tools/altosui/AltosDescent.java @@ -178,13 +178,12 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { } } - class Height extends DescentDualValue { + class Height extends DescentValue { void show (AltosState state, int crc_errors) { - show("%6.0f m", state.height, - "%3.0f°", state.elevation); + show("%6.0f m", state.height); } public Height (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Height/Elevation"); + super (layout, x, y, "Height"); } } @@ -224,7 +223,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { show("???", "???"); } public LatLon (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Lat/Long"); + super (layout, x, y, "Latitude, Longitude"); } } @@ -257,9 +256,9 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { class Bearing extends DescentDualValue { void show (AltosState state, int crc_errors) { if (state.from_pad != null) { - show( state.from_pad.bearing_words( - AltosGreatCircle.BEARING_LONG), - String.format("%3.0f°", state.from_pad.bearing)); + show( String.format("%3.0f°", state.from_pad.bearing), + state.from_pad.bearing_words( + AltosGreatCircle.BEARING_LONG)); } else { show("???", "???"); } @@ -271,12 +270,13 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { Bearing bearing; - class Range extends DescentValue { + class Range extends DescentDualValue { void show (AltosState state, int crc_errors) { - show("%6.0f m", state.range); + show("%6.0f m", state.range, + "%3.0f°", state.elevation); } public Range (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Range"); + super (layout, x, y, "Range, Elevation"); } } -- cgit v1.2.3 From b47517d4c2e49f6f7b9954d2c85f96397fe1103e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 20 Nov 2010 14:06:37 -0800 Subject: altosui: re-indent --- ao-tools/altosui/AltosAscent.java | 2 +- ao-tools/altosui/AltosDescent.java | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ao-tools/altosui/AltosAscent.java b/ao-tools/altosui/AltosAscent.java index 9bba6633..b1b812a1 100644 --- a/ao-tools/altosui/AltosAscent.java +++ b/ao-tools/altosui/AltosAscent.java @@ -69,7 +69,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; - c.gridwidth = 2; + c.gridwidth = 2; c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.BOTH; c.weightx = 1; diff --git a/ao-tools/altosui/AltosDescent.java b/ao-tools/altosui/AltosDescent.java index 930b8968..d6de8b98 100644 --- a/ao-tools/altosui/AltosDescent.java +++ b/ao-tools/altosui/AltosDescent.java @@ -69,7 +69,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; - c.gridwidth = 2; + c.gridwidth = 2; c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.BOTH; c.weightx = 1; @@ -112,7 +112,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = x + 2; c.gridy = y; - c.gridwidth = 2; + c.gridwidth = 2; c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.BOTH; c.weightx = 1; @@ -133,9 +133,9 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { abstract void show(AltosState state, int crc_errors); void show(String v1, String v2) { - value1.setText(v1); - value2.setText(v2); - } + value1.setText(v1); + value2.setText(v2); + } void show(String f1, double v1, String f2, double v2) { value1.setText(String.format(f1, v1)); value2.setText(String.format(f2, v2)); @@ -217,8 +217,8 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { class LatLon extends DescentDualValue { void show (AltosState state, int crc_errors) { if (state.gps != null) - show(pos(state.gps.lat,"N", "S"), - pos(state.gps.lon,"W", "E")); + show(pos(state.gps.lat,"N", "S"), + pos(state.gps.lon,"W", "E")); else show("???", "???"); } @@ -256,16 +256,16 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { class Bearing extends DescentDualValue { void show (AltosState state, int crc_errors) { if (state.from_pad != null) { - show( String.format("%3.0f°", state.from_pad.bearing), - state.from_pad.bearing_words( - AltosGreatCircle.BEARING_LONG)); + show( String.format("%3.0f°", state.from_pad.bearing), + state.from_pad.bearing_words( + AltosGreatCircle.BEARING_LONG)); } else { show("???", "???"); } } public Bearing (GridBagLayout layout, int x, int y) { super (layout, x, y, "Bearing"); - } + } } Bearing bearing; @@ -273,7 +273,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { class Range extends DescentDualValue { void show (AltosState state, int crc_errors) { show("%6.0f m", state.range, - "%3.0f°", state.elevation); + "%3.0f°", state.elevation); } public Range (GridBagLayout layout, int x, int y) { super (layout, x, y, "Range, Elevation"); @@ -308,11 +308,11 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { setLayout(layout); /* Elements in descent display */ - speed = new Speed(layout, 0, 0); - height = new Height(layout, 0, 1); - range = new Range(layout, 0, 2); - bearing = new Bearing(layout, 0, 3); - latlon = new LatLon(layout, 0, 4); + speed = new Speed(layout, 0, 0); + height = new Height(layout, 0, 1); + range = new Range(layout, 0, 2); + bearing = new Bearing(layout, 0, 3); + latlon = new LatLon(layout, 0, 4); apogee = new Apogee(layout, 5); main = new Main(layout, 6); -- cgit v1.2.3 From 72f5e05f9f0055f2cef8b840812f090556c94338 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 08:18:39 +1000 Subject: AltosSiteMap: major refactoring --- ao-tools/altosui/AltosSiteMap.java | 214 ++++++++++++++++++++++++++++---- ao-tools/altosui/AltosSiteMapCache.java | 103 +++++++++++++++ ao-tools/altosui/AltosSiteMapLabel.java | 142 --------------------- ao-tools/altosui/AltosSiteMapTile.java | 117 ++--------------- ao-tools/altosui/AltosUI.java | 6 +- ao-tools/altosui/Makefile.am | 3 +- 6 files changed, 316 insertions(+), 269 deletions(-) create mode 100644 ao-tools/altosui/AltosSiteMapCache.java delete mode 100644 ao-tools/altosui/AltosSiteMapLabel.java diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 1db83959..a8b66dac 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -33,36 +33,206 @@ import java.awt.geom.Point2D; import java.awt.geom.Line2D; public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { + // max vertical step in a tile in naut. miles + static final double tile_size_nmi = 1.0; + + static final int px_size = 512; + + private static Point2D.Double translatePoint(Point2D.Double p, + Point2D.Double d) + { + return new Point2D.Double(p.x + d.x, p.y + d.y); + } + + static class LatLng { + public double lat, lng; + public LatLng(double lat, double lng) { + this.lat = lat; + this.lng = lng; + } + } + + // based on google js + // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js + // search for fromLatLngToPoint and fromPointToLatLng + private static Point2D.Double pt(LatLng latlng, int zoom) { + double scale_x = 256/360.0 * Math.pow(2, zoom); + double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + return pt(latlng, scale_x, scale_y); + } + + private static Point2D.Double pt(LatLng latlng, + double scale_x, double scale_y) + { + Point2D.Double res = new Point2D.Double(); + double e; + + res.x = latlng.lng * scale_x; + + e = Math.sin(Math.toRadians(latlng.lat)); + e = Math.max(e,-(1-1.0E-15)); + e = Math.min(e, 1-1.0E-15 ); + + res.y = 0.5*Math.log((1+e)/(1-e))*-scale_y; + return res; + } + + static private LatLng latlng(Point2D.Double pt, + double scale_x, double scale_y) + { + double lat, lng; + double rads; + + lng = pt.x/scale_x; + rads = 2 * Math.atan(Math.exp(-pt.y/scale_y)); + lat = Math.toDegrees(rads - Math.PI/2); + + return new LatLng(lat,lng); + } + + int zoom; + double scale_x, scale_y; + + private Point2D.Double pt(double lat, double lng) { + return pt(new LatLng(lat, lng), scale_x, scale_y); + } + + private LatLng latlng(double x, double y) { + return latlng(new Point2D.Double(x,y), scale_x, scale_y); + } + private LatLng latlng(Point2D.Double pt) { + return latlng(pt, scale_x, scale_y); + } + + AltosSiteMapTile [] mapTiles = new AltosSiteMapTile[9]; + Point2D.Double [] tileOffset = new Point2D.Double[9]; + + private Point2D.Double getBaseLocation(double lat, double lng) { + Point2D.Double locn, north_step; + + zoom = 2; + // stupid loop structure to please Java's control flow analysis + do { + zoom++; + scale_x = 256/360.0 * Math.pow(2, zoom); + scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + locn = pt(lat, lng); + north_step = pt(lat+tile_size_nmi/60.0, lng); + if (locn.y - north_step.y > px_size) + break; + } while (zoom < 22); + locn.x = -px_size * Math.floor(locn.x/px_size); + locn.y = -px_size * Math.floor(locn.y/px_size); + return locn; + } + public void reset() { // nothing } - public void show(AltosState state, int crc_errors) { - for (int x = 0; x < mapTiles.length; x++) { - mapTiles[x].show(state, crc_errors); + + private void bgLoadMap(final int i, + final File pngfile, final String pngurl) + { + Thread thread = new Thread() { + public void run() { + ImageIcon res; + res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl); + if (res != null) { + mapTiles[i].loadMap(res); + } else { + System.out.printf("# Failed to fetch file %s\n", pngfile); + System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); + } + } + }; + thread.start(); + } + + public static void prefetchMaps(double lat, double lng, int w, int h) { + AltosPreferences.init(null); + + AltosSiteMap asm = new AltosSiteMap(true); + Point2D.Double c = asm.getBaseLocation(lat, lng); + Point2D.Double p = new Point2D.Double(); + Point2D.Double p2; + int dx = -w/2, dy = -h/2; + for (int y = dy; y < h+dy; y++) { + for (int x = dx; x < w+dx; x++) { + LatLng map_latlng = asm.latlng( + -c.x + x*px_size + px_size/2, + -c.y + y*px_size + px_size/2); + File pngfile = asm.MapFile(map_latlng.lat, map_latlng.lng); + String pngurl = asm.MapURL(map_latlng.lat, map_latlng.lng); + if (pngfile.exists()) { + System.out.printf("Already have %s\n", pngfile); + } else if (AltosSiteMapCache.fetchMap(pngfile, pngurl)) { + System.out.printf("Fetched map %s\n", pngfile); + } else { + System.out.printf("# Failed to fetch file %s\n", pngfile); + System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); + } + } } } - - AltosSiteMapTile [] mapTiles = new AltosSiteMapTile[9]; - class GrabNDrag extends MouseInputAdapter { - private JComponent scroll; - private Point startPt = new Point(); + private void initMaps(double lat, double lng) { + Point2D.Double c = getBaseLocation(lat, lng); + Point2D.Double p = new Point2D.Double(); + + for (int i = 0; i < 9; i++) { + int x = i%3 - 1, y = i/3 - 1; - public GrabNDrag(JComponent parent) { - scroll = parent; + tileOffset[i] = new Point2D.Double( + c.x - x*px_size, p.y = c.y - y*px_size); + LatLng map_latlng = latlng( + -tileOffset[i].x+px_size/2, + -tileOffset[i].y+px_size/2); + + File pngfile = MapFile(map_latlng.lat, map_latlng.lng); + String pngurl = MapURL(map_latlng.lat, map_latlng.lng); + bgLoadMap(i, pngfile, pngurl); + } + } + + private File MapFile(double lat, double lng) { + char chlat = lat < 0 ? 'S' : 'N'; + char chlng = lng < 0 ? 'E' : 'W'; + if (lat < 0) lat = -lat; + if (lng < 0) lng = -lng; + return new File(AltosPreferences.logdir(), + String.format("map-%c%.6f,%c%.6f-%d.png", + chlat, lat, chlng, lng, zoom)); + } + + private String MapURL(double lat, double lng) { + return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size); + } + + boolean initialised = false; + public void show(AltosState state, int crc_errors) { + // if insufficient gps data, nothing to update + if (!state.gps_ready) { + if (state.pad_lat == 0 && state.pad_lon == 0) + return; + if (state.ngps < 3) + return; + } + + if (!initialised) { + initMaps(state.pad_lat, state.pad_lon); + initialised = true; } - public void mousePressed(MouseEvent e) { - startPt.setLocation(e.getPoint()); + Point2D.Double pt = pt(state.gps.lat, state.gps.lon); + for (int x = 0; x < mapTiles.length; x++) { + mapTiles[x].show(state, crc_errors, + translatePoint(pt, tileOffset[x])); } - public void mouseDragged(MouseEvent e) { - int xd = e.getX() - startPt.x; - int yd = e.getY() - startPt.y; - - Rectangle r = scroll.getVisibleRect(); - r.x -= xd; - r.y -= yd; - scroll.scrollRectToVisible(r); + } + + private AltosSiteMap(boolean knowWhatYouAreDoing) { + if (!knowWhatYouAreDoing) { + throw new RuntimeException("Arggh."); } } @@ -83,9 +253,11 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { c.anchor = GridBagConstraints.CENTER; c.fill = GridBagConstraints.BOTH; + // put some space between the map tiles, debugging only + // c.insets = new Insets(5, 5, 5, 5); for (int x = 0; x < 9; x++) { c.gridx = x % 3; c.gridy = x / 3; - mapTiles[x] = new AltosSiteMapTile((x%3)-1, (x/3)-1); + mapTiles[x] = new AltosSiteMapTile(px_size); layout.setConstraints(mapTiles[x], c); comp.add(mapTiles[x]); } diff --git a/ao-tools/altosui/AltosSiteMapCache.java b/ao-tools/altosui/AltosSiteMapCache.java new file mode 100644 index 00000000..dbdcbf65 --- /dev/null +++ b/ao-tools/altosui/AltosSiteMapCache.java @@ -0,0 +1,103 @@ +/* + * Copyright © 2010 Anthony Towns + * + * 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.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.net.URL; +import java.net.URLConnection; + +public class AltosSiteMapCache extends JLabel { + public static boolean fetchMap(File file, String url) { + URL u; + try { + u = new URL(url); + } catch (java.net.MalformedURLException e) { + return false; + } + + byte[] data; + try { + URLConnection uc = u.openConnection(); + int contentLength = uc.getContentLength(); + InputStream in = new BufferedInputStream(uc.getInputStream()); + int bytesRead = 0; + int offset = 0; + data = new byte[contentLength]; + while (offset < contentLength) { + bytesRead = in.read(data, offset, data.length - offset); + if (bytesRead == -1) + break; + offset += bytesRead; + } + in.close(); + + if (offset != contentLength) { + return false; + } + } catch (IOException e) { + return false; + } + + try { + FileOutputStream out = new FileOutputStream(file); + out.write(data); + out.flush(); + out.close(); + } catch (FileNotFoundException e) { + return false; + } catch (IOException e) { + if (file.exists()) { + file.delete(); + } + return false; + } + return true; + } + + public static ImageIcon fetchAndLoadMap(File pngfile, String url) { + if (!pngfile.exists()) { + if (!fetchMap(pngfile, url)) { + return null; + } + } + return loadMap(pngfile, url); + } + + public static ImageIcon loadMap(File pngfile, String url) { + if (!pngfile.exists()) { + return null; + } + + try { + return new ImageIcon(ImageIO.read(pngfile)); + } catch (IOException e) { + System.out.printf("# IO error trying to load %s\n", pngfile); + return null; + } + } +} + diff --git a/ao-tools/altosui/AltosSiteMapLabel.java b/ao-tools/altosui/AltosSiteMapLabel.java deleted file mode 100644 index 1a371c5b..00000000 --- a/ao-tools/altosui/AltosSiteMapLabel.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright © 2010 Anthony Towns - * - * 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.image.*; -import java.awt.event.*; -import javax.swing.*; -import javax.imageio.ImageIO; -import javax.swing.table.*; -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.net.URL; -import java.net.URLConnection; - -public class AltosSiteMapLabel extends JLabel { - public static boolean fetchMap(File file, String url) { - URL u; - try { - u = new URL(url); - } catch (java.net.MalformedURLException e) { - return false; - } - - byte[] data; - try { - URLConnection uc = u.openConnection(); - int contentLength = uc.getContentLength(); - InputStream in = new BufferedInputStream(uc.getInputStream()); - int bytesRead = 0; - int offset = 0; - data = new byte[contentLength]; - while (offset < contentLength) { - bytesRead = in.read(data, offset, data.length - offset); - if (bytesRead == -1) - break; - offset += bytesRead; - } - in.close(); - - if (offset != contentLength) { - return false; - } - } catch (IOException e) { - return false; - } - - try { - FileOutputStream out = new FileOutputStream(file); - out.write(data); - out.flush(); - out.close(); - } catch (FileNotFoundException e) { - return false; - } catch (IOException e) { - if (file.exists()) { - file.delete(); - } - return false; - } - return true; - } - - public void fetchAndLoadMap(final File pngfile, final String url) { - System.out.printf("# Trying to fetch %s...\n", pngfile); - - Thread thread = new Thread() { - public void run() { - try { - if (fetchMap(pngfile, url)) { - setIcon(new ImageIcon(ImageIO.read(pngfile))); - } - } catch (Exception e) { - System.out.printf("# Failed to fetch file %s\n", pngfile); - System.out.printf(" wget -O '%s' ''\n", pngfile, url); - } - } - }; - thread.start(); - } - - public void fetchMap(double lat, double lng, int zoom, int px_size) { - File pngfile = MapFile(lat, lng, zoom); - String url = MapURL(lat, lng, zoom, px_size); - - if (!pngfile.exists()) { - fetchMap(pngfile, url); - } - } - - public void loadMap(double lat, double lng, int zoom, int px_size) { - File pngfile = MapFile(lat, lng, zoom); - String url = MapURL(lat, lng, zoom, px_size); - - if (!pngfile.exists()) { - fetchAndLoadMap(pngfile, url); - return; - } - - try { - setIcon(new ImageIcon(ImageIO.read(pngfile))); - return; - } catch (IOException e) { - System.out.printf("# IO error trying to load %s\n", pngfile); - return; - } - } - - private static File MapFile(double lat, double lng, int zoom) { - char chlat = lat < 0 ? 'S' : 'N'; - char chlng = lng < 0 ? 'E' : 'W'; - if (lat < 0) lat = -lat; - if (lng < 0) lng = -lng; - return new File(AltosPreferences.logdir(), - String.format("map-%c%.6f,%c%.6f-%d.png", - chlat, lat, chlng, lng, zoom)); - } - - private static String MapURL(double lat, double lng, - int zoom, int px_size) - { - return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size); - } -} - diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index 6035a794..de28fc8b 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -32,96 +32,15 @@ import java.awt.geom.Point2D; import java.awt.geom.Line2D; public class AltosSiteMapTile extends JLayeredPane { - int zoom; - double scale_x, scale_y; Point2D.Double coord_pt; Point2D.Double last_pt; - AltosSiteMapLabel mapLabel; + JLabel mapLabel; JLabel draw; Graphics2D g2d; - int off_x; - int off_y; - - static final int px_size = 512; - - private void loadMap() { - Point2D.Double map_latlng = latlng(px_size/2, px_size/2); - mapLabel.loadMap(map_latlng.x, map_latlng.y, zoom, px_size); - } - - private boolean setLocation(double lat, double lng) { - Point2D.Double north_step; - double step_nm = 0.5; - for (zoom = 3; zoom < 22; zoom++) { - coord_pt = pt(lat, lng, new Point2D.Double(0,0), zoom); - north_step = pt(lat+step_nm/60.0, lng, - new Point2D.Double(0,0), zoom); - if (coord_pt.y - north_step.y > px_size/2) - break; - } - coord_pt.x = -px_size * Math.floor(coord_pt.x/px_size + off_x); - coord_pt.y = -px_size * Math.floor(coord_pt.y/px_size + off_y); - - scale_x = 256/360.0 * Math.pow(2, zoom); - scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - - last_pt = null; - - return true; - } - - private static double limit(double v, double lo, double hi) { - if (v < lo) - return lo; - if (hi < v) - return hi; - return v; - } - - // based on google js - // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js - // search for fromLatLngToPoint and fromPointToLatLng - private Point2D.Double pt(double lat, double lng) { - return pt(lat, lng, coord_pt, scale_x, scale_y); - } - - private static Point2D.Double pt(double lat, double lng, - Point2D.Double centre, int zoom) - { - double scale_x = 256/360.0 * Math.pow(2, zoom); - double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - return pt(lat, lng, centre, scale_x, scale_y); - } - - private static Point2D.Double pt(double lat, double lng, - Point2D.Double centre, double scale_x, double scale_y) - { - Point2D.Double res = new Point2D.Double(); - double e; - - res.x = centre.x + lng*scale_x; - e = limit(Math.sin(Math.toRadians(lat)),-(1-1.0E-15),1-1.0E-15); - res.y = centre.y + 0.5*Math.log((1+e)/(1-e))*-scale_y; - return res; - } - - private Point2D.Double latlng(double x, double y) { - return latlng(new Point2D.Double(x,y), coord_pt); - } - private Point2D.Double latlng(Point2D.Double pt) { - return latlng(pt, coord_pt); - } - private Point2D.Double latlng(Point2D.Double pt, Point2D.Double centre) { - double lat, lng; - double rads; - - lng = (pt.x - centre.x)/scale_x; - rads = 2 * Math.atan(Math.exp((pt.y-centre.y)/-scale_y)); - lat = Math.toDegrees(rads - Math.PI/2); - - return new Point2D.Double(lat,lng); + public void loadMap(ImageIcon icn) { + mapLabel.setIcon(icn); } static Color stateColors[] = { @@ -138,21 +57,13 @@ public class AltosSiteMapTile extends JLayeredPane { boolean drawn_landed_circle = false; boolean drawn_boost_circle = false; - public void show(AltosState state, int crc_errors) { - if (!state.gps_ready) { - if (state.pad_lat == 0 && state.pad_lon == 0) - return; - if (state.ngps < 3) - return; - } - + public void show(AltosState state, int crc_errors, Point2D.Double pt) { if (last_pt == null) { - setLocation(state.pad_lat, state.pad_lon); - loadMap(); - last_pt = pt(state.pad_lat, state.pad_lon); + // setLocation(state.pad_lat, state.pad_lon); + // loadMap(); + last_pt = pt; } - Point2D.Double pt = pt(state.gps.lat, state.gps.lon); if (pt != last_pt) { if (0 <= state.state && state.state < stateColors.length) { g2d.setColor(stateColors[state.state]); @@ -160,6 +71,7 @@ public class AltosSiteMapTile extends JLayeredPane { g2d.draw(new Line2D.Double(last_pt, pt)); } + int px_size = getWidth(); if (0 <= pt.x && pt.x < px_size) { if (0 <= pt.y && pt.y < px_size) { int dx = 500, dy = 250; @@ -192,7 +104,7 @@ public class AltosSiteMapTile extends JLayeredPane { repaint(); } - public static Graphics2D fillLabel(JLabel l, Color c) { + public static Graphics2D fillLabel(JLabel l, Color c, int px_size) { BufferedImage img = new BufferedImage(px_size, px_size, BufferedImage.TYPE_INT_ARGB); Graphics2D g = img.createGraphics(); @@ -202,24 +114,21 @@ public class AltosSiteMapTile extends JLayeredPane { return g; } - public AltosSiteMapTile(int x_tile_offset, int y_tile_offset) { + public AltosSiteMapTile(int px_size) { setPreferredSize(new Dimension(px_size, px_size)); - mapLabel = new AltosSiteMapLabel(); - fillLabel(mapLabel, Color.GRAY); + mapLabel = new JLabel(); + fillLabel(mapLabel, Color.GRAY, px_size); mapLabel.setOpaque(true); mapLabel.setBounds(0, 0, px_size, px_size); add(mapLabel, new Integer(0)); draw = new JLabel(); - g2d = fillLabel(draw, new Color(127, 127, 127, 0)); + g2d = fillLabel(draw, new Color(127, 127, 127, 0), px_size); draw.setBounds(0, 0, px_size, px_size); draw.setOpaque(false); add(draw, new Integer(1)); - - off_x = x_tile_offset; - off_y = y_tile_offset; } } diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index 6bfde014..93a5e0d8 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -353,7 +353,11 @@ public class AltosUI extends JFrame { public static void main(final String[] args) { int process = 0; /* Handle batch-mode */ - if (args.length == 2 && args[0].equals("--replay")) { + if (args.length == 3 && args[0].equals("--fetchmaps")) { + double lat = Double.parseDouble(args[1]); + double lon = Double.parseDouble(args[2]); + AltosSiteMap.prefetchMaps(lat, lon, 5, 5); + } else if (args.length == 2 && args[0].equals("--replay")) { String filename = args[1]; FileInputStream in; try { diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index 334608f6..93a43b12 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -10,6 +10,7 @@ CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:../libaltos:$(FREETTS)/ bin_SCRIPTS=altosui altosui_JAVA = \ + GrabNDrag.java \ AltosAscent.java \ AltosChannelMenu.java \ AltosConfig.java \ @@ -63,7 +64,7 @@ altosui_JAVA = \ AltosSerialInUseException.java \ AltosSerialMonitor.java \ AltosSiteMap.java \ - AltosSiteMapLabel.java \ + AltosSiteMapCache.java \ AltosSiteMapTile.java \ AltosState.java \ AltosTelemetry.java \ -- cgit v1.2.3 From 66ebd954d9c9a44a8db0ee713c682e39306fabd8 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 08:28:24 +1000 Subject: Add GrabNDrag.java --- ao-tools/altosui/GrabNDrag.java | 51 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 ao-tools/altosui/GrabNDrag.java diff --git a/ao-tools/altosui/GrabNDrag.java b/ao-tools/altosui/GrabNDrag.java new file mode 100644 index 00000000..b44f3fe2 --- /dev/null +++ b/ao-tools/altosui/GrabNDrag.java @@ -0,0 +1,51 @@ +/* + * Copyright © 2010 Anthony Towns + * + * 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.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.MouseInputAdapter; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; + +class GrabNDrag extends MouseInputAdapter { + private JComponent scroll; + private Point startPt = new Point(); + + public GrabNDrag(JComponent parent) { + scroll = parent; + } + + public void mousePressed(MouseEvent e) { + startPt.setLocation(e.getPoint()); + } + public void mouseDragged(MouseEvent e) { + int xd = e.getX() - startPt.x; + int yd = e.getY() - startPt.y; + + Rectangle r = scroll.getVisibleRect(); + r.x -= xd; + r.y -= yd; + scroll.scrollRectToVisible(r); + } +} -- cgit v1.2.3 From 37f0201d724693528f37ac7d275f68f90cf94da0 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 20 Nov 2010 14:31:23 -0800 Subject: altosui: change descent tab formatting to four columns This places labels to the left of each field. For igniter voltages, it uses three columns for the labels. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosAscent.java | 8 ++-- ao-tools/altosui/AltosDescent.java | 93 ++++++++++++++++++++++++++------------ ao-tools/altosui/AltosLanded.java | 2 +- ao-tools/altosui/AltosPad.java | 4 +- 4 files changed, 70 insertions(+), 37 deletions(-) diff --git a/ao-tools/altosui/AltosAscent.java b/ao-tools/altosui/AltosAscent.java index b1b812a1..2ceaa183 100644 --- a/ao-tools/altosui/AltosAscent.java +++ b/ao-tools/altosui/AltosAscent.java @@ -65,7 +65,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(15); + value = new JTextField(17); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; @@ -102,7 +102,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(30); + value = new JTextField(17); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; @@ -151,7 +151,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(15); + value = new JTextField(17); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; @@ -161,7 +161,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { layout.setConstraints(value, c); add(value); - max_value = new JTextField(15); + max_value = new JTextField(17); max_value.setFont(Altos.value_font); max_value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 3; c.gridy = y; diff --git a/ao-tools/altosui/AltosDescent.java b/ao-tools/altosui/AltosDescent.java index d6de8b98..abe64fdc 100644 --- a/ao-tools/altosui/AltosDescent.java +++ b/ao-tools/altosui/AltosDescent.java @@ -61,15 +61,16 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.VERTICAL; + c.gridwidth = 3; c.weightx = 0; layout.setConstraints(label, c); add(label); - value = new JTextField(15); + value = new JTextField(17); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 2; c.gridy = y; - c.gridwidth = 2; + c.gridx = 4; c.gridy = y; + c.gridwidth = 1; c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.BOTH; c.weightx = 1; @@ -93,6 +94,10 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { value.setText(String.format(format, v)); } + void show(String v) { + value.setText(v); + } + public DescentValue (GridBagLayout layout, int x, int y, String text) { GridBagConstraints c = new GridBagConstraints(); c.weighty = 1; @@ -105,19 +110,17 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.VERTICAL; c.weightx = 0; - layout.setConstraints(label, c); - add(label); + add(label, c); value = new JTextField(17); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = x + 2; c.gridy = y; - c.gridwidth = 2; + c.gridwidth = 1; c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.BOTH; c.weightx = 1; - layout.setConstraints(value, c); - add(value); + add(value, c); } } @@ -169,10 +172,11 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { value2 = new JTextField(17); value2.setFont(Altos.value_font); value2.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = x + 3; c.gridy = y; + c.gridx = x + 4; c.gridy = y; c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.BOTH; c.weightx = 1; + c.gridwidth = 1; layout.setConstraints(value2, c); add(value2); } @@ -211,23 +215,36 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { } int deg = (int) Math.floor(p); double min = (p - Math.floor(p)) * 60.0; - return String.format("%s %4d° %9.6f", h, deg, min); + return String.format("%s %d° %9.6f", h, deg, min); } - class LatLon extends DescentDualValue { + class Lat extends DescentValue { void show (AltosState state, int crc_errors) { if (state.gps != null) - show(pos(state.gps.lat,"N", "S"), - pos(state.gps.lon,"W", "E")); + show(pos(state.gps.lat,"N", "S")); else - show("???", "???"); + show("???"); } - public LatLon (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Latitude, Longitude"); + public Lat (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Latitude"); } } - LatLon latlon; + Lat lat; + + class Lon extends DescentValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + show(pos(state.gps.lon,"W", "E")); + else + show("???"); + } + public Lon (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Longitude"); + } + } + + Lon lon; class Apogee extends DescentStatus { void show (AltosState state, int crc_errors) { @@ -270,24 +287,36 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { Bearing bearing; - class Range extends DescentDualValue { + class Range extends DescentValue { void show (AltosState state, int crc_errors) { - show("%6.0f m", state.range, - "%3.0f°", state.elevation); + show("%6.0f m", state.range); } public Range (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Range, Elevation"); + super (layout, x, y, "Range"); } } Range range; + class Elevation extends DescentValue { + void show (AltosState state, int crc_errors) { + show("%3.0f°", state.elevation); + } + public Elevation (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Elevation"); + } + } + + Elevation elevation; + public void reset() { - latlon.reset(); + lat.reset(); + lon.reset(); height.reset(); speed.reset(); bearing.reset(); range.reset(); + elevation.reset(); main.reset(); apogee.reset(); } @@ -297,7 +326,9 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { speed.show(state, crc_errors); bearing.show(state, crc_errors); range.show(state, crc_errors); - latlon.show(state, crc_errors); + elevation.show(state, crc_errors); + lat.show(state, crc_errors); + lon.show(state, crc_errors); main.show(state, crc_errors); apogee.show(state, crc_errors); } @@ -309,12 +340,14 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { /* Elements in descent display */ speed = new Speed(layout, 0, 0); - height = new Height(layout, 0, 1); - range = new Range(layout, 0, 2); - bearing = new Bearing(layout, 0, 3); - latlon = new LatLon(layout, 0, 4); - - apogee = new Apogee(layout, 5); - main = new Main(layout, 6); + height = new Height(layout, 2, 0); + elevation = new Elevation(layout, 0, 1); + range = new Range(layout, 2, 1); + bearing = new Bearing(layout, 0, 2); + lat = new Lat(layout, 0, 3); + lon = new Lon(layout, 2, 3); + + apogee = new Apogee(layout, 4); + main = new Main(layout, 5); } } diff --git a/ao-tools/altosui/AltosLanded.java b/ao-tools/altosui/AltosLanded.java index 0656ea6c..059dbb6d 100644 --- a/ao-tools/altosui/AltosLanded.java +++ b/ao-tools/altosui/AltosLanded.java @@ -61,7 +61,7 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(30); + value = new JTextField(17); value.setFont(value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 1; c.gridy = y; diff --git a/ao-tools/altosui/AltosPad.java b/ao-tools/altosui/AltosPad.java index 77289f89..480e4d79 100644 --- a/ao-tools/altosui/AltosPad.java +++ b/ao-tools/altosui/AltosPad.java @@ -65,7 +65,7 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(15); + value = new JTextField(17); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; @@ -101,7 +101,7 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(30); + value = new JTextField(17); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; -- cgit v1.2.3 From 82636305021c41d676f5f0f11378724fe0de0079 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 08:44:13 +1000 Subject: AltosSiteMap: be more polite about preferred size --- ao-tools/altosui/AltosSiteMap.java | 1 + 1 file changed, 1 insertion(+) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index a8b66dac..a241cbd0 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -262,6 +262,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { comp.add(mapTiles[x]); } setViewportView(comp); + setPreferredSize(new Dimension(500,200)); } } -- cgit v1.2.3 From 1e712647dd6df1e77650db705f3ac32a3c8f6907 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 08:58:44 +1000 Subject: altosui: reindent --- ao-tools/altosui/AltosSiteMap.java | 463 ++++++++++++++++---------------- ao-tools/altosui/AltosSiteMapCache.java | 126 ++++----- ao-tools/altosui/AltosSiteMapTile.java | 196 +++++++------- 3 files changed, 393 insertions(+), 392 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index a241cbd0..5f5e30f0 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -33,236 +33,237 @@ import java.awt.geom.Point2D; import java.awt.geom.Line2D; public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { - // max vertical step in a tile in naut. miles - static final double tile_size_nmi = 1.0; - - static final int px_size = 512; - - private static Point2D.Double translatePoint(Point2D.Double p, - Point2D.Double d) - { - return new Point2D.Double(p.x + d.x, p.y + d.y); - } - - static class LatLng { - public double lat, lng; - public LatLng(double lat, double lng) { - this.lat = lat; - this.lng = lng; - } - } - - // based on google js - // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js - // search for fromLatLngToPoint and fromPointToLatLng - private static Point2D.Double pt(LatLng latlng, int zoom) { - double scale_x = 256/360.0 * Math.pow(2, zoom); - double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - return pt(latlng, scale_x, scale_y); - } - - private static Point2D.Double pt(LatLng latlng, - double scale_x, double scale_y) - { - Point2D.Double res = new Point2D.Double(); - double e; - - res.x = latlng.lng * scale_x; - - e = Math.sin(Math.toRadians(latlng.lat)); - e = Math.max(e,-(1-1.0E-15)); - e = Math.min(e, 1-1.0E-15 ); - - res.y = 0.5*Math.log((1+e)/(1-e))*-scale_y; - return res; - } - - static private LatLng latlng(Point2D.Double pt, - double scale_x, double scale_y) - { - double lat, lng; - double rads; - - lng = pt.x/scale_x; - rads = 2 * Math.atan(Math.exp(-pt.y/scale_y)); - lat = Math.toDegrees(rads - Math.PI/2); - - return new LatLng(lat,lng); - } - - int zoom; - double scale_x, scale_y; - - private Point2D.Double pt(double lat, double lng) { - return pt(new LatLng(lat, lng), scale_x, scale_y); - } - - private LatLng latlng(double x, double y) { - return latlng(new Point2D.Double(x,y), scale_x, scale_y); - } - private LatLng latlng(Point2D.Double pt) { - return latlng(pt, scale_x, scale_y); - } - - AltosSiteMapTile [] mapTiles = new AltosSiteMapTile[9]; - Point2D.Double [] tileOffset = new Point2D.Double[9]; - - private Point2D.Double getBaseLocation(double lat, double lng) { - Point2D.Double locn, north_step; - - zoom = 2; - // stupid loop structure to please Java's control flow analysis - do { - zoom++; - scale_x = 256/360.0 * Math.pow(2, zoom); - scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - locn = pt(lat, lng); - north_step = pt(lat+tile_size_nmi/60.0, lng); - if (locn.y - north_step.y > px_size) - break; - } while (zoom < 22); - locn.x = -px_size * Math.floor(locn.x/px_size); - locn.y = -px_size * Math.floor(locn.y/px_size); - return locn; - } - - public void reset() { - // nothing - } - - private void bgLoadMap(final int i, - final File pngfile, final String pngurl) - { - Thread thread = new Thread() { - public void run() { - ImageIcon res; - res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl); - if (res != null) { - mapTiles[i].loadMap(res); - } else { - System.out.printf("# Failed to fetch file %s\n", pngfile); - System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); - } - } - }; - thread.start(); - } - - public static void prefetchMaps(double lat, double lng, int w, int h) { - AltosPreferences.init(null); - - AltosSiteMap asm = new AltosSiteMap(true); - Point2D.Double c = asm.getBaseLocation(lat, lng); - Point2D.Double p = new Point2D.Double(); - Point2D.Double p2; - int dx = -w/2, dy = -h/2; - for (int y = dy; y < h+dy; y++) { - for (int x = dx; x < w+dx; x++) { - LatLng map_latlng = asm.latlng( - -c.x + x*px_size + px_size/2, - -c.y + y*px_size + px_size/2); - File pngfile = asm.MapFile(map_latlng.lat, map_latlng.lng); - String pngurl = asm.MapURL(map_latlng.lat, map_latlng.lng); - if (pngfile.exists()) { - System.out.printf("Already have %s\n", pngfile); - } else if (AltosSiteMapCache.fetchMap(pngfile, pngurl)) { - System.out.printf("Fetched map %s\n", pngfile); - } else { - System.out.printf("# Failed to fetch file %s\n", pngfile); - System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); - } - } - } - } - - private void initMaps(double lat, double lng) { - Point2D.Double c = getBaseLocation(lat, lng); - Point2D.Double p = new Point2D.Double(); - - for (int i = 0; i < 9; i++) { - int x = i%3 - 1, y = i/3 - 1; - - tileOffset[i] = new Point2D.Double( - c.x - x*px_size, p.y = c.y - y*px_size); - LatLng map_latlng = latlng( - -tileOffset[i].x+px_size/2, - -tileOffset[i].y+px_size/2); - - File pngfile = MapFile(map_latlng.lat, map_latlng.lng); - String pngurl = MapURL(map_latlng.lat, map_latlng.lng); - bgLoadMap(i, pngfile, pngurl); - } - } - - private File MapFile(double lat, double lng) { - char chlat = lat < 0 ? 'S' : 'N'; - char chlng = lng < 0 ? 'E' : 'W'; - if (lat < 0) lat = -lat; - if (lng < 0) lng = -lng; - return new File(AltosPreferences.logdir(), - String.format("map-%c%.6f,%c%.6f-%d.png", - chlat, lat, chlng, lng, zoom)); - } - - private String MapURL(double lat, double lng) { - return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size); - } - - boolean initialised = false; - public void show(AltosState state, int crc_errors) { - // if insufficient gps data, nothing to update - if (!state.gps_ready) { - if (state.pad_lat == 0 && state.pad_lon == 0) - return; - if (state.ngps < 3) - return; - } - - if (!initialised) { - initMaps(state.pad_lat, state.pad_lon); - initialised = true; - } - - Point2D.Double pt = pt(state.gps.lat, state.gps.lon); - for (int x = 0; x < mapTiles.length; x++) { - mapTiles[x].show(state, crc_errors, - translatePoint(pt, tileOffset[x])); - } - } - - private AltosSiteMap(boolean knowWhatYouAreDoing) { - if (!knowWhatYouAreDoing) { - throw new RuntimeException("Arggh."); - } - } - - public AltosSiteMap() { - JComponent comp = new JComponent() { - GrabNDrag scroller = new GrabNDrag(this); - { - addMouseMotionListener(scroller); - addMouseListener(scroller); - setAutoscrolls(true); - } - }; - - GridBagLayout layout = new GridBagLayout(); - comp.setLayout(layout); - - GridBagConstraints c = new GridBagConstraints(); - c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.BOTH; - - // put some space between the map tiles, debugging only - // c.insets = new Insets(5, 5, 5, 5); - for (int x = 0; x < 9; x++) { - c.gridx = x % 3; c.gridy = x / 3; - mapTiles[x] = new AltosSiteMapTile(px_size); - layout.setConstraints(mapTiles[x], c); - comp.add(mapTiles[x]); - } - setViewportView(comp); - setPreferredSize(new Dimension(500,200)); - } + // max vertical step in a tile in naut. miles + static final double tile_size_nmi = 1.0; + + static final int px_size = 512; + + private static Point2D.Double translatePoint(Point2D.Double p, + Point2D.Double d) + { + return new Point2D.Double(p.x + d.x, p.y + d.y); + } + + static class LatLng { + public double lat, lng; + public LatLng(double lat, double lng) { + this.lat = lat; + this.lng = lng; + } + } + + // based on google js + // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js + // search for fromLatLngToPoint and fromPointToLatLng + private static Point2D.Double pt(LatLng latlng, int zoom) { + double scale_x = 256/360.0 * Math.pow(2, zoom); + double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + return pt(latlng, scale_x, scale_y); + } + + private static Point2D.Double pt(LatLng latlng, + double scale_x, double scale_y) + { + Point2D.Double res = new Point2D.Double(); + double e; + + res.x = latlng.lng * scale_x; + + e = Math.sin(Math.toRadians(latlng.lat)); + e = Math.max(e,-(1-1.0E-15)); + e = Math.min(e, 1-1.0E-15 ); + + res.y = 0.5*Math.log((1+e)/(1-e))*-scale_y; + return res; + } + + static private LatLng latlng(Point2D.Double pt, + double scale_x, double scale_y) + { + double lat, lng; + double rads; + + lng = pt.x/scale_x; + rads = 2 * Math.atan(Math.exp(-pt.y/scale_y)); + lat = Math.toDegrees(rads - Math.PI/2); + + return new LatLng(lat,lng); + } + + int zoom; + double scale_x, scale_y; + + private Point2D.Double pt(double lat, double lng) { + return pt(new LatLng(lat, lng), scale_x, scale_y); + } + + private LatLng latlng(double x, double y) { + return latlng(new Point2D.Double(x,y), scale_x, scale_y); + } + private LatLng latlng(Point2D.Double pt) { + return latlng(pt, scale_x, scale_y); + } + + AltosSiteMapTile [] mapTiles = new AltosSiteMapTile[9]; + Point2D.Double [] tileOffset = new Point2D.Double[9]; + + private Point2D.Double getBaseLocation(double lat, double lng) { + Point2D.Double locn, north_step; + + zoom = 2; + // stupid loop structure to please Java's control flow analysis + do { + zoom++; + scale_x = 256/360.0 * Math.pow(2, zoom); + scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + locn = pt(lat, lng); + north_step = pt(lat+tile_size_nmi/60.0, lng); + if (locn.y - north_step.y > px_size) + break; + } while (zoom < 22); + locn.x = -px_size * Math.floor(locn.x/px_size); + locn.y = -px_size * Math.floor(locn.y/px_size); + return locn; + } + + public void reset() { + // nothing + } + + private void bgLoadMap(final int i, + final File pngfile, final String pngurl) + { + Thread thread = new Thread() { + public void run() { + ImageIcon res; + res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl); + if (res != null) { + mapTiles[i].loadMap(res); + } else { + System.out.printf("# Failed to fetch file %s\n", pngfile); + System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); + } + } + }; + thread.start(); + } + + public static void prefetchMaps(double lat, double lng, int w, int h) { + AltosPreferences.init(null); + + AltosSiteMap asm = new AltosSiteMap(true); + Point2D.Double c = asm.getBaseLocation(lat, lng); + Point2D.Double p = new Point2D.Double(); + Point2D.Double p2; + int dx = -w/2, dy = -h/2; + for (int y = dy; y < h+dy; y++) { + for (int x = dx; x < w+dx; x++) { + LatLng map_latlng = asm.latlng( + -c.x + x*px_size + px_size/2, + -c.y + y*px_size + px_size/2); + File pngfile = asm.MapFile(map_latlng.lat, map_latlng.lng); + String pngurl = asm.MapURL(map_latlng.lat, map_latlng.lng); + if (pngfile.exists()) { + System.out.printf("Already have %s\n", pngfile); + } else if (AltosSiteMapCache.fetchMap(pngfile, pngurl)) { + System.out.printf("Fetched map %s\n", pngfile); + } else { + System.out.printf("# Failed to fetch file %s\n", pngfile); + System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); + } + } + } + } + + private void initMaps(double lat, double lng) { + Point2D.Double c = getBaseLocation(lat, lng); + Point2D.Double p = new Point2D.Double(); + + for (int i = 0; i < 9; i++) { + int x = i%3 - 1, y = i/3 - 1; + + tileOffset[i] = new Point2D.Double( + c.x - x*px_size, p.y = c.y - y*px_size); + LatLng map_latlng = latlng( + -tileOffset[i].x+px_size/2, + -tileOffset[i].y+px_size/2); + + File pngfile = MapFile(map_latlng.lat, map_latlng.lng); + String pngurl = MapURL(map_latlng.lat, map_latlng.lng); + bgLoadMap(i, pngfile, pngurl); + } + } + + private File MapFile(double lat, double lng) { + char chlat = lat < 0 ? 'S' : 'N'; + char chlng = lng < 0 ? 'E' : 'W'; + if (lat < 0) lat = -lat; + if (lng < 0) lng = -lng; + return new File(AltosPreferences.logdir(), + String.format("map-%c%.6f,%c%.6f-%d.png", + chlat, lat, chlng, lng, zoom)); + } + + private String MapURL(double lat, double lng) { + return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size); + } + + boolean initialised = false; + public void show(AltosState state, int crc_errors) { + // if insufficient gps data, nothing to update + if (!state.gps_ready) { + if (state.pad_lat == 0 && state.pad_lon == 0) + return; + if (state.ngps < 3) + return; + } + + if (!initialised) { + initMaps(state.pad_lat, state.pad_lon); + initialised = true; + } + + Point2D.Double pt = pt(state.gps.lat, state.gps.lon); + for (int x = 0; x < mapTiles.length; x++) { + mapTiles[x].show(state, crc_errors, + translatePoint(pt, tileOffset[x])); + } + } + + private AltosSiteMap(boolean knowWhatYouAreDoing) { + if (!knowWhatYouAreDoing) { + throw new RuntimeException("Arggh."); + } + } + + public AltosSiteMap() { + JComponent comp = new JComponent() { + GrabNDrag scroller = new GrabNDrag(this); + { + addMouseMotionListener(scroller); + addMouseListener(scroller); + setAutoscrolls(true); + } + }; + + GridBagLayout layout = new GridBagLayout(); + comp.setLayout(layout); + + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + + // put some space between the map tiles, debugging only + // c.insets = new Insets(5, 5, 5, 5); + for (int x = 0; x < 9; x++) { + c.gridx = x % 3; + c.gridy = x / 3; + mapTiles[x] = new AltosSiteMapTile(px_size); + layout.setConstraints(mapTiles[x], c); + comp.add(mapTiles[x]); + } + setViewportView(comp); + setPreferredSize(new Dimension(500,200)); + } } diff --git a/ao-tools/altosui/AltosSiteMapCache.java b/ao-tools/altosui/AltosSiteMapCache.java index dbdcbf65..e9dbf8e6 100644 --- a/ao-tools/altosui/AltosSiteMapCache.java +++ b/ao-tools/altosui/AltosSiteMapCache.java @@ -31,73 +31,73 @@ import java.net.URL; import java.net.URLConnection; public class AltosSiteMapCache extends JLabel { - public static boolean fetchMap(File file, String url) { - URL u; - try { - u = new URL(url); - } catch (java.net.MalformedURLException e) { - return false; - } + public static boolean fetchMap(File file, String url) { + URL u; + try { + u = new URL(url); + } catch (java.net.MalformedURLException e) { + return false; + } - byte[] data; - try { - URLConnection uc = u.openConnection(); - int contentLength = uc.getContentLength(); - InputStream in = new BufferedInputStream(uc.getInputStream()); - int bytesRead = 0; - int offset = 0; - data = new byte[contentLength]; - while (offset < contentLength) { - bytesRead = in.read(data, offset, data.length - offset); - if (bytesRead == -1) - break; - offset += bytesRead; - } - in.close(); + byte[] data; + try { + URLConnection uc = u.openConnection(); + int contentLength = uc.getContentLength(); + InputStream in = new BufferedInputStream(uc.getInputStream()); + int bytesRead = 0; + int offset = 0; + data = new byte[contentLength]; + while (offset < contentLength) { + bytesRead = in.read(data, offset, data.length - offset); + if (bytesRead == -1) + break; + offset += bytesRead; + } + in.close(); - if (offset != contentLength) { - return false; - } - } catch (IOException e) { - return false; - } - - try { - FileOutputStream out = new FileOutputStream(file); - out.write(data); - out.flush(); - out.close(); - } catch (FileNotFoundException e) { - return false; - } catch (IOException e) { - if (file.exists()) { - file.delete(); - } - return false; - } - return true; - } + if (offset != contentLength) { + return false; + } + } catch (IOException e) { + return false; + } - public static ImageIcon fetchAndLoadMap(File pngfile, String url) { - if (!pngfile.exists()) { - if (!fetchMap(pngfile, url)) { - return null; - } - } - return loadMap(pngfile, url); - } + try { + FileOutputStream out = new FileOutputStream(file); + out.write(data); + out.flush(); + out.close(); + } catch (FileNotFoundException e) { + return false; + } catch (IOException e) { + if (file.exists()) { + file.delete(); + } + return false; + } + return true; + } - public static ImageIcon loadMap(File pngfile, String url) { - if (!pngfile.exists()) { - return null; - } + public static ImageIcon fetchAndLoadMap(File pngfile, String url) { + if (!pngfile.exists()) { + if (!fetchMap(pngfile, url)) { + return null; + } + } + return loadMap(pngfile, url); + } - try { - return new ImageIcon(ImageIO.read(pngfile)); - } catch (IOException e) { - System.out.printf("# IO error trying to load %s\n", pngfile); - return null; - } - } + public static ImageIcon loadMap(File pngfile, String url) { + if (!pngfile.exists()) { + return null; + } + + try { + return new ImageIcon(ImageIO.read(pngfile)); + } catch (IOException e) { + System.out.printf("# IO error trying to load %s\n", pngfile); + return null; + } + } } diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index de28fc8b..8aee86c1 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -32,103 +32,103 @@ import java.awt.geom.Point2D; import java.awt.geom.Line2D; public class AltosSiteMapTile extends JLayeredPane { - Point2D.Double coord_pt; - Point2D.Double last_pt; - - JLabel mapLabel; - JLabel draw; - Graphics2D g2d; - - public void loadMap(ImageIcon icn) { - mapLabel.setIcon(icn); - } - - static Color stateColors[] = { - Color.WHITE, // startup - Color.WHITE, // idle - Color.WHITE, // pad - Color.RED, // boost - Color.PINK, // fast - Color.YELLOW, // coast - Color.CYAN, // drogue - Color.BLUE, // main - Color.BLACK // landed - }; - - boolean drawn_landed_circle = false; - boolean drawn_boost_circle = false; - public void show(AltosState state, int crc_errors, Point2D.Double pt) { - if (last_pt == null) { - // setLocation(state.pad_lat, state.pad_lon); - // loadMap(); - last_pt = pt; - } - - if (pt != last_pt) { - if (0 <= state.state && state.state < stateColors.length) { - g2d.setColor(stateColors[state.state]); - } - g2d.draw(new Line2D.Double(last_pt, pt)); - } - - int px_size = getWidth(); - if (0 <= pt.x && pt.x < px_size) { - if (0 <= pt.y && pt.y < px_size) { - int dx = 500, dy = 250; - if (state.state > 2) { - dx = Math.min(200, 20 + (int) Math.abs(last_pt.x - pt.x)); - dy = Math.min(100, 10 + (int) Math.abs(last_pt.y - pt.y)); - } - Rectangle r = new Rectangle((int)pt.x-dx, (int)pt.y-dy, - dx*2, dy*2); - scrollRectToVisible(r); - } - } - - if (state.state == 3 && !drawn_boost_circle) { - drawn_boost_circle = true; - g2d.setColor(Color.RED); - g2d.drawOval((int)last_pt.x-5, (int)last_pt.y-5, 10, 10); - g2d.drawOval((int)last_pt.x-20, (int)last_pt.y-20, 40, 40); - g2d.drawOval((int)last_pt.x-35, (int)last_pt.y-35, 70, 70); - } - if (state.state == 8 && !drawn_landed_circle) { - drawn_landed_circle = true; - g2d.setColor(Color.BLACK); - g2d.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10); - g2d.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40); - g2d.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); - } - - last_pt = pt; - repaint(); - } - - public static Graphics2D fillLabel(JLabel l, Color c, int px_size) { - BufferedImage img = new BufferedImage(px_size, px_size, - BufferedImage.TYPE_INT_ARGB); - Graphics2D g = img.createGraphics(); - g.setColor(c); - g.fillRect(0, 0, px_size, px_size); - l.setIcon(new ImageIcon(img)); - return g; - } - - public AltosSiteMapTile(int px_size) { - setPreferredSize(new Dimension(px_size, px_size)); - - mapLabel = new JLabel(); - fillLabel(mapLabel, Color.GRAY, px_size); - mapLabel.setOpaque(true); - mapLabel.setBounds(0, 0, px_size, px_size); - add(mapLabel, new Integer(0)); - - draw = new JLabel(); - g2d = fillLabel(draw, new Color(127, 127, 127, 0), px_size); - draw.setBounds(0, 0, px_size, px_size); - draw.setOpaque(false); - - add(draw, new Integer(1)); - } + Point2D.Double coord_pt; + Point2D.Double last_pt; + + JLabel mapLabel; + JLabel draw; + Graphics2D g2d; + + public void loadMap(ImageIcon icn) { + mapLabel.setIcon(icn); + } + + static Color stateColors[] = { + Color.WHITE, // startup + Color.WHITE, // idle + Color.WHITE, // pad + Color.RED, // boost + Color.PINK, // fast + Color.YELLOW, // coast + Color.CYAN, // drogue + Color.BLUE, // main + Color.BLACK // landed + }; + + boolean drawn_landed_circle = false; + boolean drawn_boost_circle = false; + public void show(AltosState state, int crc_errors, Point2D.Double pt) { + if (last_pt == null) { + // setLocation(state.pad_lat, state.pad_lon); + // loadMap(); + last_pt = pt; + } + + if (pt != last_pt) { + if (0 <= state.state && state.state < stateColors.length) { + g2d.setColor(stateColors[state.state]); + } + g2d.draw(new Line2D.Double(last_pt, pt)); + } + + int px_size = getWidth(); + if (0 <= pt.x && pt.x < px_size) { + if (0 <= pt.y && pt.y < px_size) { + int dx = 500, dy = 250; + if (state.state > 2) { + dx = Math.min(200, 20 + (int) Math.abs(last_pt.x - pt.x)); + dy = Math.min(100, 10 + (int) Math.abs(last_pt.y - pt.y)); + } + Rectangle r = new Rectangle((int)pt.x-dx, (int)pt.y-dy, + dx*2, dy*2); + scrollRectToVisible(r); + } + } + + if (state.state == 3 && !drawn_boost_circle) { + drawn_boost_circle = true; + g2d.setColor(Color.RED); + g2d.drawOval((int)last_pt.x-5, (int)last_pt.y-5, 10, 10); + g2d.drawOval((int)last_pt.x-20, (int)last_pt.y-20, 40, 40); + g2d.drawOval((int)last_pt.x-35, (int)last_pt.y-35, 70, 70); + } + if (state.state == 8 && !drawn_landed_circle) { + drawn_landed_circle = true; + g2d.setColor(Color.BLACK); + g2d.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10); + g2d.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40); + g2d.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); + } + + last_pt = pt; + repaint(); + } + + public static Graphics2D fillLabel(JLabel l, Color c, int px_size) { + BufferedImage img = new BufferedImage(px_size, px_size, + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = img.createGraphics(); + g.setColor(c); + g.fillRect(0, 0, px_size, px_size); + l.setIcon(new ImageIcon(img)); + return g; + } + + public AltosSiteMapTile(int px_size) { + setPreferredSize(new Dimension(px_size, px_size)); + + mapLabel = new JLabel(); + fillLabel(mapLabel, Color.GRAY, px_size); + mapLabel.setOpaque(true); + mapLabel.setBounds(0, 0, px_size, px_size); + add(mapLabel, new Integer(0)); + + draw = new JLabel(); + g2d = fillLabel(draw, new Color(127, 127, 127, 0), px_size); + draw.setBounds(0, 0, px_size, px_size); + draw.setOpaque(false); + + add(draw, new Integer(1)); + } } -- cgit v1.2.3 From ece2c86e2641b2cd613791293526c492b1606aa1 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 20 Nov 2010 16:19:42 -0800 Subject: altosui: Rewrite info table to mix with scroll pane well. Fix startup size Using a single table for the info table means that the scroll pane automatically picks up the table headers and shows them above the scrollable view. This patch also fixes the application size at startup so that no scrollbar is required in the info table, and the window is < 800x600. Signed-off-by: Keith Packard --- ao-tools/altosui/Altos.java | 2 + ao-tools/altosui/AltosAscent.java | 8 +-- ao-tools/altosui/AltosDescent.java | 8 +-- ao-tools/altosui/AltosFlightInfoTableModel.java | 77 +++++++++++++------------ ao-tools/altosui/AltosFlightUI.java | 24 +++----- ao-tools/altosui/AltosInfoTable.java | 70 +++++++--------------- ao-tools/altosui/AltosLanded.java | 2 +- ao-tools/altosui/AltosPad.java | 4 +- 8 files changed, 83 insertions(+), 112 deletions(-) diff --git a/ao-tools/altosui/Altos.java b/ao-tools/altosui/Altos.java index 197e98db..8ee94e04 100644 --- a/ao-tools/altosui/Altos.java +++ b/ao-tools/altosui/Altos.java @@ -73,6 +73,8 @@ public class Altos { static final Font value_font = new Font("Monospaced", Font.PLAIN, 22); static final Font status_font = new Font("SansSerif", Font.BOLD, 24); + static final int text_width = 16; + static void initialize_map() { string_to_state.put("startup", ao_flight_startup); diff --git a/ao-tools/altosui/AltosAscent.java b/ao-tools/altosui/AltosAscent.java index 2ceaa183..64bdcf30 100644 --- a/ao-tools/altosui/AltosAscent.java +++ b/ao-tools/altosui/AltosAscent.java @@ -65,7 +65,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(17); + value = new JTextField(Altos.text_width); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; @@ -102,7 +102,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(17); + value = new JTextField(Altos.text_width); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; @@ -151,7 +151,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(17); + value = new JTextField(Altos.text_width); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; @@ -161,7 +161,7 @@ public class AltosAscent extends JComponent implements AltosFlightDisplay { layout.setConstraints(value, c); add(value); - max_value = new JTextField(17); + max_value = new JTextField(Altos.text_width); max_value.setFont(Altos.value_font); max_value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 3; c.gridy = y; diff --git a/ao-tools/altosui/AltosDescent.java b/ao-tools/altosui/AltosDescent.java index abe64fdc..16ccd458 100644 --- a/ao-tools/altosui/AltosDescent.java +++ b/ao-tools/altosui/AltosDescent.java @@ -66,7 +66,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(17); + value = new JTextField(Altos.text_width); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 4; c.gridy = y; @@ -112,7 +112,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { c.weightx = 0; add(label, c); - value = new JTextField(17); + value = new JTextField(Altos.text_width); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = x + 2; c.gridy = y; @@ -159,7 +159,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value1 = new JTextField(17); + value1 = new JTextField(Altos.text_width); value1.setFont(Altos.value_font); value1.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = x + 2; c.gridy = y; @@ -169,7 +169,7 @@ public class AltosDescent extends JComponent implements AltosFlightDisplay { layout.setConstraints(value1, c); add(value1); - value2 = new JTextField(17); + value2 = new JTextField(Altos.text_width); value2.setFont(Altos.value_font); value2.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = x + 4; c.gridy = y; diff --git a/ao-tools/altosui/AltosFlightInfoTableModel.java b/ao-tools/altosui/AltosFlightInfoTableModel.java index 3355ff52..e23eff68 100644 --- a/ao-tools/altosui/AltosFlightInfoTableModel.java +++ b/ao-tools/altosui/AltosFlightInfoTableModel.java @@ -29,53 +29,56 @@ import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; public class AltosFlightInfoTableModel extends AbstractTableModel { - private String[] columnNames = {"Field", "Value"}; + final static private String[] columnNames = {"Field", "Value"}; - class InfoLine { - String name; - String value; + int rows; + int cols; + private String[][] data; - public InfoLine(String n, String v) { - name = n; - value = v; - } - } - - private ArrayList rows = new ArrayList(); - - public int getColumnCount() { return columnNames.length; } - public String getColumnName(int col) { return columnNames[col]; } - - public int getRowCount() { return 17; } - - int current_row = 0; - int prev_num_rows = 0; + public int getColumnCount() { return cols; } + public int getRowCount() { return rows; } + public String getColumnName(int col) { return columnNames[col & 1]; } public Object getValueAt(int row, int col) { - if (row >= rows.size()) + if (row >= rows || col >= cols) return ""; - if (col == 0) - return rows.get(row).name; - else - return rows.get(row).value; + return data[row][col]; } - public void resetRow() { - current_row = 0; + int[] current_row; + + public void reset() { + for (int i = 0; i < cols / 2; i++) + current_row[i] = 0; } - public void addRow(String name, String value) { - if (current_row >= rows.size()) - rows.add(current_row, new InfoLine(name, value)); - else - rows.set(current_row, new InfoLine(name, value)); - current_row++; + + public void clear() { + reset(); + for (int c = 0; c < cols; c++) + for (int r = 0; r < rows; r++) + data[r][c] = ""; + fireTableDataChanged(); + } + + public void addRow(int col, String name, String value) { + if (current_row[col] < rows) { + data[current_row[col]][col * 2] = name; + data[current_row[col]][col * 2 + 1] = value; + } + current_row[col]++; } + public void finish() { - if (current_row > prev_num_rows) - fireTableRowsInserted(prev_num_rows, current_row - 1); - while (rows.size() > current_row) - rows.remove(rows.size() - 1); - prev_num_rows = current_row; + for (int c = 0; c < cols / 2; c++) + while (current_row[c] < rows) + addRow(c, "", ""); fireTableDataChanged(); } + + public AltosFlightInfoTableModel (int in_rows, int in_cols) { + rows = in_rows; + cols = in_cols * 2; + data = new String[rows][cols]; + current_row = new int[in_cols]; + } } diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index ac88aa15..d5bcdb10 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -44,7 +44,6 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { AltosLanded landed; private AltosFlightStatus flightStatus; - private JScrollPane flightInfoPane; private AltosInfoTable flightInfo; static final int tab_pad = 1; @@ -66,14 +65,6 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { return tab_landed; } - public int width() { - return flightInfo.width(); - } - - public int height() { - return flightStatus.height() + flightInfo.height(); - } - void stop_display() { if (thread != null && thread.isAlive()) { thread.interrupt(); @@ -146,6 +137,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { setTitle(String.format("AltOS %s", reader.name)); + /* Stick channel selector at top of table for telemetry monitoring */ if (serial >= 0) { // Channel menu channels = new AltosChannelMenu(AltosPreferences.channel(serial)); @@ -162,6 +154,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { bag.add (channels, c); } + /* Flight status is always visible */ flightStatus = new AltosFlightStatus(); c.gridx = 0; c.gridy = 1; @@ -169,6 +162,9 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { c.weightx = 1; bag.add(flightStatus, c); + /* The rest of the window uses a tabbed pane to + * show one of the alternate data views + */ pane = new JTabbedPane(); pad = new AltosPad(); @@ -184,9 +180,9 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { pane.add("Landed", landed); flightInfo = new AltosInfoTable(); - flightInfoPane = new JScrollPane(flightInfo.box()); - pane.add("Table", flightInfoPane); + pane.add("Table", new JScrollPane(flightInfo)); + /* Make the tabbed pane use the rest of the window space */ c.gridx = 0; c.gridy = 2; c.fill = GridBagConstraints.BOTH; @@ -194,9 +190,6 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { c.weighty = 1; bag.add(pane, c); - this.setSize(this.getPreferredSize()); - this.validate(); - setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { @Override @@ -209,7 +202,8 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { } }); - this.setVisible(true); + pack(); + setVisible(true); thread = new AltosDisplayThread(this, voice, this, reader); diff --git a/ao-tools/altosui/AltosInfoTable.java b/ao-tools/altosui/AltosInfoTable.java index 28924410..c571d5c9 100644 --- a/ao-tools/altosui/AltosInfoTable.java +++ b/ao-tools/altosui/AltosInfoTable.java @@ -28,11 +28,8 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -public class AltosInfoTable { - private Box box; - private JTable table[]; - private AltosFlightInfoTableModel model[]; - private Box ibox[]; +public class AltosInfoTable extends JTable { + private AltosFlightInfoTableModel model; private Font infoLabelFont = new Font("SansSerif", Font.PLAIN, 12); private Font infoValueFont = new Font("Monospaced", Font.PLAIN, 12); @@ -40,58 +37,35 @@ public class AltosInfoTable { static final int info_columns = 3; static final int info_rows = 17; - public AltosInfoTable() { - box = Box.createHorizontalBox(); - model = new AltosFlightInfoTableModel[info_columns]; - table = new JTable[info_columns]; - ibox = new Box[info_columns]; - for (int i = 0; i < info_columns; i++) { - model[i] = new AltosFlightInfoTableModel(); - table[i] = new JTable(model[i]); - ibox[i] = box.createVerticalBox(); - - table[i].setFont(infoValueFont); - table[i].setRowHeight(rowHeight()); - table[i].setShowGrid(true); - ibox[i].add(table[i].getTableHeader()); - ibox[i].add(table[i]); - box.add(ibox[i]); - } - } - - public int rowHeight() { - FontMetrics infoValueMetrics = table[0].getFontMetrics(infoValueFont); - return (infoValueMetrics.getHeight() + infoValueMetrics.getLeading()) * 20 / 10; - } - - public int columnWidth() { - FontMetrics infoValueMetrics = table[0].getFontMetrics(infoValueFont); - return infoValueMetrics.charWidth('0') * 20 * 2; + int desired_row_height() { + FontMetrics infoValueMetrics = getFontMetrics(infoValueFont); + return (infoValueMetrics.getHeight() + infoValueMetrics.getLeading()) * 18 / 10; } - public int height() { - return rowHeight() * info_rows; - } - - public int width() { - return columnWidth() * info_columns; + public AltosInfoTable() { + super(new AltosFlightInfoTableModel(info_rows, info_columns)); + model = (AltosFlightInfoTableModel) getModel(); + setFont(infoValueFont); + setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS); + setShowGrid(true); + setRowHeight(desired_row_height()); + doLayout(); } - public Box box() { - return box; + public Dimension getPreferredScrollableViewportSize() { + return getPreferredSize(); } void info_reset() { - for (int i = 0; i < info_columns; i++) - model[i].resetRow(); + model.reset(); } void info_add_row(int col, String name, String value) { - model[col].addRow(name, value); + model.addRow(col, name, value); } void info_add_row(int col, String name, String format, Object... parameters) { - model[col].addRow(name, String.format(format, parameters)); + info_add_row (col, name, String.format(format, parameters)); } void info_add_deg(int col, String name, double v, int pos, int neg) { @@ -103,17 +77,15 @@ public class AltosInfoTable { double deg = Math.floor(v); double min = (v - deg) * 60; - model[col].addRow(name, String.format("%3.0f°%08.5f'", deg, min)); + info_add_row(col, name, String.format("%3.0f°%08.5f'", deg, min)); } void info_finish() { - for (int i = 0; i < info_columns; i++) - model[i].finish(); + model.finish(); } public void clear() { - info_reset(); - info_finish(); + model.clear(); } public void show(AltosState state, int crc_errors) { diff --git a/ao-tools/altosui/AltosLanded.java b/ao-tools/altosui/AltosLanded.java index 059dbb6d..d34efe6d 100644 --- a/ao-tools/altosui/AltosLanded.java +++ b/ao-tools/altosui/AltosLanded.java @@ -61,7 +61,7 @@ public class AltosLanded extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(17); + value = new JTextField(Altos.text_width); value.setFont(value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 1; c.gridy = y; diff --git a/ao-tools/altosui/AltosPad.java b/ao-tools/altosui/AltosPad.java index 480e4d79..66954347 100644 --- a/ao-tools/altosui/AltosPad.java +++ b/ao-tools/altosui/AltosPad.java @@ -65,7 +65,7 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(17); + value = new JTextField(Altos.text_width); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; @@ -101,7 +101,7 @@ public class AltosPad extends JComponent implements AltosFlightDisplay { layout.setConstraints(label, c); add(label); - value = new JTextField(17); + value = new JTextField(Altos.text_width); value.setFont(Altos.value_font); value.setHorizontalAlignment(SwingConstants.RIGHT); c.gridx = 2; c.gridy = y; -- cgit v1.2.3 From e5b1adae9b23b98a6321986f5cd67c9d3166b87f Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 10:34:39 +1000 Subject: AltosSiteMap: better gps check, lower zoom --- ao-tools/altosui/AltosSiteMap.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 5f5e30f0..72a65b15 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -34,7 +34,7 @@ import java.awt.geom.Line2D; public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { // max vertical step in a tile in naut. miles - static final double tile_size_nmi = 1.0; + static final double tile_size_nmi = 2.0; static final int px_size = 512; @@ -211,10 +211,10 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { boolean initialised = false; public void show(AltosState state, int crc_errors) { // if insufficient gps data, nothing to update - if (!state.gps_ready) { + if (!state.gps.locked) { if (state.pad_lat == 0 && state.pad_lon == 0) return; - if (state.ngps < 3) + if (state.gps.nsat < 4) return; } -- cgit v1.2.3 From 440a0f3f5130eb0c8e614691892be8c94e7fd3c3 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 20 Nov 2010 16:55:12 -0800 Subject: altosui: Set site map flight path lines to 6 pixels anti-aliased. Much more visible over the map. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosSiteMapTile.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index 9d6f855d..fd4cf0bb 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -125,6 +125,9 @@ public class AltosSiteMapTile extends JLayeredPane { draw = new JLabel(); g2d = fillLabel(draw, new Color(127, 127, 127, 0), px_size); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); draw.setBounds(0, 0, px_size, px_size); draw.setOpaque(false); -- cgit v1.2.3 From 878913551a1e4e3c8f2b39fa4aeb234880735a1c Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 10:55:22 +1000 Subject: AltosSiteMap: explain tile size better --- ao-tools/altosui/AltosSiteMap.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 72a65b15..e222e2c8 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -33,8 +33,10 @@ import java.awt.geom.Point2D; import java.awt.geom.Line2D; public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { - // max vertical step in a tile in naut. miles - static final double tile_size_nmi = 2.0; + // preferred vertical step in a tile in naut. miles + // will actually choose a step size between x and 2x, where this + // is 1.5x + static final double tile_size_nmi = 1.5; static final int px_size = 512; @@ -117,7 +119,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { scale_x = 256/360.0 * Math.pow(2, zoom); scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); locn = pt(lat, lng); - north_step = pt(lat+tile_size_nmi/60.0, lng); + north_step = pt(lat+tile_size_nmi*4/3/60.0, lng); if (locn.y - north_step.y > px_size) break; } while (zoom < 22); -- cgit v1.2.3 From c3994dd82d489289ebc99ff9c5fa88f560c023ac Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 13:07:11 +1000 Subject: AltosSiteMap: extend map if rocket goes far away --- ao-tools/altosui/AltosSiteMap.java | 130 +++++++++++++++++++++++---------- ao-tools/altosui/AltosSiteMapTile.java | 22 ++---- 2 files changed, 98 insertions(+), 54 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 2c542061..25450e7e 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -36,7 +36,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { // preferred vertical step in a tile in naut. miles // will actually choose a step size between x and 2x, where this // is 1.5x - static final double tile_size_nmi = 1.5; + static final double tile_size_nmi = 0.75; static final int px_size = 512; @@ -106,8 +106,23 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { return latlng(pt, scale_x, scale_y); } - AltosSiteMapTile [] mapTiles = new AltosSiteMapTile[9]; - Point2D.Double [] tileOffset = new Point2D.Double[9]; + Vector mapTiles = new Vector(); + Point2D.Double centre; + + private Point tileOffset(AltosSiteMapTile tile) { + GridBagConstraints c = layout.getConstraints(tile); + return new Point(c.gridx - 100, c.gridy - 100); + } + private Point2D.Double tileCoordOffset(AltosSiteMapTile tile) { + Point p = tileOffset(tile); + return new Point2D.Double(centre.x - p.x*px_size, + centre.y - p.y * px_size); + } + + private Point tileOffset(Point2D.Double p) { + return new Point((int)Math.floor((centre.x+p.x)/px_size), + (int)Math.floor((centre.y+p.y)/px_size)); + } private Point2D.Double getBaseLocation(double lat, double lng) { Point2D.Double locn, north_step; @@ -132,15 +147,16 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { // nothing } - private void bgLoadMap(final int i, + private void bgLoadMap(final AltosSiteMapTile tile, final File pngfile, final String pngurl) { + //System.out.printf("Loading/fetching map %s\n", pngfile); Thread thread = new Thread() { public void run() { ImageIcon res; res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl); if (res != null) { - mapTiles[i].loadMap(res); + tile.loadMap(res); } else { System.out.printf("# Failed to fetch file %s\n", pngfile); System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); @@ -154,15 +170,16 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { AltosPreferences.init(null); AltosSiteMap asm = new AltosSiteMap(true); - Point2D.Double c = asm.getBaseLocation(lat, lng); + asm.centre = asm.getBaseLocation(lat, lng); + Point2D.Double p = new Point2D.Double(); Point2D.Double p2; int dx = -w/2, dy = -h/2; for (int y = dy; y < h+dy; y++) { for (int x = dx; x < w+dx; x++) { LatLng map_latlng = asm.latlng( - -c.x + x*px_size + px_size/2, - -c.y + y*px_size + px_size/2); + -asm.centre.x + x*px_size + px_size/2, + -asm.centre.y + y*px_size + px_size/2); File pngfile = asm.MapFile(map_latlng.lat, map_latlng.lng); String pngurl = asm.MapURL(map_latlng.lat, map_latlng.lng); if (pngfile.exists()) { @@ -177,22 +194,21 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } } - private void initMaps(double lat, double lng) { - Point2D.Double c = getBaseLocation(lat, lng); - Point2D.Double p = new Point2D.Double(); + private void initMap(AltosSiteMapTile tile) { + Point2D.Double offset = tileCoordOffset(tile); - for (int i = 0; i < 9; i++) { - int x = i%3 - 1, y = i/3 - 1; + LatLng map_latlng = latlng(px_size/2-offset.x, px_size/2-offset.y); - tileOffset[i] = new Point2D.Double( - c.x - x*px_size, p.y = c.y - y*px_size); - LatLng map_latlng = latlng( - -tileOffset[i].x+px_size/2, - -tileOffset[i].y+px_size/2); + File pngfile = MapFile(map_latlng.lat, map_latlng.lng); + String pngurl = MapURL(map_latlng.lat, map_latlng.lng); + bgLoadMap(tile, pngfile, pngurl); + } + + private void initMaps(double lat, double lng) { + centre = getBaseLocation(lat, lng); - File pngfile = MapFile(map_latlng.lat, map_latlng.lng); - String pngurl = MapURL(map_latlng.lat, map_latlng.lng); - bgLoadMap(i, pngfile, pngurl); + for (AltosSiteMapTile tile : mapTiles) { + initMap(tile); } } @@ -211,6 +227,8 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } boolean initialised = false; + Point2D.Double last_pt = null; + int last_state = -1; public void show(AltosState state, int crc_errors) { // if insufficient gps data, nothing to update if (state.gps == null) @@ -228,10 +246,51 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } Point2D.Double pt = pt(state.gps.lat, state.gps.lon); - for (int x = 0; x < mapTiles.length; x++) { - mapTiles[x].show(state, crc_errors, - translatePoint(pt, tileOffset[x])); + if (last_pt == pt && last_state == state.state) + return; + + if (last_pt == null) { + last_pt = pt; + } + boolean in_any = false; + for (AltosSiteMapTile tile : mapTiles) { + Point2D.Double ref, lref; + ref = translatePoint(pt, tileCoordOffset(tile)); + lref = translatePoint(last_pt, tileCoordOffset(tile)); + tile.show(state, crc_errors, lref, ref); + if (0 <= ref.x && ref.x < px_size) + if (0 <= ref.y && ref.y < px_size) + in_any = true; } + if (!in_any) { + AltosSiteMapTile tile = addTileAt(tileOffset(pt)); + Point2D.Double ref, lref; + ref = translatePoint(pt, tileCoordOffset(tile)); + lref = translatePoint(last_pt, tileCoordOffset(tile)); + initMap(tile); + setViewportView(comp); + tile.show(state, crc_errors, lref, ref); + } + last_pt = pt; + last_state = state.state; + } + + private AltosSiteMapTile addTileAt(Point offset) { + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + + // put some space between the map tiles, debugging only + // c.insets = new Insets(5, 5, 5, 5); + // + AltosSiteMapTile t = new AltosSiteMapTile(px_size); + mapTiles.add(t); + c.gridx = offset.x + 100; + c.gridy = offset.y + 100; + layout.setConstraints(t, c); + comp.add(t); + + return t; } private AltosSiteMap(boolean knowWhatYouAreDoing) { @@ -240,8 +299,11 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } } + JComponent comp; + private GridBagLayout layout; + public AltosSiteMap() { - JComponent comp = new JComponent() { + comp = new JComponent() { GrabNDrag scroller = new GrabNDrag(this); { addMouseMotionListener(scroller); @@ -250,21 +312,13 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } }; - GridBagLayout layout = new GridBagLayout(); + layout = new GridBagLayout(); comp.setLayout(layout); - GridBagConstraints c = new GridBagConstraints(); - c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.BOTH; - - // put some space between the map tiles, debugging only - // c.insets = new Insets(5, 5, 5, 5); - for (int x = 0; x < 9; x++) { - c.gridx = x % 3; - c.gridy = x / 3; - mapTiles[x] = new AltosSiteMapTile(px_size); - layout.setConstraints(mapTiles[x], c); - comp.add(mapTiles[x]); + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + addTileAt(new Point(x, y)); + } } setViewportView(comp); setPreferredSize(new Dimension(500,200)); diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index fd4cf0bb..ea8c8bd9 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -32,9 +32,6 @@ import java.awt.geom.Point2D; import java.awt.geom.Line2D; public class AltosSiteMapTile extends JLayeredPane { - Point2D.Double coord_pt; - Point2D.Double last_pt; - JLabel mapLabel; JLabel draw; Graphics2D g2d; @@ -57,19 +54,13 @@ public class AltosSiteMapTile extends JLayeredPane { boolean drawn_landed_circle = false; boolean drawn_boost_circle = false; - public void show(AltosState state, int crc_errors, Point2D.Double pt) { - if (last_pt == null) { - // setLocation(state.pad_lat, state.pad_lon); - // loadMap(); - last_pt = pt; - } - - if (pt != last_pt) { - if (0 <= state.state && state.state < stateColors.length) { - g2d.setColor(stateColors[state.state]); - } - g2d.draw(new Line2D.Double(last_pt, pt)); + public void show(AltosState state, int crc_errors, + Point2D.Double last_pt, Point2D.Double pt) + { + if (0 <= state.state && state.state < stateColors.length) { + g2d.setColor(stateColors[state.state]); } + g2d.draw(new Line2D.Double(last_pt, pt)); int px_size = getWidth(); if (0 <= pt.x && pt.x < px_size) { @@ -100,7 +91,6 @@ public class AltosSiteMapTile extends JLayeredPane { g2d.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); } - last_pt = pt; repaint(); } -- cgit v1.2.3 From c040bcd06679484175542208fb564d0271a7fc1b Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 13:19:36 +1000 Subject: AltosSiteMap: try to get new tile construction right --- ao-tools/altosui/AltosSiteMap.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 25450e7e..dd99ad48 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -264,12 +264,14 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } if (!in_any) { AltosSiteMapTile tile = addTileAt(tileOffset(pt)); + setViewportView(comp); + Point2D.Double ref, lref; ref = translatePoint(pt, tileCoordOffset(tile)); lref = translatePoint(last_pt, tileCoordOffset(tile)); - initMap(tile); - setViewportView(comp); tile.show(state, crc_errors, lref, ref); + + initMap(tile); } last_pt = pt; last_state = state.state; -- cgit v1.2.3 From 71e487344395a8efc9cd279aad92f601ff4c6d3d Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 14:05:00 +1000 Subject: AltosSiteMap: thread safe tile addition --- ao-tools/altosui/AltosSiteMap.java | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index dd99ad48..df5207bf 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -229,7 +229,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { boolean initialised = false; Point2D.Double last_pt = null; int last_state = -1; - public void show(AltosState state, int crc_errors) { + public void show(final AltosState state, final int crc_errors) { // if insufficient gps data, nothing to update if (state.gps == null) return; @@ -245,7 +245,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { initialised = true; } - Point2D.Double pt = pt(state.gps.lat, state.gps.lon); + final Point2D.Double pt = pt(state.gps.lat, state.gps.lon); if (last_pt == pt && last_state == state.state) return; @@ -263,15 +263,23 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { in_any = true; } if (!in_any) { - AltosSiteMapTile tile = addTileAt(tileOffset(pt)); - setViewportView(comp); - - Point2D.Double ref, lref; - ref = translatePoint(pt, tileCoordOffset(tile)); - lref = translatePoint(last_pt, tileCoordOffset(tile)); - tile.show(state, crc_errors, lref, ref); - - initMap(tile); + try { + SwingUtilities.invokeAndWait( new Runnable() { + public void run() { + AltosSiteMapTile tile = addTileAt(tileOffset(pt)); + setViewportView(comp); + + Point2D.Double ref, lref; + ref = translatePoint(pt, tileCoordOffset(tile)); + lref = translatePoint(last_pt, tileCoordOffset(tile)); + tile.show(state, crc_errors, lref, ref); + + initMap(tile); + } + } ); + } catch (Exception e) { + // pray + } } last_pt = pt; last_state = state.state; -- cgit v1.2.3 From 84e570d8a8a52e0d358582135ec1b3a12be94c26 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 14:45:10 +1000 Subject: AltosSiteMap: refactor tile collection --- ao-tools/altosui/AltosSiteMap.java | 75 ++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index df5207bf..5e34dd49 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -106,15 +106,10 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { return latlng(pt, scale_x, scale_y); } - Vector mapTiles = new Vector(); + HashMap mapTiles = new HashMap(); Point2D.Double centre; - private Point tileOffset(AltosSiteMapTile tile) { - GridBagConstraints c = layout.getConstraints(tile); - return new Point(c.gridx - 100, c.gridy - 100); - } - private Point2D.Double tileCoordOffset(AltosSiteMapTile tile) { - Point p = tileOffset(tile); + private Point2D.Double tileCoordOffset(Point p) { return new Point2D.Double(centre.x - p.x*px_size, centre.y - p.y * px_size); } @@ -194,10 +189,10 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } } - private void initMap(AltosSiteMapTile tile) { - Point2D.Double offset = tileCoordOffset(tile); + private void initMap(AltosSiteMapTile tile, Point offset) { + Point2D.Double coord = tileCoordOffset(offset); - LatLng map_latlng = latlng(px_size/2-offset.x, px_size/2-offset.y); + LatLng map_latlng = latlng(px_size/2-coord.x, px_size/2-coord.y); File pngfile = MapFile(map_latlng.lat, map_latlng.lng); String pngurl = MapURL(map_latlng.lat, map_latlng.lng); @@ -207,8 +202,8 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { private void initMaps(double lat, double lng) { centre = getBaseLocation(lat, lng); - for (AltosSiteMapTile tile : mapTiles) { - initMap(tile); + for (Point k : mapTiles.keySet()) { + initMap(mapTiles.get(k), k); } } @@ -253,39 +248,40 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { last_pt = pt; } boolean in_any = false; - for (AltosSiteMapTile tile : mapTiles) { + for (Point offset : mapTiles.keySet()) { + AltosSiteMapTile tile = mapTiles.get(offset); Point2D.Double ref, lref; - ref = translatePoint(pt, tileCoordOffset(tile)); - lref = translatePoint(last_pt, tileCoordOffset(tile)); + ref = translatePoint(pt, tileCoordOffset(offset)); + lref = translatePoint(last_pt, tileCoordOffset(offset)); tile.show(state, crc_errors, lref, ref); if (0 <= ref.x && ref.x < px_size) if (0 <= ref.y && ref.y < px_size) in_any = true; } if (!in_any) { - try { - SwingUtilities.invokeAndWait( new Runnable() { - public void run() { - AltosSiteMapTile tile = addTileAt(tileOffset(pt)); - setViewportView(comp); - - Point2D.Double ref, lref; - ref = translatePoint(pt, tileCoordOffset(tile)); - lref = translatePoint(last_pt, tileCoordOffset(tile)); - tile.show(state, crc_errors, lref, ref); - - initMap(tile); - } - } ); - } catch (Exception e) { - // pray - } + final AltosSiteMapTile tile = new AltosSiteMapTile(px_size); + final Point offset = tileOffset(pt); + mapTiles.put(offset, tile); + + Point2D.Double ref, lref; + ref = translatePoint(pt, tileCoordOffset(offset)); + lref = translatePoint(last_pt, tileCoordOffset(offset)); + tile.show(state, crc_errors, lref, ref); + + initMap(tile, offset); + + SwingUtilities.invokeLater( new Runnable() { + public void run() { + addTileAt(tile, offset); + setViewportView(comp); + } + } ); } last_pt = pt; last_state = state.state; } - private AltosSiteMapTile addTileAt(Point offset) { + private void addTileAt(AltosSiteMapTile tile, Point offset) { GridBagConstraints c = new GridBagConstraints(); c.anchor = GridBagConstraints.CENTER; c.fill = GridBagConstraints.BOTH; @@ -293,14 +289,10 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { // put some space between the map tiles, debugging only // c.insets = new Insets(5, 5, 5, 5); // - AltosSiteMapTile t = new AltosSiteMapTile(px_size); - mapTiles.add(t); c.gridx = offset.x + 100; c.gridy = offset.y + 100; - layout.setConstraints(t, c); - comp.add(t); - - return t; + layout.setConstraints(tile, c); + comp.add(tile); } private AltosSiteMap(boolean knowWhatYouAreDoing) { @@ -327,7 +319,10 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { for (int x = -1; x <= 1; x++) { for (int y = -1; y <= 1; y++) { - addTileAt(new Point(x, y)); + AltosSiteMapTile t = new AltosSiteMapTile(px_size); + Point offset = new Point(x, y); + mapTiles.put(offset, t); + addTileAt(t, offset); } } setViewportView(comp); -- cgit v1.2.3 From a08b2a6363c194195db92029743f6612676373ce Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 15:03:21 +1000 Subject: AltosSiteMap: never accept 0,0 as lat/long --- ao-tools/altosui/AltosSiteMap.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 5e34dd49..0375128e 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -228,9 +228,9 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { // if insufficient gps data, nothing to update if (state.gps == null) return; + if (state.pad_lat == 0 && state.pad_lon == 0) + return; if (!state.gps.locked) { - if (state.pad_lat == 0 && state.pad_lon == 0) - return; if (state.gps.nsat < 4) return; } -- cgit v1.2.3 From 0393830f85da5efc96bbdf0d9769b66019c34b33 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 15:13:35 +1000 Subject: AltosSiteMap: limit nr of tiles to 200x200 --- ao-tools/altosui/AltosSiteMap.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 0375128e..b2d79043 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -40,6 +40,8 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { static final int px_size = 512; + static final int MAX_TILE_DELTA = 100; + private static Point2D.Double translatePoint(Point2D.Double p, Point2D.Double d) { @@ -282,15 +284,22 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } private void addTileAt(AltosSiteMapTile tile, Point offset) { + if (Math.abs(offset.x) >= MAX_TILE_DELTA || + Math.abs(offset.y) >= MAX_TILE_DELTA) + { + System.out.printf("Rocket too far away from pad (tile %d,%d)\n", + offset.x, offset.y); + return; + } + GridBagConstraints c = new GridBagConstraints(); c.anchor = GridBagConstraints.CENTER; c.fill = GridBagConstraints.BOTH; - // put some space between the map tiles, debugging only // c.insets = new Insets(5, 5, 5, 5); - // - c.gridx = offset.x + 100; - c.gridy = offset.y + 100; + + c.gridx = offset.x + MAX_TILE_DELTA; + c.gridy = offset.y + MAX_TILE_DELTA; layout.setConstraints(tile, c); comp.add(tile); } -- cgit v1.2.3 From e7954c820763f80e993f9f822e837725cf36af84 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 20 Nov 2010 22:03:26 -0800 Subject: altosui: When fixing eeprom gps time information, make GPS data valid Eeprom files may be missing the GPS time (due to a firmware bug). Working around this involves finding the next valid GPS time and using that to create a fake GPS time entry. However, that next GPS time may not be locked or may have few sats as it is from the boost stage of the flight. Fix this by simply forcing the fake time packet to have 4 sats and be locked. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosEepromIterable.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ao-tools/altosui/AltosEepromIterable.java b/ao-tools/altosui/AltosEepromIterable.java index fc683321..f8e6d7e5 100644 --- a/ao-tools/altosui/AltosEepromIterable.java +++ b/ao-tools/altosui/AltosEepromIterable.java @@ -309,6 +309,12 @@ public class AltosEepromIterable extends AltosRecordIterable { int flags = (good.b >> 8); int seconds = hour * 3600 + minute * 60 + second; + /* Make sure this looks like a good GPS value */ + if ((flags & Altos.AO_GPS_NUM_SAT_MASK) >> Altos.AO_GPS_NUM_SAT_SHIFT < 4) + flags = (flags & ~Altos.AO_GPS_NUM_SAT_MASK) | (4 << Altos.AO_GPS_NUM_SAT_SHIFT); + flags |= Altos.AO_GPS_RUNNING; + flags |= Altos.AO_GPS_VALID; + int new_seconds = seconds + diff; if (new_seconds < 0) new_seconds += 24 * 3600; -- cgit v1.2.3 From 4a9ded5b39ed08e13abc2cddba8b712f62b983f2 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 17:39:50 +1000 Subject: AltosSiteMap: ensure buffer around active tile --- ao-tools/altosui/AltosSiteMap.java | 53 ++++++++++++++++++++++++++-------- ao-tools/altosui/AltosSiteMapTile.java | 17 +++++++---- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index b2d79043..802eb68c 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -260,29 +260,57 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { if (0 <= ref.y && ref.y < px_size) in_any = true; } - if (!in_any) { - final AltosSiteMapTile tile = new AltosSiteMapTile(px_size); - final Point offset = tileOffset(pt); - mapTiles.put(offset, tile); + Point offset = tileOffset(pt); + if (!in_any) { Point2D.Double ref, lref; ref = translatePoint(pt, tileCoordOffset(offset)); lref = translatePoint(last_pt, tileCoordOffset(offset)); - tile.show(state, crc_errors, lref, ref); + AltosSiteMapTile tile = createTile(offset); + tile.show(state, crc_errors, lref, ref); initMap(tile, offset); + finishTileLater(tile, offset); + } - SwingUtilities.invokeLater( new Runnable() { - public void run() { - addTileAt(tile, offset); - setViewportView(comp); - } - } ); + if (offset != tileOffset(last_pt)) { + ensureTilesAround(offset); } + last_pt = pt; last_state = state.state; } + private AltosSiteMapTile createTile(final Point offset) { + final AltosSiteMapTile tile = new AltosSiteMapTile(px_size); + mapTiles.put(offset, tile); + return tile; + } + private void finishTileLater(final AltosSiteMapTile tile, + final Point offset) + { + SwingUtilities.invokeLater( new Runnable() { + public void run() { + addTileAt(tile, offset); + tile.setScrollable(); + } + } ); + } + + private void ensureTilesAround(Point base_offset) { + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + Point offset = new Point(base_offset.x + x, base_offset.y + y); + if (mapTiles.containsKey(offset)) + continue; + AltosSiteMapTile tile = createTile(offset); + initMap(tile, offset); + finishTileLater(tile, offset); + } + } + } + + private void addTileAt(AltosSiteMapTile tile, Point offset) { if (Math.abs(offset.x) >= MAX_TILE_DELTA || Math.abs(offset.y) >= MAX_TILE_DELTA) @@ -328,10 +356,11 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { for (int x = -1; x <= 1; x++) { for (int y = -1; y <= 1; y++) { - AltosSiteMapTile t = new AltosSiteMapTile(px_size); Point offset = new Point(x, y); + AltosSiteMapTile t = new AltosSiteMapTile(px_size); mapTiles.put(offset, t); addTileAt(t, offset); + t.setScrollable(); } } setViewportView(comp); diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index ea8c8bd9..e0942986 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -52,10 +52,17 @@ public class AltosSiteMapTile extends JLayeredPane { Color.BLACK // landed }; - boolean drawn_landed_circle = false; - boolean drawn_boost_circle = false; - public void show(AltosState state, int crc_errors, - Point2D.Double last_pt, Point2D.Double pt) + private boolean drawn_landed_circle = false; + private boolean drawn_boost_circle = false; + private boolean scrollable = false; + public synchronized void setScrollable() { + scrollable = true; + } + public synchronized boolean isScrollable() { + return scrollable; + } + public synchronized void show(AltosState state, int crc_errors, + Point2D.Double last_pt, Point2D.Double pt) { if (0 <= state.state && state.state < stateColors.length) { g2d.setColor(stateColors[state.state]); @@ -63,7 +70,7 @@ public class AltosSiteMapTile extends JLayeredPane { g2d.draw(new Line2D.Double(last_pt, pt)); int px_size = getWidth(); - if (0 <= pt.x && pt.x < px_size) { + if (isScrollable() && 0 <= pt.x && pt.x < px_size) { if (0 <= pt.y && pt.y < px_size) { int dx = 500, dy = 250; if (state.state > 2) { -- cgit v1.2.3 From b85df38b5611e45cb9296df07b720badf74ac26e Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Mon, 22 Nov 2010 05:22:17 +1000 Subject: altosui: improve sitemap scrolling behaviour --- ao-tools/altosui/AltosSiteMap.java | 53 +++++++++++++++++++++++----------- ao-tools/altosui/AltosSiteMapTile.java | 21 -------------- ao-tools/altosui/GrabNDrag.java | 35 ++++++++++++---------- 3 files changed, 55 insertions(+), 54 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 802eb68c..2477e4f8 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -273,6 +273,8 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { finishTileLater(tile, offset); } + scrollRocketToVisible(pt); + if (offset != tileOffset(last_pt)) { ensureTilesAround(offset); } @@ -281,8 +283,8 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { last_state = state.state; } - private AltosSiteMapTile createTile(final Point offset) { - final AltosSiteMapTile tile = new AltosSiteMapTile(px_size); + private AltosSiteMapTile createTile(Point offset) { + AltosSiteMapTile tile = new AltosSiteMapTile(px_size); mapTiles.put(offset, tile); return tile; } @@ -292,7 +294,6 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { SwingUtilities.invokeLater( new Runnable() { public void run() { addTileAt(tile, offset); - tile.setScrollable(); } } ); } @@ -310,6 +311,18 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } } + private Point topleft = new Point(0,0); + private void scrollRocketToVisible(Point2D.Double pt) { + Rectangle r = comp.getVisibleRect(); + Point2D.Double copt = translatePoint(pt, tileCoordOffset(topleft)); + int dx = (int)copt.x - r.width/2 - r.x; + int dy = (int)copt.y - r.height/2 - r.y; + if (Math.abs(dx) > r.width/3 || Math.abs(dy) > r.height/3) { + r.x += dx; + r.y += dy; + comp.scrollRectToVisible(r); + } + } private void addTileAt(AltosSiteMapTile tile, Point offset) { if (Math.abs(offset.x) >= MAX_TILE_DELTA || @@ -320,6 +333,18 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { return; } + boolean review = false; + Rectangle r = comp.getVisibleRect(); + if (offset.x < topleft.x) { + r.x += (topleft.x - offset.x) * px_size; + topleft.x = offset.x; + review = true; + } + if (offset.y < topleft.y) { + r.y += (topleft.y - offset.y) * px_size; + topleft.y = offset.y; + review = true; + } GridBagConstraints c = new GridBagConstraints(); c.anchor = GridBagConstraints.CENTER; c.fill = GridBagConstraints.BOTH; @@ -329,7 +354,11 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { c.gridx = offset.x + MAX_TILE_DELTA; c.gridy = offset.y + MAX_TILE_DELTA; layout.setConstraints(tile, c); + comp.add(tile); + if (review) { + comp.scrollRectToVisible(r); + } } private AltosSiteMap(boolean knowWhatYouAreDoing) { @@ -338,29 +367,19 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } } - JComponent comp; - private GridBagLayout layout; + JComponent comp = new JComponent() { }; + private GridBagLayout layout = new GridBagLayout(); public AltosSiteMap() { - comp = new JComponent() { - GrabNDrag scroller = new GrabNDrag(this); - { - addMouseMotionListener(scroller); - addMouseListener(scroller); - setAutoscrolls(true); - } - }; + GrabNDrag scroller = new GrabNDrag(comp); - layout = new GridBagLayout(); comp.setLayout(layout); for (int x = -1; x <= 1; x++) { for (int y = -1; y <= 1; y++) { Point offset = new Point(x, y); - AltosSiteMapTile t = new AltosSiteMapTile(px_size); - mapTiles.put(offset, t); + AltosSiteMapTile t = createTile(offset); addTileAt(t, offset); - t.setScrollable(); } } setViewportView(comp); diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index e0942986..8301f42b 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -54,13 +54,6 @@ public class AltosSiteMapTile extends JLayeredPane { private boolean drawn_landed_circle = false; private boolean drawn_boost_circle = false; - private boolean scrollable = false; - public synchronized void setScrollable() { - scrollable = true; - } - public synchronized boolean isScrollable() { - return scrollable; - } public synchronized void show(AltosState state, int crc_errors, Point2D.Double last_pt, Point2D.Double pt) { @@ -69,20 +62,6 @@ public class AltosSiteMapTile extends JLayeredPane { } g2d.draw(new Line2D.Double(last_pt, pt)); - int px_size = getWidth(); - if (isScrollable() && 0 <= pt.x && pt.x < px_size) { - if (0 <= pt.y && pt.y < px_size) { - int dx = 500, dy = 250; - if (state.state > 2) { - dx = Math.min(200, 20 + (int) Math.abs(last_pt.x - pt.x)); - dy = Math.min(100, 10 + (int) Math.abs(last_pt.y - pt.y)); - } - Rectangle r = new Rectangle((int)pt.x-dx, (int)pt.y-dy, - dx*2, dy*2); - scrollRectToVisible(r); - } - } - if (state.state == 3 && !drawn_boost_circle) { drawn_boost_circle = true; g2d.setColor(Color.RED); diff --git a/ao-tools/altosui/GrabNDrag.java b/ao-tools/altosui/GrabNDrag.java index b44f3fe2..e6b87b58 100644 --- a/ao-tools/altosui/GrabNDrag.java +++ b/ao-tools/altosui/GrabNDrag.java @@ -29,23 +29,26 @@ import java.util.*; import java.text.*; class GrabNDrag extends MouseInputAdapter { - private JComponent scroll; - private Point startPt = new Point(); + private JComponent scroll; + private Point startPt = new Point(); - public GrabNDrag(JComponent parent) { - scroll = parent; - } + public GrabNDrag(JComponent scroll) { + this.scroll = scroll; + scroll.addMouseMotionListener(this); + scroll.addMouseListener(this); + scroll.setAutoscrolls(true); + } - public void mousePressed(MouseEvent e) { - startPt.setLocation(e.getPoint()); - } - public void mouseDragged(MouseEvent e) { - int xd = e.getX() - startPt.x; - int yd = e.getY() - startPt.y; + public void mousePressed(MouseEvent e) { + startPt.setLocation(e.getPoint()); + } + public void mouseDragged(MouseEvent e) { + int xd = e.getX() - startPt.x; + int yd = e.getY() - startPt.y; - Rectangle r = scroll.getVisibleRect(); - r.x -= xd; - r.y -= yd; - scroll.scrollRectToVisible(r); - } + Rectangle r = scroll.getVisibleRect(); + r.x -= xd; + r.y -= yd; + scroll.scrollRectToVisible(r); + } } -- cgit v1.2.3 From 902735ffbfdd97672d52b09f17cdcd619193fd05 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Mon, 22 Nov 2010 05:29:26 +1000 Subject: altosui: keep sitemap more centred on rocket --- ao-tools/altosui/AltosSiteMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 2477e4f8..80970605 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -317,7 +317,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { Point2D.Double copt = translatePoint(pt, tileCoordOffset(topleft)); int dx = (int)copt.x - r.width/2 - r.x; int dy = (int)copt.y - r.height/2 - r.y; - if (Math.abs(dx) > r.width/3 || Math.abs(dy) > r.height/3) { + if (Math.abs(dx) > r.width/4 || Math.abs(dy) > r.height/4) { r.x += dx; r.y += dy; comp.scrollRectToVisible(r); -- cgit v1.2.3 From 377ee7e90ecd028f984cd1abce96b2efc3b5b977 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 21 Nov 2010 14:03:17 -0800 Subject: altos: Add on/off modes to 'C' command This lets the user turn the radio on/off and then invoke other commands. Signed-off-by: Keith Packard --- src/ao_radio.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/ao_radio.c b/src/ao_radio.c index f4a9d3b2..3fb4afd7 100644 --- a/src/ao_radio.c +++ b/src/ao_radio.c @@ -448,19 +448,32 @@ ao_radio_rdf_abort(void) void ao_radio_test(void) { - ao_set_monitor(0); - ao_packet_slave_stop(); - ao_radio_get(); - printf ("Hit a character to stop..."); flush(); - RFST = RFST_STX; - getchar(); - ao_radio_idle(); - ao_radio_put(); - putchar('\n'); + uint8_t mode = 2; + ao_cmd_white(); + if (ao_cmd_lex_c != '\n') { + ao_cmd_decimal(); + mode = (uint8_t) ao_cmd_lex_u32; + } + mode++; + if (mode & 2) { + ao_set_monitor(0); + ao_packet_slave_stop(); + ao_radio_get(); + RFST = RFST_STX; + } + if (mode == 3) { + printf ("Hit a character to stop..."); flush(); + getchar(); + putchar('\n'); + } + if (mode & 1) { + ao_radio_idle(); + ao_radio_put(); + } } __code struct ao_cmds ao_radio_cmds[] = { - { 'C', ao_radio_test, "C Radio carrier test" }, + { 'C', ao_radio_test, "C <1 start, 0 stop, none both> Radio carrier test" }, { 0, ao_radio_test, NULL }, }; -- cgit v1.2.3 From a79606a6507fc01a74910f7959e84c4e9a730714 Mon Sep 17 00:00:00 2001 From: Bob Finch Date: Mon, 22 Nov 2010 12:24:42 -0700 Subject: Added PKGBUILDs for deps into contribs --- contrib/arch-linux/PKGBUILD-git.freetts | 38 +++++ contrib/arch-linux/PKGBUILD-git.jcommon | 28 ++++ contrib/arch-linux/PKGBUILD-git.jfreechart | 27 ++++ contrib/arch-linux/PKGBUILD-git.nsis.patched | 34 ++++ contrib/arch-linux/PKGBUILD-git.sdcc_patched | 34 ++++ contrib/arch-linux/new.patch | 35 ++++ contrib/arch-linux/nsis-2.43-64bit-fixes.patch | 211 +++++++++++++++++++++++++ 7 files changed, 407 insertions(+) create mode 100644 contrib/arch-linux/PKGBUILD-git.freetts create mode 100644 contrib/arch-linux/PKGBUILD-git.jcommon create mode 100644 contrib/arch-linux/PKGBUILD-git.jfreechart create mode 100644 contrib/arch-linux/PKGBUILD-git.nsis.patched create mode 100644 contrib/arch-linux/PKGBUILD-git.sdcc_patched create mode 100644 contrib/arch-linux/new.patch create mode 100644 contrib/arch-linux/nsis-2.43-64bit-fixes.patch diff --git a/contrib/arch-linux/PKGBUILD-git.freetts b/contrib/arch-linux/PKGBUILD-git.freetts new file mode 100644 index 00000000..b16a994d --- /dev/null +++ b/contrib/arch-linux/PKGBUILD-git.freetts @@ -0,0 +1,38 @@ +# Original contributor: Bob Finch + +pkgname=freetts +_pkgname=FreeTTS +pkgver=1.2.2 +_pkgver=1.2 +pkgrel=1 +pkgdesc="Sun's rewrite of flite for java" +arch=('any') +license=('custom') +depends=('java-environment') +makedepends=('junit' 'apache-ant') +#source=(http://downloads.sourceforge.net/project/\ +#$pkgname/$_pkgname/$_pkgname%20$pkgver/$pkgname-$pkgver-src.zip) +source=(http://downloads.sourceforge.net/project/\ +$pkgname/$_pkgname/$_pkgname%20$pkgver/$pkgname-$pkgver-bin.zip) +url="http://freetts.sourceforge.net/" +#md5sums=('692b5ece251fed88539736e55af5f391') +md5sums=('cd751e5fd5c7ed29cf6879fc5200605d') + +build() { +# [ -z "${JAVA_HOME}" ] && . /etc/profile.d/jdk.sh +# [ -z "${ANT_HOME}" ] && . /etc/profile.d/apache-ant.sh + +# cd ${startdir}/src/$pkgname-$pkgver/lib + cd ${startdir}/src/$pkgname-$_pkgver/lib + rm README.txt jsapi.sh jsapi.exe + +# cd ${startdir}/src/$pkgname-$pkgver +# ln -s . src +# /usr/share/java/apache-ant/bin/ant + + cd $srcdir/$pkgname-$_pkgver + install -d $pkgdir/usr/share/java/$pkgname/lib + install -m644 lib/* $pkgdir/usr/share/java/$pkgname/lib/ + +# cp -a bld $pkgdir/usr/share/java/$pkgname/ +} diff --git a/contrib/arch-linux/PKGBUILD-git.jcommon b/contrib/arch-linux/PKGBUILD-git.jcommon new file mode 100644 index 00000000..8c435436 --- /dev/null +++ b/contrib/arch-linux/PKGBUILD-git.jcommon @@ -0,0 +1,28 @@ +# Original contributor: Bob Finch + +pkgname=jcommon +_pkgname=JCommon +_project=jfreechart +pkgver=1.0.16 +pkgrel=1 +pkgdesc="Base routines for JFreeChart" +arch=('any') +license=('lgpl') +depends=('java-environment') +makedepends=('apache-ant' 'zip' 'gzip' 'tar') +source=(http://downloads.sourceforge.net/project/\ +$_project/3.%20$_pkgname/$pkgver/$pkgname-$pkgver.tar.gz) +url="http://www.jfree.org/jcommon/" +md5sums=('5fb774c225cdc7d15a99c9702031ae05') + +build() { + [ -z "${JAVA_HOME}" ] && . /etc/profile.d/jdk.sh + [ -z "${ANT_HOME}" ] && . /etc/profile.d/apache-ant.sh + + cd ${startdir}/src/$pkgname-$pkgver/ant + /usr/share/java/apache-ant/bin/ant + + cd ${startdir}/src/$pkgname-$pkgver + install -d $pkgdir/usr/share/java/$pkgname/lib + install -m644 $pkgname-$pkgver.jar $pkgdir/usr/share/java/$pkgname/lib/$pkgname.jar +} diff --git a/contrib/arch-linux/PKGBUILD-git.jfreechart b/contrib/arch-linux/PKGBUILD-git.jfreechart new file mode 100644 index 00000000..f7427983 --- /dev/null +++ b/contrib/arch-linux/PKGBUILD-git.jfreechart @@ -0,0 +1,27 @@ +# Original contributor: Bob Finch + +pkgname=jfreechart +_pkgname=JFreeChart +pkgver=1.0.13 +pkgrel=1 +pkgdesc="Charting program for Java" +arch=('any') +license=('lgpl') +depends=('java-environment' 'jcommon') +makedepends=('apache-ant' 'zip' 'gzip' 'tar') +source=(http://downloads.sourceforge.net/project/\ +$pkgname/1.%20$_pkgname/$pkgver/$pkgname-$pkgver.tar.gz) +url="http://www.jfree.org/jfreechart/" +md5sums=('c90e2f8f612b9aaf3f24a4afce219076') + +build() { + [ -z "${JAVA_HOME}" ] && . /etc/profile.d/jdk.sh + [ -z "${ANT_HOME}" ] && . /etc/profile.d/apache-ant.sh + + cd ${startdir}/src/$pkgname-$pkgver/ant + /usr/share/java/apache-ant/bin/ant + + cd ${startdir}/src/$pkgname-$pkgver + install -d $pkgdir/usr/share/java/$pkgname/lib + install -m644 lib/$pkgname-$pkgver.jar $pkgdir/usr/share/java/$pkgname/lib/$pkgname.jar +} diff --git a/contrib/arch-linux/PKGBUILD-git.nsis.patched b/contrib/arch-linux/PKGBUILD-git.nsis.patched new file mode 100644 index 00000000..fe751cb9 --- /dev/null +++ b/contrib/arch-linux/PKGBUILD-git.nsis.patched @@ -0,0 +1,34 @@ +# Contributor: Andre Klitzing +# Contributor: mosra +pkgname=nsis +pkgver=2.46 +pkgrel=3 +pkgdesc='A professional open source system to create Windows installers' +arch=('i686' 'x86_64') +url='http://nsis.sourceforge.net' +license='http://nsis.sourceforge.net/License' +depends=('mingw32-runtime') +makedepends=('scons' 'mingw32-gcc' 'mingw32-binutils' 'mingw32-w32api') +source=(http://downloads.sourceforge.net/project/nsis/NSIS%202/$pkgver/$pkgname-$pkgver-src.tar.bz2 + nsis-2.43-64bit-fixes.patch) +md5sums=('61c2e81739436b06d7cf7bcce1d533ac' + '9eead3b78da54e3afda8f6a5b663aea9') + +build() { + cd "$srcdir/$pkgname-$pkgver-src" + + # Patch taken from + # http://cvs.fedoraproject.org/viewvc/rpms/mingw32-nsis/F-11/nsis-2.43-64bit-fixes.patch + patch -p1 -i "$srcdir/nsis-2.43-64bit-fixes.patch" || return 1 + + # Patch version from DD-MM-YYY.cvs to 2.46 (makes CPack working again) + sed -i "s/'Version of NSIS', cvs_version)/'Version of NSIS', '${pkgver}')/" \ + "${srcdir}/${pkgname}-${pkgver}-src/SConstruct" + + scons PREFIX_DEST="$pkgdir/" PREFIX=/usr/i486-mingw32 SKIPUTILS='NSIS Menu' install || return 1 + + # Add a symlink to 'makensis' for lazy people ;-) + mkdir "$pkgdir/usr/bin/" + cd "$pkgdir/usr/bin/" + ln -s ../i486-mingw32/bin/makensis +} diff --git a/contrib/arch-linux/PKGBUILD-git.sdcc_patched b/contrib/arch-linux/PKGBUILD-git.sdcc_patched new file mode 100644 index 00000000..1a78c292 --- /dev/null +++ b/contrib/arch-linux/PKGBUILD-git.sdcc_patched @@ -0,0 +1,34 @@ +# $Id: PKGBUILD 23526 2010-08-12 12:59:41Z spupykin $ +# Maintainer: Sergej Pupykin +# Maintainer: Jose Negron +# Patched w/ keith packard's patch for altos - RJF 26-aug-10 + +pkgname=sdcc +pkgver=2.9.0 +pkgrel=2_patched +pkgdesc="Retargettable ANSI C compiler (Intel 8051, Maxim 80DS390, Zilog Z80 and the Motorola 68HC08)" +arch=('i686' 'x86_64') +license=('GPL') +depends=('bash' 'gcc-libs') +makedepends=('gputils' 'flex' 'bison' 'patch') +provides=('sdcc') +conflicts=('sdcc') +url="http://sdcc.sourceforge.net/" +options=(!strip) +#Patch file was taken from https://bugzilla.redhat.com/show_bug.cgi?id=488217 +source=(http://downloads.sourceforge.net/sourceforge/sdcc/$pkgname-src-$pkgver.tar.bz2 + http://aur.archlinux.org/packages/$pkgname/$pkgname/$pkgname-$pkgver.patch + new.patch) +md5sums=('a6151ed328fd3bc48305ffbc628dc122' + '35313a8edca4f2c8a03ad57036da4e62' + '65612bb094e719713bc477efd6000672') + +build() { + cd $srcdir/$pkgname + patch -p1 -i ../$pkgname-$pkgver.patch + patch -p0 -i ../new.patch + ./configure --prefix=$pkgdir/usr + make + make install + strip $pkgdir/usr/bin/* || true +} diff --git a/contrib/arch-linux/new.patch b/contrib/arch-linux/new.patch new file mode 100644 index 00000000..74e1df06 --- /dev/null +++ b/contrib/arch-linux/new.patch @@ -0,0 +1,35 @@ +--- src/SDCCast.c ++++ src/SDCCast.c +@@ -863,6 +863,8 @@ processParms (ast *func, + + ftype = (*actParm)->ftype; + ++ resultType = RESULT_TYPE_NONE; ++ + /* If it's a char, upcast to int. */ + if (IS_INTEGRAL (ftype) + && (getSize (ftype) < (unsigned) INTSIZE)) +@@ -874,12 +876,14 @@ processParms (ast *func, + { + newType = newAst_LINK (copyLinkChain(ftype)); + DCL_TYPE (newType->opval.lnk) = port->unqualified_pointer; ++ resultType = RESULT_TYPE_GPTR; + } + + if (IS_AGGREGATE (ftype)) + { + newType = newAst_LINK (copyLinkChain (ftype)); + DCL_TYPE (newType->opval.lnk) = port->unqualified_pointer; ++ resultType = RESULT_TYPE_GPTR; + } + + if (newType) +@@ -890,7 +894,7 @@ processParms (ast *func, + (*actParm)->filename = (*actParm)->right->filename; + (*actParm)->lineno = (*actParm)->right->lineno; + +- decorateType (*actParm, RESULT_TYPE_NONE); ++ decorateType (*actParm, resultType); + } + return 0; + } /* vararg */ diff --git a/contrib/arch-linux/nsis-2.43-64bit-fixes.patch b/contrib/arch-linux/nsis-2.43-64bit-fixes.patch new file mode 100644 index 00000000..342396d7 --- /dev/null +++ b/contrib/arch-linux/nsis-2.43-64bit-fixes.patch @@ -0,0 +1,211 @@ +diff -ur nsis-2.43-src/SCons/Config/gnu nsis-2.43-src-64bit-fixes/SCons/Config/gnu +--- nsis-2.43-src/SCons/Config/gnu 2009-02-05 01:52:28.000000000 +0100 ++++ nsis-2.43-src-64bit-fixes/SCons/Config/gnu 2009-02-25 07:59:44.000000000 +0100 +@@ -95,8 +95,6 @@ + makensis_env.Append(CXXFLAGS = ['-Wall']) # all warnings + + conf = FlagsConfigure(makensis_env) +-conf.CheckCompileFlag('-m32') # +-conf.CheckLinkFlag('-m32') # + conf.CheckLinkFlag('$MAP_FLAG') # generate map file + if not defenv['DEBUG'] and defenv['STRIP'] and defenv['STRIP_CP']: + TestStrip(conf) # strip +@@ -149,8 +147,6 @@ + ### cross-platform util environment adjustments + + conf = FlagsConfigure(cp_util_env) +-conf.CheckCompileFlag('-m32') +-conf.CheckLinkFlag('-m32') + if not defenv['DEBUG'] and defenv['STRIP'] and defenv['STRIP_CP']: + TestStrip(conf) # strip + conf.Finish() +@@ -160,8 +156,6 @@ + test_env = defenv.Clone() + test_env.Append(CPPPATH = ['#$BUILD_CONFIG']) + conf = FlagsConfigure(test_env) +-conf.CheckCompileFlag('-m32') +-conf.CheckLinkFlag('-m32') + conf.Finish() + + ### weird GCC requirements +diff -ur nsis-2.43-src/Source/DialogTemplate.cpp nsis-2.43-src-64bit-fixes/Source/DialogTemplate.cpp +--- nsis-2.43-src/Source/DialogTemplate.cpp 2007-11-30 10:54:13.000000000 +0100 ++++ nsis-2.43-src-64bit-fixes/Source/DialogTemplate.cpp 2009-02-25 07:59:44.000000000 +0100 +@@ -74,7 +74,7 @@ + if (IS_INTRESOURCE(x)) { \ + *(WORD*)seeker = 0xFFFF; \ + seeker += sizeof(WORD); \ +- *(WORD*)seeker = ConvertEndianness(WORD(DWORD(x))); \ ++ *(WORD*)seeker = ConvertEndianness(WORD(long(x))); \ + seeker += sizeof(WORD); \ + } \ + else { \ +@@ -622,7 +622,7 @@ + } + } + +- assert((DWORD) seeker - (DWORD) pbDlg == dwSize); ++ assert((long) seeker - (long) pbDlg == dwSize); + + // DONE! + return pbDlg; +diff -ur nsis-2.43-src/Source/mmap.cpp nsis-2.43-src-64bit-fixes/Source/mmap.cpp +--- nsis-2.43-src/Source/mmap.cpp 2009-02-01 15:44:30.000000000 +0100 ++++ nsis-2.43-src-64bit-fixes/Source/mmap.cpp 2009-02-25 07:59:44.000000000 +0100 +@@ -322,7 +322,7 @@ + if (!pView) + return; + +- unsigned int alignment = ((unsigned int)pView) % m_iAllocationGranularity; ++ unsigned int alignment = ((unsigned long)pView) % m_iAllocationGranularity; + pView = (char *)pView - alignment; + size += alignment; + #ifdef _WIN32 +diff -ur nsis-2.43-src/Source/Platform.h nsis-2.43-src-64bit-fixes/Source/Platform.h +--- nsis-2.43-src/Source/Platform.h 2009-02-01 15:44:30.000000000 +0100 ++++ nsis-2.43-src-64bit-fixes/Source/Platform.h 2009-02-25 07:59:44.000000000 +0100 +@@ -166,7 +166,7 @@ + # define MAKEINTRESOURCE MAKEINTRESOURCEA + # endif + # ifndef IMAGE_FIRST_SECTION +-# define IMAGE_FIRST_SECTION(h) ( PIMAGE_SECTION_HEADER( (DWORD) h + \ ++# define IMAGE_FIRST_SECTION(h) ( PIMAGE_SECTION_HEADER( (long) h + \ + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + \ + FIX_ENDIAN_INT16(PIMAGE_NT_HEADERS(h)->FileHeader.SizeOfOptionalHeader) ) ) + # endif +@@ -198,7 +198,7 @@ + #endif + + #ifndef ULONG_PTR +-# define ULONG_PTR DWORD ++# define ULONG_PTR ULONG + #endif + + #ifndef IDC_HAND +@@ -703,7 +703,7 @@ + WORD e_oemid; + WORD e_oeminfo; + WORD e_res2[10]; +- LONG e_lfanew; ++ DWORD e_lfanew; + } IMAGE_DOS_HEADER,*PIMAGE_DOS_HEADER; + # pragma pack() + # pragma pack(4) +diff -ur nsis-2.43-src/Source/Plugins.cpp nsis-2.43-src-64bit-fixes/Source/Plugins.cpp +--- nsis-2.43-src/Source/Plugins.cpp 2009-02-01 15:44:30.000000000 +0100 ++++ nsis-2.43-src-64bit-fixes/Source/Plugins.cpp 2009-02-25 07:59:44.000000000 +0100 +@@ -136,7 +136,7 @@ + DWORD prd = FIX_ENDIAN_INT32(sections[i].PointerToRawData); + PIMAGE_EXPORT_DIRECTORY exports = PIMAGE_EXPORT_DIRECTORY(&dlldata[0] + prd + ExportDirVA - va); + DWORD na = FIX_ENDIAN_INT32(exports->AddressOfNames); +- unsigned long *names = (unsigned long*)((unsigned long) exports + (char *) na - ExportDirVA); ++ unsigned int *names = (unsigned int*)((unsigned long) exports + (char *) na - ExportDirVA); + for (unsigned long j = 0; j < FIX_ENDIAN_INT32(exports->NumberOfNames); j++) + { + const string name = string((char*)exports + FIX_ENDIAN_INT32(names[j]) - ExportDirVA); +diff -ur nsis-2.43-src/Source/ResourceEditor.cpp nsis-2.43-src-64bit-fixes/Source/ResourceEditor.cpp +--- nsis-2.43-src/Source/ResourceEditor.cpp 2009-02-05 01:50:12.000000000 +0100 ++++ nsis-2.43-src-64bit-fixes/Source/ResourceEditor.cpp 2009-02-25 07:59:44.000000000 +0100 +@@ -684,7 +684,7 @@ + rdDir.NumberOfIdEntries = ConvertEndianness(rdDir.NumberOfIdEntries); + + CopyMemory(seeker, &rdDir, sizeof(IMAGE_RESOURCE_DIRECTORY)); +- crd->m_dwWrittenAt = DWORD(seeker); ++ crd->m_dwWrittenAt = long(seeker); + seeker += sizeof(IMAGE_RESOURCE_DIRECTORY); + + for (int i = 0; i < crd->CountEntries(); i++) { +@@ -705,7 +705,7 @@ + rDirE.UName.NameString.NameIsString = (crd->GetEntry(i)->HasName()) ? 1 : 0; + + CopyMemory(seeker, &rDirE, sizeof(MY_IMAGE_RESOURCE_DIRECTORY_ENTRY)); +- crd->GetEntry(i)->m_dwWrittenAt = DWORD(seeker); ++ crd->GetEntry(i)->m_dwWrittenAt = long(seeker); + seeker += sizeof(MY_IMAGE_RESOURCE_DIRECTORY_ENTRY); + } + qDirs.pop(); +@@ -721,7 +721,7 @@ + rDataE.Size = ConvertEndianness(cRDataE->GetSize()); + + CopyMemory(seeker, &rDataE, sizeof(IMAGE_RESOURCE_DATA_ENTRY)); +- cRDataE->m_dwWrittenAt = DWORD(seeker); ++ cRDataE->m_dwWrittenAt = long(seeker); + seeker += sizeof(IMAGE_RESOURCE_DATA_ENTRY); + + qDataEntries.pop(); +@@ -733,7 +733,7 @@ + while (!qStrings.empty()) { + CResourceDirectoryEntry* cRDirE = qStrings.front(); + +- PMY_IMAGE_RESOURCE_DIRECTORY_ENTRY(cRDirE->m_dwWrittenAt)->UName.NameString.NameOffset = ConvertEndianness(DWORD(seeker) - DWORD(pbRsrcSec)); ++ PMY_IMAGE_RESOURCE_DIRECTORY_ENTRY(cRDirE->m_dwWrittenAt)->UName.NameString.NameOffset = ConvertEndianness(long(seeker) - long(pbRsrcSec)); + + WCHAR* szName = cRDirE->GetName(); + WORD iLen = winchar_strlen(szName) + 1; +@@ -764,7 +764,7 @@ + /* + * Set all of the directory entries offsets. + */ +- SetOffsets(m_cResDir, DWORD(pbRsrcSec)); ++ SetOffsets(m_cResDir, long(pbRsrcSec)); + } + + // Sets the offsets in directory entries +@@ -887,7 +887,7 @@ + // Returns -1 if can not be found + int CResourceDirectory::Find(WCHAR* szName) { + if (IS_INTRESOURCE(szName)) +- return Find((WORD) (DWORD) szName); ++ return Find((WORD) (long) szName); + else + if (szName[0] == '#') + return Find(WORD(winchar_stoi(szName + 1))); +@@ -965,7 +965,7 @@ + if (IS_INTRESOURCE(szName)) { + m_bHasName = false; + m_szName = 0; +- m_wId = (WORD) (DWORD) szName; ++ m_wId = (WORD) (long) szName; + } + else { + m_bHasName = true; +@@ -979,7 +979,7 @@ + if (IS_INTRESOURCE(szName)) { + m_bHasName = false; + m_szName = 0; +- m_wId = (WORD) (DWORD) szName; ++ m_wId = (WORD) (long) szName; + } + else { + m_bHasName = true; +diff -ur nsis-2.43-src/Source/util.cpp nsis-2.43-src-64bit-fixes/Source/util.cpp +--- nsis-2.43-src/Source/util.cpp 2009-02-01 15:44:30.000000000 +0100 ++++ nsis-2.43-src-64bit-fixes/Source/util.cpp 2009-02-25 07:59:44.000000000 +0100 +@@ -77,9 +77,9 @@ + } + + if (width != 0) { +- LONG biWidth; ++ DWORD biWidth; + fseek(f, 18, SEEK_SET); // Seek to the width member of the header +- fread(&biWidth, sizeof(LONG), 1, f); ++ fread(&biWidth, sizeof(DWORD), 1, f); + FIX_ENDIAN_INT32_INPLACE(biWidth); + if (width != biWidth) { + fclose(f); +@@ -88,12 +88,12 @@ + } + + if (height != 0) { +- LONG biHeight; ++ DWORD biHeight; + fseek(f, 22, SEEK_SET); // Seek to the height member of the header +- fread(&biHeight, sizeof(LONG), 1, f); ++ fread(&biHeight, sizeof(DWORD), 1, f); + FIX_ENDIAN_INT32_INPLACE(biHeight); + // Bitmap height can be negative too... +- if (height != abs(biHeight)) { ++ if (height != abs((long int)biHeight)) { + fclose(f); + return -3; + } -- cgit v1.2.3 From 68323cbb222f1f33198a42abaa0550af22f75a93 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 22 Nov 2010 15:53:27 -0800 Subject: altosui: Close serial port when debug link fails If the debug connection isn't working, close down the serial port when reporting the failure. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosFlash.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ao-tools/altosui/AltosFlash.java b/ao-tools/altosui/AltosFlash.java index fa2465d3..3af25c23 100644 --- a/ao-tools/altosui/AltosFlash.java +++ b/ao-tools/altosui/AltosFlash.java @@ -336,7 +336,9 @@ public class AltosFlash { debug = new AltosDebug(in_debug_dongle); input = new FileInputStream(file); image = new AltosHexfile(input); - if (!debug.check_connection()) + if (!debug.check_connection()) { + debug.close(); throw new IOException("Debug port not connected"); + } } } \ No newline at end of file -- cgit v1.2.3 From b27327a05d249eaf969b67d2a8d12fc6a93841f0 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 22 Nov 2010 15:56:04 -0800 Subject: altos: assume igniter worked. Many igniters don't go open when fired, so there's no way to know if they worked. Assume they did as a failed igniter is unlikely to do anything when fired again anyways. Signed-off-by: Keith Packard --- src/ao_ignite.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ao_ignite.c b/src/ao_ignite.c index 4093b6a7..58d340d9 100644 --- a/src/ao_ignite.c +++ b/src/ao_ignite.c @@ -113,9 +113,7 @@ ao_igniter(void) ao_igniter_fire(igniter); ao_delay(AO_IGNITER_CHARGE_TIME); - status = ao_igniter_status(igniter); - if (status == ao_igniter_open) - ao_ignition[igniter].fired = 1; + ao_ignition[igniter].fired = 1; } } } -- cgit v1.2.3 From 5523e7d55ecc8d310e495fa4f5115f7483c42d65 Mon Sep 17 00:00:00 2001 From: Bdale Garbee Date: Mon, 22 Nov 2010 21:07:10 -0700 Subject: add a rudimentary --help for command line use --- ao-tools/altosui/AltosUI.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index 93a5e0d8..94c4dd2a 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -353,7 +353,14 @@ public class AltosUI extends JFrame { public static void main(final String[] args) { int process = 0; /* Handle batch-mode */ - if (args.length == 3 && args[0].equals("--fetchmaps")) { + if (args.length == 1 && args[0].equals("--help")) { + System.out.printf("Usage: altosui [OPTION]... [FILE]...\n"); + System.out.printf(" Options:\n"); + System.out.printf(" --fetchmaps \tpre-fetch maps for site map view\n"); + System.out.printf(" --replay \t\trelive the glory of past flights \n"); + System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n"); + System.out.printf(" --kml\tgenerate KML output for use with Google Earth\n"); + } else if (args.length == 3 && args[0].equals("--fetchmaps")) { double lat = Double.parseDouble(args[1]); double lon = Double.parseDouble(args[2]); AltosSiteMap.prefetchMaps(lat, lon, 5, 5); -- cgit v1.2.3 From 737f2fdd012202f453120ece117ae5e859b32082 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 22 Nov 2010 22:26:19 -0800 Subject: doc: Add internal documentation for AltOS Signed-off-by: Keith Packard --- doc/Makefile | 4 +- doc/altos.xsl | 1441 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1443 insertions(+), 2 deletions(-) create mode 100644 doc/altos.xsl diff --git a/doc/Makefile b/doc/Makefile index 57300c10..52934290 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -2,8 +2,8 @@ # http://docbook.sourceforge.net/release/xsl/current/README # -HTML=telemetrum-doc.html altosui-doc.html -PDF=telemetrum-doc.pdf altosui-doc.pdf +HTML=telemetrum-doc.html altosui-doc.html altos.html +PDF=telemetrum-doc.pdf altosui-doc.pdf altos.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 diff --git a/doc/altos.xsl b/doc/altos.xsl new file mode 100644 index 00000000..9a88a5b5 --- /dev/null +++ b/doc/altos.xsl @@ -0,0 +1,1441 @@ + + + + + AltOS + Altos Metrum Operating System + + + Keith + Packard + + + 2010 + Keith Packard + + + + This document is released under the terms of the + + Creative Commons ShareAlike 3.0 + + license. + + + + + 0.1 + 22 November 2010 + Initial content + + + + + Overview + + AltOS is a operating system built for the 8051-compatible + processor found in the TI cc1111 microcontroller. It's designed + to be small and easy to program with. The main features are: + + + Multi-tasking. While the 8051 doesn't provide separate + address spaces, it's often easier to write code that operates + in separate threads instead of tying everything into one giant + event loop. + + + + Non-preemptive. This increases latency for thread + switching but reduces the number of places where context + switching can occur. It also simplifies the operating system + design somewhat. Nothing in the target system (rocket flight + control) has tight timing requirements, and so this seems like + a reasonable compromise. + + + + Sleep/wakeup scheduling. Taken directly from ancient + Unix designs, these two provide the fundemental scheduling + primitive within AltOS. + + + + Mutexes. As a locking primitive, mutexes are easier to + use than semaphores, at least in my experience. + + + + Timers. Tasks can set an alarm which will abort any + pending sleep, allowing operations to time-out instead of + blocking forever. + + + + + + The device drivers and other subsystems in AltOS are + conventionally enabled by invoking their _init() function from + the 'main' function before that calls + ao_start_scheduler(). These functions initialize the pin + assignments, add various commands to the command processor and + may add tasks to the scheduler to handle the device. A typical + main program, thus, looks like: + +void +main(void) +{ + ao_clock_init(); + + /* Turn on the LED until the system is stable */ + ao_led_init(LEDS_AVAILABLE); + ao_led_on(AO_LED_RED); + ao_timer_init(); + ao_cmd_init(); + ao_usb_init(); + ao_monitor_init(AO_LED_GREEN, TRUE); + ao_rssi_init(AO_LED_RED); + ao_radio_init(); + ao_packet_slave_init(); + ao_packet_master_init(); +#if HAS_DBG + ao_dbg_init(); +#endif + ao_config_init(); + ao_start_scheduler(); +} + + As you can see, a long sequence of subsystems are initialized + and then the scheduler is started. + + + + Programming the 8051 with SDCC + + The 8051 is a primitive 8-bit processor, designed in the mists + of time in as few transistors as possible. The architecture is + highly irregular and includes several separate memory + spaces. Furthermore, accessing stack variables is slow, and the + stack itself is of limited size. While SDCC papers over the + instruction set, it is not completely able to hide the memory + architecture from the application designer. + +
+ 8051 memory spaces + + The __data/__xdata/__code memory spaces below were completely + separate in the original 8051 design. In the cc1111, this + isn't true—they all live in a single unified 64kB address + space, and so it's possible to convert any address into a + unique 16-bit address. SDCC doesn't know this, and so a + 'global' address to SDCC consumes 3 bytes of memory, 1 byte as + a tag indicating the memory space and 2 bytes of offset within + that space. AltOS avoids these 3-byte addresses as much as + possible; using them involves a function call per byte + access. The result is that nearly every variable declaration + is decorated with a memory space identifier which clutters the + code but makes the resulting code far smaller and more + efficient. + + + SDCC 8051 memory spaces + + __data + + + The 8051 can directly address these 128 bytes of + memory. This makes them precious so they should be + reserved for frequently addressed values. Oh, just to + confuse things further, the 8 general registers in the + CPU are actually stored in this memory space. There are + magic instructions to 'bank switch' among 4 banks of + these registers located at 0x00 - 0x1F. AltOS uses only + the first bank at 0x00 - 0x07, leaving the other 24 + bytes available for other data. + + + + + __idata + + + There are an additional 128 bytes of internal memory + that share the same address space as __data but which + cannot be directly addressed. The stack normally + occupies this space and so AltOS doesn't place any + static storage here. + + + + + __xdata + + + This is additional general memory accessed through a + single 16-bit address register. The CC1111F32 has 32kB + of memory available here. Most program data should live + in this memory space. + + + + + __pdata + + + This is an alias for the first 256 bytes of __xdata + memory, but uses a shorter addressing mode with + single global 8-bit value for the high 8 bits of the + address and any of several 8-bit registers for the low 8 + bits. AltOS uses a few bits of this memory, it should + probably use more. + + + + + __code + + + All executable code must live in this address space, but + you can stick read-only data here too. It is addressed + using the 16-bit address register and special 'code' + access opcodes. Anything read-only should live in this space. + + + + + __bit + + + The 8051 has 128 bits of bit-addressible memory that + lives in the __data segment from 0x20 through + 0x2f. Special instructions access these bits + in a single atomic operation. This isn't so much a + separate address space as a special addressing mode for + a few bytes in the __data segment. + + + + + __sfr, __sfr16, __sfr32, __sbit + + + Access to physical registers in the device use this mode + which declares the variable name, it's type and the + address it lives at. No memory is allocated for these + variables. + + + + +
+
+ Function calls on the 8051 + + Because stack addressing is expensive, and stack space + limited, the default function call declaration in SDCC + allocates all parameters and local variables in static global + memory. Just like fortran. This makes these functions + non-reentrant, and also consume space for parameters and + locals even when they are not running. The benefit is smaller + code and faster execution. + +
+ __reentrant functions + + All functions which are re-entrant, either due to recursion + or due to a potential context switch while executing, should + be marked as __reentrant so that their parameters and local + variables get allocated on the stack. This ensures that + these values are not overwritten by another invocation of + the function. + + + Functions which use significant amounts of space for + arguments and/or local variables and which are not often + invoked can also be marked as __reentrant. The resulting + code will be larger, but the savings in memory are + frequently worthwhile. + +
+
+ Non __reentrant functions + + All parameters and locals in non-reentrant functions can + have data space decoration so that they are allocated in + __xdata, __pdata or __data space as desired. This can avoid + consuming __data space for infrequently used variables in + frequently used functions. + + + All library functions called by SDCC, including functions + for multiplying and dividing large data types, are + non-reentrant. Because of this, interrupt handlers must not + invoke any library functions, including the multiply and + divide code. + +
+
+ __interrupt functions + + Interrupt functions are declared with with an __interrupt + decoration that includes the interrupt number. SDCC saves + and restores all of the registers in these functions and + uses the 'reti' instruction at the end so that they operate + as stand-alone interrupt handlers. Interrupt functions may + call the ao_wakeup function to wake AltOS tasks. + +
+
+ __critical functions and statements + + SDCC has built-in support for suspending interrupts during + critical code. Functions marked as __critical will have + interrupts suspended for the whole period of + execution. Individual statements may also be marked as + __critical which blocks interrupts during the execution of + that statement. Keeping critical sections as short as + possible is key to ensuring that interrupts are handled as + quickly as possible. + +
+
+
+ + Task functions + + This chapter documents how to create, destroy and schedule AltOS tasks. + + + AltOS Task Functions + + ao_add_task + + +void +ao_add_task(__xdata struct ao_task * task, + void (*start)(void), + __code char *name); + + + This initializes the statically allocated task structure, + assigns a name to it (not used for anything but the task + display), and the start address. It does not switch to the + new task. 'start' must not ever return; there is no place + to return to. + + + + + ao_exit + + +void +ao_exit(void) + + + This terminates the current task. + + + + + ao_sleep + + +void +ao_sleep(__xdata void *wchan) + + + This suspends the current task until 'wchan' is signaled + by ao_wakeup, or until the timeout, set by ao_alarm, + fires. If 'wchan' is signaled, ao_sleep returns 0, otherwise + it returns 1. This is the only way to switch to another task. + + + + + ao_wakeup + + +void +ao_wakeup(__xdata void *wchan) + + + Wake all tasks blocked on 'wchan'. This makes them + available to be run again, but does not actually switch + to another task. + + + + + ao_alarm + + +void +ao_alarm(uint16_t delay) + + + Schedules an alarm to fire in at least 'delay' ticks. If + the task is asleep when the alarm fires, it will wakeup + and ao_sleep will return 1. + + + + + ao_wake_task + + +void +ao_wake_task(__xdata struct ao_task *task) + + + Force a specific task to wake up, independent of which + 'wchan' it is waiting for. + + + + + ao_start_scheduler + + +void +ao_start_scheduler(void) + + + This is called from 'main' when the system is all + initialized and ready to run. It will not return. + + + + + ao_clock_init + + +void +ao_clock_init(void) + + + This turns on the external 48MHz clock then switches the + hardware to using it. This is required by many of the + internal devices like USB. It should be called by the + 'main' function first, before initializing any of the + other devices in the system. + + + + + + + Timer Functions + + AltOS sets up one of the cc1111 timers to run at 100Hz and + exposes this tick as the fundemental unit of time. At each + interrupt, AltOS increments the counter, and schedules any tasks + waiting for that time to pass, then fires off the ADC system to + collect current data readings. Doing this from the ISR ensures + that the ADC values are sampled at a regular rate, independent + of any scheduling jitter. + + + AltOS Timer Functions + + ao_time + + +uint16_t +ao_time(void) + + + Returns the current system tick count. Note that this is + only a 16 bit value, and so it wraps every 655.36 seconds. + + + + + ao_delay + + +void +ao_delay(uint16_t ticks); + + + Suspend the current task for at least 'ticks' clock units. + + + + + ao_timer_set_adc_interval + + +void +ao_timer_set_adc_interval(uint8_t interval); + + + This sets the number of ticks between ADC samples. If set + to 0, no ADC samples are generated. AltOS uses this to + slow down the ADC sampling rate to save power. + + + + + ao_timer_init + + +void +ao_timer_init(void) + + + This turns on the 100Hz tick using the CC1111 timer 1. It + is required for any of the time-based functions to + work. It should be called by 'main' before ao_start_scheduler. + + + + + + + AltOS Mutexes + + AltOS provides mutexes as a basic synchronization primitive. Each + mutexes is simply a byte of memory which holds 0 when the mutex + is free or the task id of the owning task when the mutex is + owned. Mutex calls are checked—attempting to acquire a mutex + already held by the current task or releasing a mutex not held + by the current task will both cause a panic. + + + Mutex Functions + + ao_mutex_get + + +void +ao_mutex_get(__xdata uint8_t *mutex); + + + Acquires the specified mutex, blocking if the mutex is + owned by another task. + + + + + ao_mutex_put + + +void +ao_mutex_put(__xdata uint8_t *mutex); + + + Releases the specified mutex, waking up all tasks waiting + for it. + + + + + + + CC1111 DMA engine + + The CC1111 contains a useful bit of extra hardware in the form + of five programmable DMA engines. They can be configured to copy + data in memory, or between memory and devices (or even between + two devices). AltOS exposes a general interface to this hardware + and uses it to handle radio and SPI data. + + + Code using a DMA engine should allocate one at startup + time. There is no provision to free them, and if you run out, + AltOS will simply panic. + + + During operation, the DMA engine is initialized with the + transfer parameters. Then it is started, at which point it + awaits a suitable event to start copying data. When copying data + from hardware to memory, that trigger event is supplied by the + hardware device. When copying data from memory to hardware, the + transfer is usually initiated by software. + + + AltOS DMA functions + + ao_dma_alloc + + +uint8_t +ao_dma_alloc(__xdata uint8_t *done) + + + Allocates a DMA engine, returning the identifier. Whenever + this DMA engine completes a transfer. 'done' is cleared + when the DMA is started, and then receives the + AO_DMA_DONE bit on a successful transfer or the + AO_DMA_ABORTED bit if ao_dma_abort was called. Note that + it is possible to get both bits if the transfer was + aborted after it had finished. + + + + + ao_dma_set_transfer + + +void +ao_dma_set_transfer(uint8_t id, + void __xdata *srcaddr, + void __xdata *dstaddr, + uint16_t count, + uint8_t cfg0, + uint8_t cfg1) + + + Initializes the specified dma engine to copy data + from 'srcaddr' to 'dstaddr' for 'count' units. cfg0 and + cfg1 are values directly out of the CC1111 documentation + and tell the DMA engine what the transfer unit size, + direction and step are. + + + + + ao_dma_start + + +void +ao_dma_start(uint8_t id); + + + Arm the specified DMA engine and await a signal from + either hardware or software to start transferring data. + + + + + ao_dma_trigger + + +void +ao_dma_trigger(uint8_t id) + + + Trigger the specified DMA engine to start copying data. + + + + + ao_dma_abort + + +void +ao_dma_abort(uint8_t id) + + + Terminate any in-progress DMA transation, marking its + 'done' variable with the AO_DMA_ABORTED bit. + + + + + + + SDCC Stdio interface + + AltOS offers a stdio interface over both USB and the RF packet + link. This provides for control of the device localy or + remotely. This is hooked up to the stdio functions in SDCC by + providing the standard putchar/getchar/flush functions. These + automatically multiplex the two available communication + channels; output is always delivered to the channel which + provided the most recent input. + + + SDCC stdio functions + + putchar + + +void +putchar(char c) + + + Delivers a single character to the current console + device. + + + + + getchar + + +char +getchar(void) + + + Reads a single character from any of the available + console devices. The current console device is set to + that which delivered this character. This blocks until + a character is available. + + + + + flush + + +void +flush(void) + + + Flushes the current console device output buffer. Any + pending characters will be delivered to the target device. +xo + + + + ao_add_stdio + + +void +ao_add_stdio(char (*pollchar)(void), + void (*putchar)(char), + void (*flush)(void)) + + + This adds another console device to the available + list. + + + 'pollchar' returns either an available character or + AO_READ_AGAIN if none is available. Significantly, it does + not block. The device driver must set 'ao_stdin_ready' to + 1 and call ao_wakeup(&ao_stdin_ready) when it receives + input to tell getchar that more data is available, at + which point 'pollchar' will be called again. + + + 'putchar' queues a character for output, flushing if the output buffer is + full. It may block in this case. + + + 'flush' forces the output buffer to be flushed. It may + block until the buffer is delivered, but it is not + required to do so. + + + + + + + Command line interface + + AltOS includes a simple command line parser which is hooked up + to the stdio interfaces permitting remote control of the device + over USB or the RF link as desired. Each command uses a single + character to invoke it, the remaining characters on the line are + available as parameters to the command. + + + AltOS command line parsing functions + + ao_cmd_register + + +void +ao_cmd_register(__code struct ao_cmds *cmds) + + + This registers a set of commands with the command + parser. There is a fixed limit on the number of command + sets, the system will panic if too many are registered. + Each command is defined by a struct ao_cmds entry: + +struct ao_cmds { + char cmd; + void (*func)(void); + const char *help; +}; + + 'cmd' is the character naming the command. 'func' is the + function to invoke and 'help' is a string displayed by the + '?' command. Syntax errors found while executing 'func' + should be indicated by modifying the global ao_cmd_status + variable with one of the following values: + + + ao_cmd_success + + + The command was parsed successfully. There is no + need to assign this value, it is the default. + + + + + ao_cmd_lex_error + + + A token in the line was invalid, such as a number + containing invalid characters. The low-level + lexing functions already assign this value as needed. + + + + + ao_syntax_error + + + The command line is invalid for some reason other + than invalid tokens. + + + + + + + + + ao_cmd_lex + + +void +ao_cmd_lex(void); + + + This gets the next character out of the command line + buffer and sticks it into ao_cmd_lex_c. At the end of the + line, ao_cmd_lex_c will get a newline ('\n') character. + + + + + ao_cmd_put16 + + +void +ao_cmd_put16(uint16_t v); + + + Writes 'v' as four hexadecimal characters. + + + + + ao_cmd_put8 + + +void +ao_cmd_put8(uint8_t v); + + + Writes 'v' as two hexadecimal characters. + + + + + ao_cmd_white + + +void +ao_cmd_white(void) + + + This skips whitespace by calling ao_cmd_lex while + ao_cmd_lex_c is either a space or tab. It does not skip + any characters if ao_cmd_lex_c already non-white. + + + + + ao_cmd_hex + + +void +ao_cmd_hex(void) + + + This reads a 16-bit hexadecimal value from the command + line with optional leading whitespace. The resulting value + is stored in ao_cmd_lex_i; + + + + + ao_cmd_decimal + + +void +ao_cmd_decimal(void) + + + This reads a 32-bit decimal value from the command + line with optional leading whitespace. The resulting value + is stored in ao_cmd_lex_u32 and the low 16 bits are stored + in ao_cmd_lex_i; + + + + + ao_match_word + + +uint8_t +ao_match_word(__code char *word) + + + This checks to make sure that 'word' occurs on the command + line. It does not skip leading white space. If 'word' is + found, then 1 is returned. Otherwise, ao_cmd_status is set to + ao_cmd_syntax_error and 0 is returned. + + + + + ao_cmd_init + + +void +ao_cmd_init(void + + + Initializes the command system, setting up the built-in + commands and adding a task to run the command processing + loop. It should be called by 'main' before ao_start_scheduler. + + + + + + + CC1111 USB target device + + The CC1111 contains a full-speed USB target device. It can be + programmed to offer any kind of USB target, but to simplify + interactions with a variety of operating systems, AltOS provides + only a single target device profile, that of a USB modem which + has native drivers for Linux, Windows and Mac OS X. It would be + easy to change the code to provide an alternate target device if + necessary. + + + To the rest of the system, the USB device looks like a simple + two-way byte stream. It can be hooked into the command line + interface if desired, offering control of the device over the + USB link. Alternatively, the functions can be accessed directly + to provide for USB-specific I/O. + + + AltOS USB functions + + ao_usb_flush + + +void +ao_usb_flush(void); + + + Flushes any pending USB output. This queues an 'IN' packet + to be delivered to the USB host if there is pending data, + or if the last IN packet was full to indicate to the host + that there isn't any more pending data available. + + + + + ao_usb_putchar + + +void +ao_usb_putchar(char c); + + + If there is a pending 'IN' packet awaiting delivery to the + host, this blocks until that has been fetched. Then, this + adds a byte to the pending IN packet for delivery to the + USB host. If the USB packet is full, this queues the 'IN' + packet for delivery. + + + + + ao_usb_pollchar + + +char +ao_usb_pollchar(void); + + + If there are no characters remaining in the last 'OUT' + packet received, this returns AO_READ_AGAIN. Otherwise, it + returns the next character, reporting to the host that it + is ready for more data when the last character is gone. + + + + + ao_usb_getchar + + +char +ao_usb_getchar(void); + + + This uses ao_pollchar to receive the next character, + blocking while ao_pollchar returns AO_READ_AGAIN. + + + + + ao_usb_disable + + +void +ao_usb_disable(void); + + + This turns off the USB controller. It will no longer + respond to host requests, nor return characters. Calling + any of the i/o routines while the USB device is disabled + is undefined, and likely to break things. Disabling the + USB device when not needed saves power. + + + Note that neither TeleDongle nor TeleMetrum are able to + signal to the USB host that they have disconnected, so + after disabling the USB device, it's likely that the cable + will need to be disconnected and reconnected before it + will work again. + + + + + ao_usb_enable + + +void +ao_usb_enable(void); + + + This turns the USB controller on again after it has been + disabled. See the note above about needing to physically + remove and re-insert the cable to get the host to + re-initialize the USB link. + + + + + ao_usb_init + + +void +ao_usb_init(void); + + + This turns the USB controller on, adds a task to handle + the control end point and adds the usb I/O functions to + the stdio system. Call this from main before + ao_start_scheduler. + + + + + + + CC1111 Serial peripheral + + The CC1111 provides two USART peripherals. AltOS uses one for + asynch serial data, generally to communicate with a GPS device, + and the other for a SPI bus. The UART is configured to operate + in 8-bits, no parity, 1 stop bit framing. The default + configuration has clock settings for 4800, 9600 and 57600 baud + operation. Additional speeds can be added by computing + appropriate clock values. + + + To prevent loss of data, AltOS provides receive and transmit + fifos of 32 characters each. + + + AltOS serial functions + + ao_serial_getchar + + +char +ao_serial_getchar(void); + + + Returns the next character from the receive fifo, blocking + until a character is received if the fifo is empty. + + + + + ao_serial_putchar + + +void +ao_serial_putchar(char c); + + + Adds a character to the transmit fifo, blocking if the + fifo is full. Starts transmitting characters. + + + + + ao_serial_drain + + +void +ao_serial_drain(void); + + + Blocks until the transmit fifo is empty. Used internally + when changing serial speeds. + + + + + ao_serial_set_speed + + +void +ao_serial_set_speed(uint8_t speed); + + + Changes the serial baud rate to one of + AO_SERIAL_SPEED_4800, AO_SERIAL_SPEED_9600 or + AO_SERIAL_SPEED_57600. This first flushes the transmit + fifo using ao_serial_drain. + + + + + ao_serial_init + + +void +ao_serial_init(void) + + + Initializes the serial peripheral. Call this from 'main' + before jumping to ao_start_scheduler. The default speed + setting is AO_SERIAL_SPEED_4800. + + + + + + + CC1111 Radio peripheral + + The CC1111 radio transceiver sends and receives digital packets + with forward error correction and detection. The AltOS driver is + fairly specific to the needs of the TeleMetrum and TeleDongle + devices, using it for other tasks may require customization of + the driver itself. There are three basic modes of operation: + + + + Telemetry mode. In this mode, TeleMetrum transmits telemetry + frames at a fixed rate. The frames are of fixed size. This + is strictly a one-way communication from TeleMetrum to + TeleDongle. + + + + + Packet mode. In this mode, the radio is used to create a + reliable duplex byte stream between TeleDongle and + TeleMetrum. This is an asymmetrical protocol with + TeleMetrum only transmitting in response to a packet sent + from TeleDongle. Thus getting data from TeleMetrum to + TeleDongle requires polling. The polling rate is adaptive, + when no data has been received for a while, the rate slows + down. The packets are checked at both ends and invalid + data are ignored. + + + On the TeleMetrum side, the packet link is hooked into the + stdio mechanism, providing an alternate data path for the + command processor. It is enabled when the unit boots up in + 'idle' mode. + + + On the TeleDongle side, the packet link is enabled with a + command; data from the stdio package is forwarded over the + packet link providing a connection from the USB command + stream to the remote TeleMetrum device. + + + + + Radio Direction Finding mode. In this mode, TeleMetrum + constructs a special packet that sounds like an audio tone + when received by a conventional narrow-band FM + receiver. This is designed to provide a beacon to track + the device when other location mechanisms fail. + + + + + + AltOS radio functions + + ao_radio_set_telemetry + + +void +ao_radio_set_telemetry(void); + + + Configures the radio to send or receive telemetry + packets. This includes packet length, modulation scheme and + other RF parameters. It does not include the base frequency + or channel though. Those are set at the time of transmission + or reception, in case the values are changed by the user. + + + + + ao_radio_set_packet + + +void +ao_radio_set_packet(void); + + + Configures the radio to send or receive packet data. This + includes packet length, modulation scheme and other RF + parameters. It does not include the base frequency or + channel though. Those are set at the time of transmission or + reception, in case the values are changed by the user. + + + + + ao_radio_set_rdf + + +void +ao_radio_set_rdf(void); + + + Configures the radio to send RDF 'packets'. An RDF 'packet' + is a sequence of hex 0x55 bytes sent at a base bit rate of + 2kbps using a 5kHz deviation. All of the error correction + and data whitening logic is turned off so that the resulting + modulation is received as a 1kHz tone by a conventional 70cm + FM audio receiver. + + + + + ao_radio_idle + + +void +ao_radio_idle(void); + + + Sets the radio device to idle mode, waiting until it reaches + that state. This will terminate any in-progress transmit or + receive operation. + + + + + ao_radio_get + + +void +ao_radio_get(void); + + + Acquires the radio mutex and then configures the radio + frequency using the global radio calibration and channel + values. + + + + + ao_radio_put + + +void +ao_radio_put(void); + + + Releases the radio mutex. + + + + + ao_radio_abort + + +void +ao_radio_abort(void); + + + Aborts any transmission or reception process by aborting the + associated DMA object and calling ao_radio_idle to terminate + the radio operation. + + + + + + AltOS radio telemetry functions + + In telemetry mode, you can send or receive a telemetry + packet. The data from receiving a packet also includes the RSSI + and status values supplied by the receiver. These are added + after the telemetry data. + + + ao_radio_send + + +void +ao_radio_send(__xdata struct ao_telemetry *telemetry); + + + This sends the specific telemetry packet, waiting for the + transmission to complete. The radio must have been set to + telemetry mode. This function calls ao_radio_get() before + sending, and ao_radio_put() afterwards, to correctly + serialize access to the radio device. + + + + + ao_radio_recv + + +void +ao_radio_recv(__xdata struct ao_radio_recv *radio); + + + This blocks waiting for a telemetry packet to be received. + The radio must have been set to telemetry mode. This + function calls ao_radio_get() before receiving, and + ao_radio_put() afterwards, to correctly serialize access + to the radio device. This returns non-zero if a packet was + received, or zero if the operation was aborted (from some + other task calling ao_radio_abort()). + + + + + + AltOS radio direction finding function + + In radio direction finding mode, there's just one function to + use + + + ao_radio_rdf + + +void +ao_radio_rdf(int ms); + + + This sends an RDF packet lasting for the specified amount + of time. The maximum length is 1020 ms. + + + + + + Packet mode functions + + Packet mode is asymmetrical and is configured at compile time + for either master or slave mode (but not both). The basic I/O + functions look the same at both ends, but the internals are + different, along with the initialization steps. + + + ao_packet_putchar + + +void +ao_packet_putchar(char c); + + + If the output queue is full, this first blocks waiting for + that data to be delivered. Then, queues a character for + packet transmission. On the master side, this will + transmit a packet if the output buffer is full. On the + slave side, any pending data will be sent the next time + the master polls for data. + + + + + ao_packet_pollchar + + +char +ao_packet_pollchar(void); + + + This returns a pending input character if available, + otherwise returns AO_READ_AGAIN. On the master side, if + this empties the buffer, it triggers a poll for more data. + + + + + ao_packet_slave_start + + +void +ao_packet_slave_start(void); + + + This is available only on the slave side and starts a task + to listen for packet data. + + + + + ao_packet_slave_stop + + +void +ao_packet_slave_stop(void); + + + Disables the packet slave task, stopping the radio receiver. + + + + + ao_packet_slave_init + + +void +ao_packet_slave_init(void); + + + Adds the packet stdio functions to the stdio package so + that when packet slave mode is enabled, characters will + get send and received through the stdio functions. + + + + + ao_packet_master_init + + +void +ao_packet_master_init(void); + + + Adds the 'p' packet forward command to start packet mode. + + + + + +
-- cgit v1.2.3 From c7119c21baa9d4ca681975b8613ade6593f65577 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Wed, 24 Nov 2010 02:11:36 +1000 Subject: altosui: don't switch away from user selected tab --- ao-tools/altosui/AltosFlightUI.java | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 732f7395..b0cc521d 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -47,23 +47,17 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { private AltosFlightStatus flightStatus; private AltosInfoTable flightInfo; - static final int tab_pad = 1; - static final int tab_ascent = 2; - static final int tab_descent = 3; - static final int tab_landed = 4; - - int cur_tab = 0; - boolean exit_on_close = false; - int which_tab(AltosState state) { + JComponent cur_tab = null; + JComponent which_tab(AltosState state) { if (state.state < Altos.ao_flight_boost) - return tab_pad; + return pad; if (state.state <= Altos.ao_flight_coast) - return tab_ascent; + return ascent; if (state.state <= Altos.ao_flight_main) - return tab_descent; - return tab_landed; + return descent; + return landed; } void stop_display() { @@ -90,24 +84,14 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { } public void show(AltosState state, int crc_errors) { - int tab = which_tab(state); + JComponent tab = which_tab(state); pad.show(state, crc_errors); ascent.show(state, crc_errors); descent.show(state, crc_errors); landed.show(state, crc_errors); if (tab != cur_tab) { - switch (tab) { - case tab_pad: - pane.setSelectedComponent(pad); - break; - case tab_ascent: - pane.setSelectedComponent(ascent); - break; - case tab_descent: - pane.setSelectedComponent(descent); - break; - case tab_landed: - pane.setSelectedComponent(landed); + if (cur_tab == pane.getSelectedComponent()) { + pane.setSelectedComponent(tab); } cur_tab = tab; } -- cgit v1.2.3 From 84cd5d42d8b5659463544fe2a400758b56478609 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Wed, 24 Nov 2010 02:13:32 +1000 Subject: altosui: sitemap uses rocket gps if no pad gps --- ao-tools/altosui/AltosSiteMap.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 80970605..d4a4cbf4 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -230,16 +230,19 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { // if insufficient gps data, nothing to update if (state.gps == null) return; - if (state.pad_lat == 0 && state.pad_lon == 0) + if (!state.gps.locked && state.gps.nsat < 4) return; - if (!state.gps.locked) { - if (state.gps.nsat < 4) - return; - } if (!initialised) { - initMaps(state.pad_lat, state.pad_lon); - initialised = true; + if (state.pad_lat != 0 || state.pad_lon != 0) { + initMaps(state.pad_lat, state.pad_lon); + initialised = true; + } else if (state.gps.lat != 0 || state.gps.lon != 0) { + initMaps(state.gps.lat, state.gps.lon); + initialised = true; + } else { + return; + } } final Point2D.Double pt = pt(state.gps.lat, state.gps.lon); -- cgit v1.2.3 From 853b7112e34212040c4cb7289f9cfdb2f3ea9f90 Mon Sep 17 00:00:00 2001 From: Bdale Garbee Date: Tue, 23 Nov 2010 18:53:18 -0700 Subject: merge Keith's AltosUI documention into "the big book" --- doc/Makefile | 4 +- doc/altosui-doc.xsl | 596 ------------------ doc/telemetrum-doc.xsl | 1629 ++++++++++++++++++++++++++++++++---------------- 3 files changed, 1099 insertions(+), 1130 deletions(-) delete mode 100644 doc/altosui-doc.xsl diff --git a/doc/Makefile b/doc/Makefile index 52934290..65917ea2 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -2,8 +2,8 @@ # http://docbook.sourceforge.net/release/xsl/current/README # -HTML=telemetrum-doc.html altosui-doc.html altos.html -PDF=telemetrum-doc.pdf altosui-doc.pdf altos.pdf +HTML=telemetrum-doc.html altos.html +PDF=telemetrum-doc.pdf altos.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 diff --git a/doc/altosui-doc.xsl b/doc/altosui-doc.xsl deleted file mode 100644 index 4a1f43b5..00000000 --- a/doc/altosui-doc.xsl +++ /dev/null @@ -1,596 +0,0 @@ - - - - - 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 - - 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. - - - - Fire Igniter - - - -
\ No newline at end of file diff --git a/doc/telemetrum-doc.xsl b/doc/telemetrum-doc.xsl index b7963aec..6be23e7f 100644 --- a/doc/telemetrum-doc.xsl +++ b/doc/telemetrum-doc.xsl @@ -27,6 +27,11 @@ + + 0.3 + 23 November 2010 + New section on AltosUI mostly by Keith + 0.2 18 July 2010 @@ -118,12 +123,12 @@ When you have successfully installed the software suite (either from compiled source code or as the pre-built Debian package) you will have 10 or so executable programs all of which have names beginning - with 'ao-'. + with 'ao-'. ('ao-view' is the lone GUI-based program, the rest are command-line oriented.) You will also have man pages, that give you basic info - on each program. + on each program. You will also get this documentation in two file types in the doc/ -directory, telemetrum-doc.pdf and telemetrum-doc.html. + directory, telemetrum-doc.pdf and telemetrum-doc.html. Finally you will have a couple control files that allow the ao-view GUI-based program to appear in your menu of programs (under the 'Internet' category). @@ -133,7 +138,7 @@ directory, telemetrum-doc.pdf and telemetrum-doc.html. with using USB ports. The first thing you should try after getting both units plugged into to your computer's usb port(s) is to run 'ao-list' from a terminal-window to see what port-device-name each - device has been assigned by the operating system. + device has been assigned by the operating system. You will need this information to access the devices via their respective on-board firmware and data using other command line programs in the AltOS software suite. @@ -158,7 +163,7 @@ directory, telemetrum-doc.pdf and telemetrum-doc.html. Both TeleMetrum and TeleDongle share the concept of a two level command set in their firmware. - The first layer has several single letter commands. Once + The first layer has several single letter commands. Once you are using 'cu' (or 'cutecom') sending (typing) a '?' returns a full list of these commands. The second level are configuration sub-commands accessed @@ -177,7 +182,7 @@ directory, telemetrum-doc.pdf and telemetrum-doc.html. use 'N0CALL' which is cute, but not exactly legal! Spend a few minutes getting comfortable with the units, their firmware, and 'cu' (or possibly 'cutecom'). - For instance, try to send + For instance, try to send (type) a 'c r 2' and verify the channel change by sending a 'c s'. Verify you can connect and disconnect from the units while in your terminal program by sending the escape-disconnect mentioned above. @@ -250,7 +255,7 @@ directory, telemetrum-doc.pdf and telemetrum-doc.html. As for ao-view.... some things are in the menu but don't do anything very useful. The developers have stopped working on ao-view to focus on a new, cross-platform ground station program. So ao-view may or - may not be updated in the future. Mostly you just use + may not be updated in the future. Mostly you just use the Log and Device menus. It has a wonderful display of the incoming flight data and I am sure you will enjoy what it has to say to you once you enable the voice output! @@ -299,611 +304,1171 @@ directory, telemetrum-doc.pdf and telemetrum-doc.html. Live telemetry is written to file(s) whenever 'ao-view' is connected to the TeleDongle. The file area defaults to ~/altos but is easily changed using the menus in 'ao-view'. The files that - are written end in '.telem'. The after-flight + are written end in '.telem'. The after-flight data-dumped files will end in .eeprom and represent continuous data unlike the rf-linked .telem files that are subject to the turnarounds/data-packaging time slots in the half-duplex rf data path. See the above instructions on what and how to save the eeprom stored data after physically retrieving your TeleMetrum. Make sure to save - the on-board data after each flight, as the current firmware will - over-write any previous flight data during a new flight. + the on-board data after each flight, as the current firmware will + over-write any previous flight data during a new flight. + + +
+ + Specifications + + + + Recording altimeter for model rocketry. + + + + + Supports dual deployment (can fire 2 ejection charges). + + + + + 70cm ham-band transceiver for telemetry downlink. + + + + + Barometric pressure sensor good to 45k feet MSL. + + + + + 1-axis high-g accelerometer for motor characterization, capable of + +/- 50g using default part. + + + + + On-board, integrated GPS receiver with 5hz update rate capability. + + + + + On-board 1 megabyte non-volatile memory for flight data storage. + + + + + USB interface for battery charging, configuration, and data recovery. + + + + + Fully integrated support for LiPo rechargeable batteries. + + + + + Uses LiPo to fire e-matches, support for optional separate pyro + battery if needed. + + + + + 2.75 x 1 inch board designed to fit inside 29mm airframe coupler tube. + + + + + + Handling Precautions + + TeleMetrum is a sophisticated electronic device. When handled gently and + properly installed in an airframe, it will deliver impressive results. + However, like all electronic devices, there are some precautions you + must take. + + + The Lithium Polymer rechargeable batteries used with TeleMetrum have an + extraordinary power density. This is great because we can fly with + much less battery mass than if we used alkaline batteries or previous + generation rechargeable batteries... but if they are punctured + or their leads are allowed to short, they can and will release their + energy very rapidly! + Thus we recommend that you take some care when handling our batteries + and consider giving them some extra protection in your airframe. We + often wrap them in suitable scraps of closed-cell packing foam before + strapping them down, for example. + + + The TeleMetrum barometric sensor is sensitive to sunlight. In normal + mounting situations, it and all of the other surface mount components + are "down" towards whatever the underlying mounting surface is, so + this is not normally a problem. Please consider this, though, when + designing an installation, for example, in a 29mm airframe with a + see-through plastic payload bay. + + + The TeleMetrum barometric sensor sampling port must be able to + "breathe", + both by not being covered by foam or tape or other materials that might + directly block the hole on the top of the sensor, but also by having a + suitable static vent to outside air. + + + As with all other rocketry electronics, TeleMetrum must be protected + from exposure to corrosive motor exhaust and ejection charge gasses. + + + + Hardware Overview + + TeleMetrum is a 1 inch by 2.75 inch circuit board. It was designed to + fit inside coupler for 29mm airframe tubing, but using it in a tube that + small in diameter may require some creativity in mounting and wiring + to succeed! The default 1/4 + wave UHF wire antenna attached to the center of the nose-cone end of + the board is about 7 inches long, and wiring for a power switch and + the e-matches for apogee and main ejection charges depart from the + fin can end of the board. Given all this, an ideal "simple" avionics + bay for TeleMetrum should have at least 10 inches of interior length. + + + A typical TeleMetrum installation using the on-board GPS antenna and + default wire UHF antenna involves attaching only a suitable + Lithium Polymer battery, a single pole switch for power on/off, and + two pairs of wires connecting e-matches for the apogee and main ejection + charges. + + + By default, we use the unregulated output of the LiPo battery directly + to fire ejection charges. This works marvelously with standard + low-current e-matches like the J-Tek from MJG Technologies, and with + Quest Q2G2 igniters. However, if you + want or need to use a separate pyro battery, you can do so by adding + a second 2mm connector to position B2 on the board and cutting the + thick pcb trace connecting the LiPo battery to the pyro circuit between + the two silk screen marks on the surface mount side of the board shown + here [insert photo] + + + We offer two choices of pyro and power switch connector, or you can + choose neither and solder wires directly to the board. All three choices + are reasonable depending on the constraints of your airframe. Our + favorite option when there is sufficient room above the board is to use + the Tyco pin header with polarization and locking. If you choose this + option, you crimp individual wires for the power switch and e-matches + into a mating connector, and installing and removing the TeleMetrum + board from an airframe is as easy as plugging or unplugging two + connectors. If the airframe will not support this much height or if + you want to be able to directly attach e-match leads to the board, we + offer a screw terminal block. This is very similar to what most other + altimeter vendors provide and so may be the most familiar option. + You'll need a very small straight blade screwdriver to connect + and disconnect the board in this case, such as you might find in a + jeweler's screwdriver set. Finally, you can forego both options and + solder wires directly to the board, which may be the best choice for + minimum diameter and/or minimum mass designs. + + + For most airframes, the integrated GPS antenna and wire UHF antenna are + a great combination. However, if you are installing in a carbon-fiber + electronics bay which is opaque to RF signals, you may need to use + off-board external antennas instead. In this case, you can order + TeleMetrum with an SMA connector for the UHF antenna connection, and + you can unplug the integrated GPS antenna and select an appropriate + off-board GPS antenna with cable terminating in a U.FL connector. + + + + System Operation +
+ Firmware Modes + + The AltOS firmware build for TeleMetrum has two fundamental modes, + "idle" and "flight". Which of these modes the firmware operates in + is determined by the orientation of the rocket (well, actually the + board, of course...) at the time power is switched on. If the rocket + is "nose up", then TeleMetrum assumes it's on a rail or rod being + prepared for launch, so the firmware chooses flight mode. However, + if the rocket is more or less horizontal, the firmware instead enters + idle mode. + + + At power on, you will hear three beeps + ("S" in Morse code for startup) and then a pause while + TeleMetrum completes initialization and self tests, and decides which + mode to enter next. + + + In flight or "pad" mode, TeleMetrum turns on the GPS system, + engages the flight + state machine, goes into transmit-only mode on the RF link sending + telemetry, and waits for launch to be detected. Flight mode is + indicated by an audible "di-dah-dah-dit" ("P" for pad) on the + beeper, followed by + beeps indicating the state of the pyrotechnic igniter continuity. + One beep indicates apogee continuity, two beeps indicate + main continuity, three beeps indicate both apogee and main continuity, + and one longer "brap" sound indicates no continuity. For a dual + deploy flight, make sure you're getting three beeps before launching! + For apogee-only or motor eject flights, do what makes sense. + + + In idle mode, you will hear an audible "di-dit" ("I" for idle), and + the normal flight state machine is disengaged, thus + no ejection charges will fire. TeleMetrum also listens on the RF + link when in idle mode for packet mode requests sent from TeleDongle. + Commands can be issued to a TeleMetrum in idle mode over either + USB or the RF link equivalently. + Idle mode is useful for configuring TeleMetrum, for extracting data + from the on-board storage chip after flight, and for ground testing + pyro charges. + + + One "neat trick" of particular value when TeleMetrum is used with very + large airframes, is that you can power the board up while the rocket + is horizontal, such that it comes up in idle mode. Then you can + raise the airframe to launch position, use a TeleDongle to open + a packet connection, and issue a 'reset' command which will cause + TeleMetrum to reboot, realize it's now nose-up, and thus choose + flight mode. This is much safer than standing on the top step of a + rickety step-ladder or hanging off the side of a launch tower with + a screw-driver trying to turn on your avionics before installing + igniters! + +
+
+ GPS + + TeleMetrum includes a complete GPS receiver. See a later section for + a brief explanation of how GPS works that will help you understand + the information in the telemetry stream. The bottom line is that + the TeleMetrum GPS receiver needs to lock onto at least four + satellites to obtain a solid 3 dimensional position fix and know + what time it is! + + + TeleMetrum provides backup power to the GPS chip any time a LiPo + battery is connected. This allows the receiver to "warm start" on + the launch rail much faster than if every power-on were a "cold start" + for the GPS receiver. In typical operations, powering up TeleMetrum + on the flight line in idle mode while performing final airframe + preparation will be sufficient to allow the GPS receiver to cold + start and acquire lock. Then the board can be powered down during + RSO review and installation on a launch rod or rail. When the board + is turned back on, the GPS system should lock very quickly, typically + long before igniter installation and return to the flight line are + complete. + +
+
+ Ground Testing + + An important aspect of preparing a rocket using electronic deployment + for flight is ground testing the recovery system. Thanks + to the bi-directional RF link central to the Altus Metrum system, + this can be accomplished in a TeleMetrum-equipped rocket without as + much work as you may be accustomed to with other systems. It can + even be fun! + + + Just prep the rocket for flight, then power up TeleMetrum while the + airframe is horizontal. This will cause the firmware to go into + "idle" mode, in which the normal flight state machine is disabled and + charges will not fire without manual command. Then, establish an + RF packet connection from a TeleDongle-equipped computer using the + P command from a safe distance. You can now command TeleMetrum to + fire the apogee or main charges to complete your testing. + + + In order to reduce the chance of accidental firing of pyrotechnic + charges, the command to fire a charge is intentionally somewhat + difficult to type, and the built-in help is slightly cryptic to + prevent accidental echoing of characters from the help text back at + the board from firing a charge. The command to fire the apogee + drogue charge is 'i DoIt drogue' and the command to fire the main + charge is 'i DoIt main'. + +
+
+ Radio Link + + The chip our boards are based on incorporates an RF transceiver, but + it's not a full duplex system... each end can only be transmitting or + receiving at any given moment. So we had to decide how to manage the + link. + + + By design, TeleMetrum firmware listens for an RF connection when + it's in "idle mode" (turned on while the rocket is horizontal), which + allows us to use the RF link to configure the rocket, do things like + ejection tests, and extract data after a flight without having to + crack open the airframe. However, when the board is in "flight + mode" (turned on when the rocket is vertical) the TeleMetrum only + transmits and doesn't listen at all. That's because we want to put + ultimate priority on event detection and getting telemetry out of + the rocket and out over + the RF link in case the rocket crashes and we aren't able to extract + data later... + + + We don't use a 'normal packet radio' mode because they're just too + inefficient. The GFSK modulation we use is just FSK with the + baseband pulses passed through a + Gaussian filter before they go into the modulator to limit the + transmitted bandwidth. When combined with the hardware forward error + correction support in the cc1111 chip, this allows us to have a very + robust 38.4 kilobit data link with only 10 milliwatts of transmit power, + a whip antenna in the rocket, and a hand-held Yagi on the ground. We've + had flights to above 21k feet AGL with good reception, and calculations + suggest we should be good to well over 40k feet AGL with a 5-element yagi on + the ground. We hope to fly boards to higher altitudes soon, and would + of course appreciate customer feedback on performance in higher + altitude flights! + +
+
+ Configurable Parameters + + Configuring a TeleMetrum board for flight is very simple. Because we + have both acceleration and pressure sensors, there is no need to set + a "mach delay", for example. The few configurable parameters can all + be set using a simple terminal program over the USB port or RF link + via TeleDongle. + +
+ Radio Channel + + Our firmware supports 10 channels. The default channel 0 corresponds + to a center frequency of 434.550 Mhz, and channels are spaced every + 100 khz. Thus, channel 1 is 434.650 Mhz, and channel 9 is 435.550 Mhz. + At any given launch, we highly recommend coordinating who will use + each channel and when to avoid interference. And of course, both + TeleMetrum and TeleDongle must be configured to the same channel to + successfully communicate with each other. + + + To set the radio channel, use the 'c r' command, like 'c r 3' to set + channel 3. + As with all 'c' sub-commands, follow this with a 'c w' to write the + change to the parameter block in the on-board DataFlash chip on + your TeleMetrum board if you want the change to stay in place across reboots. + +
+
+ Apogee Delay + + Apogee delay is the number of seconds after TeleMetrum detects flight + apogee that the drogue charge should be fired. In most cases, this + should be left at the default of 0. However, if you are flying + redundant electronics such as for an L3 certification, you may wish + to set one of your altimeters to a positive delay so that both + primary and backup pyrotechnic charges do not fire simultaneously. + + + To set the apogee delay, use the [FIXME] command. + As with all 'c' sub-commands, follow this with a 'c w' to write the + change to the parameter block in the on-board DataFlash chip. + + + Please note that the TeleMetrum apogee detection algorithm always + fires a fraction of a second *after* apogee. If you are also flying + an altimeter like the PerfectFlite MAWD, which only supports selecting + 0 or 1 seconds of apogee delay, you may wish to set the MAWD to 0 + seconds delay and set the TeleMetrum to fire your backup 2 or 3 + seconds later to avoid any chance of both charges firing + simultaneously. We've flown several airframes this way quite happily, + including Keith's successful L3 cert.
- - - Specifications +
+ Main Deployment Altitude + + By default, TeleMetrum will fire the main deployment charge at an + elevation of 250 meters (about 820 feet) above ground. We think this + is a good elevation for most airframes, but feel free to change this + to suit. In particular, if you are flying two altimeters, you may + wish to set the + deployment elevation for the backup altimeter to be something lower + than the primary so that both pyrotechnic charges don't fire + simultaneously. + + + To set the main deployment altitude, use the [FIXME] command. + As with all 'c' sub-commands, follow this with a 'c w' to write the + change to the parameter block in the on-board DataFlash chip. + +
+
+
+ Calibration + + There are only two calibrations required for a TeleMetrum board, and + only one for TeleDongle. + +
+ Radio Frequency + + The radio frequency is synthesized from a clock based on the 48 Mhz + crystal on the board. The actual frequency of this oscillator must be + measured to generate a calibration constant. While our GFSK modulation + bandwidth is wide enough to allow boards to communicate even when + their oscillators are not on exactly the same frequency, performance + is best when they are closely matched. + Radio frequency calibration requires a calibrated frequency counter. + Fortunately, once set, the variation in frequency due to aging and + temperature changes is small enough that re-calibration by customers + should generally not be required. + + + To calibrate the radio frequency, connect the UHF antenna port to a + frequency counter, set the board to channel 0, and use the 'C' + command to generate a CW carrier. Wait for the transmitter temperature + to stabilize and the frequency to settle down. + Then, divide 434.550 Mhz by the + measured frequency and multiply by the current radio cal value show + in the 'c s' command. For an unprogrammed board, the default value + is 1186611. Take the resulting integer and program it using the 'c f' + command. Testing with the 'C' command again should show a carrier + within a few tens of Hertz of the intended frequency. + As with all 'c' sub-commands, follow this with a 'c w' to write the + change to the parameter block in the on-board DataFlash chip. + +
+
+ Accelerometer + + The accelerometer we use has its own 5 volt power supply and + the output must be passed through a resistive voltage divider to match + the input of our 3.3 volt ADC. This means that unlike the barometric + sensor, the output of the acceleration sensor is not ratiometric to + the ADC converter, and calibration is required. We also support the + use of any of several accelerometers from a Freescale family that + includes at least +/- 40g, 50g, 100g, and 200g parts. Using gravity, + a simple 2-point calibration yields acceptable results capturing both + the different sensitivities and ranges of the different accelerometer + parts and any variation in power supply voltages or resistor values + in the divider network. + + + To calibrate the acceleration sensor, use the 'c a 0' command. You + will be prompted to orient the board vertically with the UHF antenna + up and press a key, then to orient the board vertically with the + UHF antenna down and press a key. + As with all 'c' sub-commands, follow this with a 'c w' to write the + change to the parameter block in the on-board DataFlash chip. + + + The +1g and -1g calibration points are included in each telemetry + frame and are part of the header extracted by ao-dumplog after flight. + Note that we always store and return raw ADC samples for each + sensor... nothing is permanently "lost" or "damaged" if the + calibration is poor. + +
+
+
+ + + AltosUI + + 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. + - Recording altimeter for model rocketry. + Save Flight Data—Recover flight data from the rocket without + opening it up. - Supports dual deployment (can fire 2 ejection charges). + 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. - 70cm ham-band transceiver for telemetry downlink. + 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: + + - - Barometric pressure sensor good to 45k feet MSL. - + The TeleMetrum callsign - - 1-axis high-g accelerometer for motor characterization, capable of - +/- 50g using default part. + The TeleMetrum serial number + + + The flight number. Each TeleMetrum remembers how many + times it has flown. - On-board, integrated GPS receiver with 5hz update rate capability. + The rocket flight state. Each flight passes through several + states including Pad, Boost, Fast, Coast, Drogue, Main and + Landed. - On-board 1 megabyte non-volatile memory for flight data storage. + 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: + + - USB interface for battery charging, configuration, and data recovery. + 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. - Fully integrated support for LiPo rechargeable batteries. + Reset. This resets the dialog to the most recently saved values, + erasing any changes you have made. - Uses LiPo to fire e-matches, support for optional separate pyro - battery if needed. + Reboot. This reboots the TeleMetrum device. Use this to + switch from idle to pad mode by rebooting once the rocket is + oriented for flight. - 2.75 x 1 inch board designed to fit inside 29mm airframe coupler tube. + Close. This closes the dialog. Any unsaved changes will be + lost. - - - Handling Precautions - - TeleMetrum is a sophisticated electronic device. When handled gently and - properly installed in an airframe, it will deliver impressive results. - However, like all electronic devices, there are some precautions you - must take. - - - The Lithium Polymer rechargeable batteries used with TeleMetrum have an - extraordinary power density. This is great because we can fly with - much less battery mass than if we used alkaline batteries or previous - generation rechargeable batteries... but if they are punctured - or their leads are allowed to short, they can and will release their - energy very rapidly! - Thus we recommend that you take some care when handling our batteries - and consider giving them some extra protection in your airframe. We - often wrap them in suitable scraps of closed-cell packing foam before - strapping them down, for example. - - - The TeleMetrum barometric sensor is sensitive to sunlight. In normal - mounting situations, it and all of the other surface mount components - are "down" towards whatever the underlying mounting surface is, so - this is not normally a problem. Please consider this, though, when - designing an installation, for example, in a 29mm airframe with a - see-through plastic payload bay. - - - The TeleMetrum barometric sensor sampling port must be able to - "breathe", - both by not being covered by foam or tape or other materials that might - directly block the hole on the top of the sensor, but also by having a - suitable static vent to outside air. - - - As with all other rocketry electronics, TeleMetrum must be protected - from exposure to corrosive motor exhaust and ejection charge gasses. - - - - Hardware Overview - - TeleMetrum is a 1 inch by 2.75 inch circuit board. It was designed to - fit inside coupler for 29mm airframe tubing, but using it in a tube that - small in diameter may require some creativity in mounting and wiring - to succeed! The default 1/4 - wave UHF wire antenna attached to the center of the nose-cone end of - the board is about 7 inches long, and wiring for a power switch and - the e-matches for apogee and main ejection charges depart from the - fin can end of the board. Given all this, an ideal "simple" avionics - bay for TeleMetrum should have at least 10 inches of interior length. - - - A typical TeleMetrum installation using the on-board GPS antenna and - default wire UHF antenna involves attaching only a suitable - Lithium Polymer battery, a single pole switch for power on/off, and - two pairs of wires connecting e-matches for the apogee and main ejection - charges. - - - By default, we use the unregulated output of the LiPo battery directly - to fire ejection charges. This works marvelously with standard - low-current e-matches like the J-Tek from MJG Technologies, and with - Quest Q2G2 igniters. However, if you - want or need to use a separate pyro battery, you can do so by adding - a second 2mm connector to position B2 on the board and cutting the - thick pcb trace connecting the LiPo battery to the pyro circuit between - the two silk screen marks on the surface mount side of the board shown - here [insert photo] - - - We offer two choices of pyro and power switch connector, or you can - choose neither and solder wires directly to the board. All three choices - are reasonable depending on the constraints of your airframe. Our - favorite option when there is sufficient room above the board is to use - the Tyco pin header with polarization and locking. If you choose this - option, you crimp individual wires for the power switch and e-matches - into a mating connector, and installing and removing the TeleMetrum - board from an airframe is as easy as plugging or unplugging two - connectors. If the airframe will not support this much height or if - you want to be able to directly attach e-match leads to the board, we - offer a screw terminal block. This is very similar to what most other - altimeter vendors provide and so may be the most familiar option. - You'll need a very small straight blade screwdriver to connect - and disconnect the board in this case, such as you might find in a - jeweler's screwdriver set. Finally, you can forego both options and - solder wires directly to the board, which may be the best choice for - minimum diameter and/or minimum mass designs. - - - For most airframes, the integrated GPS antenna and wire UHF antenna are - a great combination. However, if you are installing in a carbon-fiber - electronics bay which is opaque to RF signals, you may need to use - off-board external antennas instead. In this case, you can order - TeleMetrum with an SMA connector for the UHF antenna connection, and - you can unplug the integrated GPS antenna and select an appropriate - off-board GPS antenna with cable terminating in a U.FL connector. - - - - Operation + + The rest of the dialog contains the parameters to be configured. +
- Firmware Modes - - The AltOS firmware build for TeleMetrum has two fundamental modes, - "idle" and "flight". Which of these modes the firmware operates in - is determined by the orientation of the rocket (well, actually the - board, of course...) at the time power is switched on. If the rocket - is "nose up", then TeleMetrum assumes it's on a rail or rod being - prepared for launch, so the firmware chooses flight mode. However, - if the rocket is more or less horizontal, the firmware instead enters - idle mode. - - - At power on, you will hear three beeps - ("S" in Morse code for startup) and then a pause while - TeleMetrum completes initialization and self tests, and decides which - mode to enter next. - - - In flight or "pad" mode, TeleMetrum turns on the GPS system, - engages the flight - state machine, goes into transmit-only mode on the RF link sending - telemetry, and waits for launch to be detected. Flight mode is - indicated by an audible "di-dah-dah-dit" ("P" for pad) on the - beeper, followed by - beeps indicating the state of the pyrotechnic igniter continuity. - One beep indicates apogee continuity, two beeps indicate - main continuity, three beeps indicate both apogee and main continuity, - and one longer "brap" sound indicates no continuity. For a dual - deploy flight, make sure you're getting three beeps before launching! - For apogee-only or motor eject flights, do what makes sense. - - - In idle mode, you will hear an audible "di-dit" ("I" for idle), and - the normal flight state machine is disengaged, thus - no ejection charges will fire. TeleMetrum also listens on the RF - link when in idle mode for packet mode requests sent from TeleDongle. - Commands can be issued to a TeleMetrum in idle mode over either - USB or the RF link equivalently. - Idle mode is useful for configuring TeleMetrum, for extracting data - from the on-board storage chip after flight, and for ground testing - pyro charges. - - - One "neat trick" of particular value when TeleMetrum is used with very - large airframes, is that you can power the board up while the rocket - is horizontal, such that it comes up in idle mode. Then you can - raise the airframe to launch position, use a TeleDongle to open - a packet connection, and issue a 'reset' command which will cause - TeleMetrum to reboot, realize it's now nose-up, and thus choose - flight mode. This is much safer than standing on the top step of a - rickety step-ladder or hanging off the side of a launch tower with - a screw-driver trying to turn on your avionics before installing - igniters! + 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.
- GPS - - TeleMetrum includes a complete GPS receiver. See a later section for - a brief explanation of how GPS works that will help you understand - the information in the telemetry stream. The bottom line is that - the TeleMetrum GPS receiver needs to lock onto at least four - satellites to obtain a solid 3 dimensional position fix and know - what time it is! - - - TeleMetrum provides backup power to the GPS chip any time a LiPo - battery is connected. This allows the receiver to "warm start" on - the launch rail much faster than if every power-on were a "cold start" - for the GPS receiver. In typical operations, powering up TeleMetrum - on the flight line in idle mode while performing final airframe - preparation will be sufficient to allow the GPS receiver to cold - start and acquire lock. Then the board can be powered down during - RSO review and installation on a launch rod or rail. When the board - is turned back on, the GPS system should lock very quickly, typically - long before igniter installation and return to the flight line are - complete. + 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.
- Ground Testing + Radio Channel - An important aspect of preparing a rocket using electronic deployment - for flight is ground testing the recovery system. Thanks - to the bi-directional RF link central to the Altus Metrum system, - this can be accomplished in a TeleMetrum-equipped rocket without as - much work as you may be accustomed to with other systems. It can - even be fun! + 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 - Just prep the rocket for flight, then power up TeleMetrum while the - airframe is horizontal. This will cause the firmware to go into - "idle" mode, in which the normal flight state machine is disabled and - charges will not fire without manual command. Then, establish an - RF packet connection from a TeleDongle-equipped computer using the - P command from a safe distance. You can now command TeleMetrum to - fire the apogee or main charges to complete your testing. + 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 - In order to reduce the chance of accidental firing of pyrotechnic - charges, the command to fire a charge is intentionally somewhat - difficult to type, and the built-in help is slightly cryptic to - prevent accidental echoing of characters from the help text back at - the board from firing a charge. The command to fire the apogee - drogue charge is 'i DoIt drogue' and the command to fire the main - charge is 'i DoIt main'. + 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. +
- Radio Link - - The chip our boards are based on incorporates an RF transceiver, but - it's not a full duplex system... each end can only be transmitting or - receiving at any given moment. So we had to decide how to manage the - link. - - - By design, TeleMetrum firmware listens for an RF connection when - it's in "idle mode" (turned on while the rocket is horizontal), which - allows us to use the RF link to configure the rocket, do things like - ejection tests, and extract data after a flight without having to - crack open the airframe. However, when the board is in "flight - mode" (turned on when the rocket is vertical) the TeleMetrum only - transmits and doesn't listen at all. That's because we want to put - ultimate priority on event detection and getting telemetry out of - the rocket and out over - the RF link in case the rocket crashes and we aren't able to extract - data later... - - - We don't use a 'normal packet radio' mode because they're just too - inefficient. The GFSK modulation we use is just FSK with the - baseband pulses passed through a - Gaussian filter before they go into the modulator to limit the - transmitted bandwidth. When combined with the hardware forward error - correction support in the cc1111 chip, this allows us to have a very - robust 38.4 kilobit data link with only 10 milliwatts of transmit power, - a whip antenna in the rocket, and a hand-held Yagi on the ground. We've - had flights to above 21k feet AGL with good reception, and calculations - suggest we should be good to well over 40k feet AGL with a 5-element yagi on - the ground. We hope to fly boards to higher altitudes soon, and would - of course appreciate customer feedback on performance in higher - altitude flights! + 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 + + +
- Configurable Parameters + Log Directory - Configuring a TeleMetrum board for flight is very simple. Because we - have both acceleration and pressure sensors, there is no need to set - a "mach delay", for example. The few configurable parameters can all - be set using a simple terminal program over the USB port or RF link - via TeleDongle. + 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. -
- Radio Channel - - Our firmware supports 10 channels. The default channel 0 corresponds - to a center frequency of 434.550 Mhz, and channels are spaced every - 100 khz. Thus, channel 1 is 434.650 Mhz, and channel 9 is 435.550 Mhz. - At any given launch, we highly recommend coordinating who will use - each channel and when to avoid interference. And of course, both - TeleMetrum and TeleDongle must be configured to the same channel to - successfully communicate with each other. - - - To set the radio channel, use the 'c r' command, like 'c r 3' to set - channel 3. - As with all 'c' sub-commands, follow this with a 'c w' to write the - change to the parameter block in the on-board DataFlash chip on - your TeleMetrum board if you want the change to stay in place across reboots. - -
-
- Apogee Delay - - Apogee delay is the number of seconds after TeleMetrum detects flight - apogee that the drogue charge should be fired. In most cases, this - should be left at the default of 0. However, if you are flying - redundant electronics such as for an L3 certification, you may wish - to set one of your altimeters to a positive delay so that both - primary and backup pyrotechnic charges do not fire simultaneously. - - - To set the apogee delay, use the [FIXME] command. - As with all 'c' sub-commands, follow this with a 'c w' to write the - change to the parameter block in the on-board DataFlash chip. - - - Please note that the TeleMetrum apogee detection algorithm always - fires a fraction of a second *after* apogee. If you are also flying - an altimeter like the PerfectFlite MAWD, which only supports selecting - 0 or 1 seconds of apogee delay, you may wish to set the MAWD to 0 - seconds delay and set the TeleMetrum to fire your backup 2 or 3 - seconds later to avoid any chance of both charges firing - simultaneously. We've flown several airframes this way quite happily, - including Keith's successful L3 cert. - -
-
- Main Deployment Altitude - - By default, TeleMetrum will fire the main deployment charge at an - elevation of 250 meters (about 820 feet) above ground. We think this - is a good elevation for most airframes, but feel free to change this - to suit. In particular, if you are flying two altimeters, you may - wish to set the - deployment elevation for the backup altimeter to be something lower - than the primary so that both pyrotechnic charges don't fire - simultaneously. - - - To set the main deployment altitude, use the [FIXME] command. - As with all 'c' sub-commands, follow this with a 'c w' to write the - change to the parameter block in the on-board DataFlash chip. - -
- Calibration + Callsign - There are only two calibrations required for a TeleMetrum board, and - only one for TeleDongle. + 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. -
- Radio Frequency - - The radio frequency is synthesized from a clock based on the 48 Mhz - crystal on the board. The actual frequency of this oscillator must be - measured to generate a calibration constant. While our GFSK modulation - bandwidth is wide enough to allow boards to communicate even when - their oscillators are not on exactly the same frequency, performance - is best when they are closely matched. - Radio frequency calibration requires a calibrated frequency counter. - Fortunately, once set, the variation in frequency due to aging and - temperature changes is small enough that re-calibration by customers - should generally not be required. - - - To calibrate the radio frequency, connect the UHF antenna port to a - frequency counter, set the board to channel 0, and use the 'C' - command to generate a CW carrier. Wait for the transmitter temperature - to stabilize and the frequency to settle down. - Then, divide 434.550 Mhz by the - measured frequency and multiply by the current radio cal value show - in the 'c s' command. For an unprogrammed board, the default value - is 1186611. Take the resulting integer and program it using the 'c f' - command. Testing with the 'C' command again should show a carrier - within a few tens of Hertz of the intended frequency. - As with all 'c' sub-commands, follow this with a 'c w' to write the - change to the parameter block in the on-board DataFlash chip. - -
-
- Accelerometer - - The accelerometer we use has its own 5 volt power supply and - the output must be passed through a resistive voltage divider to match - the input of our 3.3 volt ADC. This means that unlike the barometric - sensor, the output of the acceleration sensor is not ratiometric to - the ADC converter, and calibration is required. We also support the - use of any of several accelerometers from a Freescale family that - includes at least +/- 40g, 50g, 100g, and 200g parts. Using gravity, - a simple 2-point calibration yields acceptable results capturing both - the different sensitivities and ranges of the different accelerometer - parts and any variation in power supply voltages or resistor values - in the divider network. - - - To calibrate the acceleration sensor, use the 'c a 0' command. You - will be prompted to orient the board vertically with the UHF antenna - up and press a key, then to orient the board vertically with the - UHF antenna down and press a key. - As with all 'c' sub-commands, follow this with a 'c w' to write the - change to the parameter block in the on-board DataFlash chip. - - - The +1g and -1g calibration points are included in each telemetry - frame and are part of the header extracted by ao-dumplog after flight. - Note that we always store and return raw ADC samples for each - sensor... nothing is permanently "lost" or "damaged" if the - calibration is poor. - -
- - - Using Altus Metrum Products +
+
+ 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. + +
+
+ Fire Igniter + + +
+
+ + Using Altus Metrum Products +
+ Being Legal + + First off, in the US, you need an [amateur radio license](../Radio) or + other authorization to legally operate the radio transmitters that are part + of our products. +
- Being Legal + In the Rocket - First off, in the US, you need an [amateur radio license](../Radio) or - other authorization to legally operate the radio transmitters that are part - of our products. + In the rocket itself, you just need a [TeleMetrum](../TeleMetrum) board and + a LiPo rechargeable battery. An 860mAh battery weighs less than a 9V + alkaline battery, and will run a [TeleMetrum](../TeleMetrum) for hours. + + + By default, we ship TeleMetrum with a simple wire antenna. If your + electronics bay or the airframe it resides within is made of carbon fiber, + which is opaque to RF signals, you may choose to have an SMA connector + installed so that you can run a coaxial cable to an antenna mounted + elsewhere in the rocket. -
- In the Rocket - - In the rocket itself, you just need a [TeleMetrum](../TeleMetrum) board and - a LiPo rechargeable battery. An 860mAh battery weighs less than a 9V - alkaline battery, and will run a [TeleMetrum](../TeleMetrum) for hours. - - - By default, we ship TeleMetrum with a simple wire antenna. If your - electronics bay or the airframe it resides within is made of carbon fiber, - which is opaque to RF signals, you may choose to have an SMA connector - installed so that you can run a coaxial cable to an antenna mounted - elsewhere in the rocket. - -
-
- On the Ground - - To receive the data stream from the rocket, you need an antenna and short - feedline connected to one of our [TeleDongle](../TeleDongle) units. The - TeleDongle in turn plugs directly into the USB port on a notebook - computer. Because TeleDongle looks like a simple serial port, your computer - does not require special device drivers... just plug it in. - - - Right now, all of our application software is written for Linux. However, - because we understand that many people run Windows or MacOS, we are working - on a new ground station program written in Java that should work on all - operating systems. - - - After the flight, you can use the RF link to extract the more detailed data - logged in the rocket, or you can use a mini USB cable to plug into the - TeleMetrum board directly. Pulling out the data without having to open up - the rocket is pretty cool! A USB cable is also how you charge the LiPo - battery, so you'll want one of those anyway... the same cable used by lots - of digital cameras and other modern electronic stuff will work fine. - - - If your rocket lands out of sight, you may enjoy having a hand-held GPS - receiver, so that you can put in a waypoint for the last reported rocket - position before touch-down. This makes looking for your rocket a lot like - Geo-Cacheing... just go to the waypoint and look around starting from there. - - - You may also enjoy having a ham radio "HT" that covers the 70cm band... you - can use that with your antenna to direction-find the rocket on the ground - the same way you can use a Walston or Beeline tracker. This can be handy - if the rocket is hiding in sage brush or a tree, or if the last GPS position - doesn't get you close enough because the rocket dropped into a canyon, or - the wind is blowing it across a dry lake bed, or something like that... Keith - and Bdale both currently own and use the Yaesu VX-7R at launches. - - - So, to recap, on the ground the hardware you'll need includes: - - - an antenna and feedline - - - a TeleDongle - - - a notebook computer - - - optionally, a handheld GPS receiver - - - optionally, an HT or receiver covering 435 Mhz - - - - - The best hand-held commercial directional antennas we've found for radio - direction finding rockets are from - - Arrow Antennas. - - The 440-3 and 440-5 are both good choices for finding a - TeleMetrum-equipped rocket when used with a suitable 70cm HT. - -
-
- Data Analysis - - Our software makes it easy to log the data from each flight, both the - telemetry received over the RF link during the flight itself, and the more - complete data log recorded in the DataFlash memory on the TeleMetrum - board. Once this data is on your computer, our postflight tools make it - easy to quickly get to the numbers everyone wants, like apogee altitude, - max acceleration, and max velocity. You can also generate and view a - standard set of plots showing the altitude, acceleration, and - velocity of the rocket during flight. And you can even export a data file - useable with Google Maps and Google Earth for visualizing the flight path - in two or three dimensions! - - - Our ultimate goal is to emit a set of files for each flight that can be - published as a web page per flight, or just viewed on your local disk with - a web browser. - -
-
- Future Plans - - In the future, we intend to offer "companion boards" for the rocket that will - plug in to TeleMetrum to collect additional data, provide more pyro channels, - and so forth. A reference design for a companion board will be documented - soon, and will be compatible with open source Arduino programming tools. - - - We are also working on the design of a hand-held ground terminal that will - allow monitoring the rocket's status, collecting data during flight, and - logging data after flight without the need for a notebook computer on the - flight line. Particularly since it is so difficult to read most notebook - screens in direct sunlight, we think this will be a great thing to have. - - - Because all of our work is open, both the hardware designs and the software, - if you have some great idea for an addition to the current Altus Metrum family, - feel free to dive in and help! Or let us know what you'd like to see that - we aren't already working on, and maybe we'll get excited about it too... - -
- - How GPS Works - + On the Ground + + To receive the data stream from the rocket, you need an antenna and short + feedline connected to one of our [TeleDongle](../TeleDongle) units. The + TeleDongle in turn plugs directly into the USB port on a notebook + computer. Because TeleDongle looks like a simple serial port, your computer + does not require special device drivers... just plug it in. + + + Right now, all of our application software is written for Linux. However, + because we understand that many people run Windows or MacOS, we are working + on a new ground station program written in Java that should work on all + operating systems. + - Placeholder. + After the flight, you can use the RF link to extract the more detailed data + logged in the rocket, or you can use a mini USB cable to plug into the + TeleMetrum board directly. Pulling out the data without having to open up + the rocket is pretty cool! A USB cable is also how you charge the LiPo + battery, so you'll want one of those anyway... the same cable used by lots + of digital cameras and other modern electronic stuff will work fine. + + + If your rocket lands out of sight, you may enjoy having a hand-held GPS + receiver, so that you can put in a waypoint for the last reported rocket + position before touch-down. This makes looking for your rocket a lot like + Geo-Cacheing... just go to the waypoint and look around starting from there. + + + You may also enjoy having a ham radio "HT" that covers the 70cm band... you + can use that with your antenna to direction-find the rocket on the ground + the same way you can use a Walston or Beeline tracker. This can be handy + if the rocket is hiding in sage brush or a tree, or if the last GPS position + doesn't get you close enough because the rocket dropped into a canyon, or + the wind is blowing it across a dry lake bed, or something like that... Keith + and Bdale both currently own and use the Yaesu VX-7R at launches. + + + So, to recap, on the ground the hardware you'll need includes: + + + an antenna and feedline + + + a TeleDongle + + + a notebook computer + + + optionally, a handheld GPS receiver + + + optionally, an HT or receiver covering 435 Mhz + + + + + The best hand-held commercial directional antennas we've found for radio + direction finding rockets are from + + Arrow Antennas. + + The 440-3 and 440-5 are both good choices for finding a + TeleMetrum-equipped rocket when used with a suitable 70cm HT.
- - - +
+ Data Analysis + + Our software makes it easy to log the data from each flight, both the + telemetry received over the RF link during the flight itself, and the more + complete data log recorded in the DataFlash memory on the TeleMetrum + board. Once this data is on your computer, our postflight tools make it + easy to quickly get to the numbers everyone wants, like apogee altitude, + max acceleration, and max velocity. You can also generate and view a + standard set of plots showing the altitude, acceleration, and + velocity of the rocket during flight. And you can even export a data file + useable with Google Maps and Google Earth for visualizing the flight path + in two or three dimensions! + + + Our ultimate goal is to emit a set of files for each flight that can be + published as a web page per flight, or just viewed on your local disk with + a web browser. + +
+
+ Future Plans + + In the future, we intend to offer "companion boards" for the rocket that will + plug in to TeleMetrum to collect additional data, provide more pyro channels, + and so forth. A reference design for a companion board will be documented + soon, and will be compatible with open source Arduino programming tools. + + + We are also working on the design of a hand-held ground terminal that will + allow monitoring the rocket's status, collecting data during flight, and + logging data after flight without the need for a notebook computer on the + flight line. Particularly since it is so difficult to read most notebook + screens in direct sunlight, we think this will be a great thing to have. + + + Because all of our work is open, both the hardware designs and the software, + if you have some great idea for an addition to the current Altus Metrum family, + feel free to dive in and help! Or let us know what you'd like to see that + we aren't already working on, and maybe we'll get excited about it too... + +
+
+
+ + How GPS Works + + + Placeholder. + +
+
+
+ -- cgit v1.2.3 From f1892b137b1de3d6caf0293bd40ed5c3e4948066 Mon Sep 17 00:00:00 2001 From: Bdale Garbee Date: Tue, 23 Nov 2010 18:58:11 -0700 Subject: lose the placeholder on how GPS works, as it's going to be a while before I tackle that, if ever. --- doc/telemetrum-doc.xsl | 8 -------- 1 file changed, 8 deletions(-) diff --git a/doc/telemetrum-doc.xsl b/doc/telemetrum-doc.xsl index 6be23e7f..5c3e4c38 100644 --- a/doc/telemetrum-doc.xsl +++ b/doc/telemetrum-doc.xsl @@ -1461,14 +1461,6 @@ -
- - How GPS Works - - - Placeholder. - -
-- cgit v1.2.3 From d873dc28f0752aeb58a6263e42bdd5b9095bd392 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 23 Nov 2010 18:56:46 -0800 Subject: altos: remove unused variable from ao_igniter The 'status' variable used to hold a reported status value from the igniter after firing, but we ignore that now. Signed-off-by: Keith Packard --- src/ao_ignite.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ao_ignite.c b/src/ao_ignite.c index 58d340d9..f2b15dd2 100644 --- a/src/ao_ignite.c +++ b/src/ao_ignite.c @@ -101,7 +101,6 @@ void ao_igniter(void) { __xdata enum ao_ignter igniter; - __xdata enum ao_igniter_status status; ao_config_get(); for (;;) { -- cgit v1.2.3 From 54468e5dc567aaac5c5c20e921859b7cec28bb88 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 23 Nov 2010 18:57:49 -0800 Subject: altos: Don't abort radio transmissions with ao_radio_abort We only want to abort pending radio reception to release the radio for other use, or to change the radio channel. Let radio transmission proceed. This fixes a problem with using packet mode to configure the radio channel; if the packet transmission is aborted, the TM ends up wedged. Signed-off-by: Keith Packard --- src/ao_radio.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ao_radio.c b/src/ao_radio.c index 3fb4afd7..b2105ff8 100644 --- a/src/ao_radio.c +++ b/src/ao_radio.c @@ -432,8 +432,11 @@ ao_radio_rdf(int ms) void ao_radio_abort(void) { - ao_dma_abort(ao_radio_dma); - ao_radio_idle(); + /* Only abort if a task is waiting to receive data */ + if (RFST == RFST_SRX) { + ao_dma_abort(ao_radio_dma); + ao_radio_idle(); + } } void -- cgit v1.2.3 From 3b9db8c82d26a6a2e43d4ca40742fc1bdc502380 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 23 Nov 2010 19:02:54 -0800 Subject: altos: Make radio test command careful with the radio mutex. Remember whether the radio test mode is on or off and don't try to do either of them twice to prevent the mutex from being acquired or released twice. Signed-off-by: Keith Packard --- src/ao_radio.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ao_radio.c b/src/ao_radio.c index b2105ff8..67d5f6ba 100644 --- a/src/ao_radio.c +++ b/src/ao_radio.c @@ -452,26 +452,29 @@ void ao_radio_test(void) { uint8_t mode = 2; + static __xdata radio_on; ao_cmd_white(); if (ao_cmd_lex_c != '\n') { ao_cmd_decimal(); mode = (uint8_t) ao_cmd_lex_u32; } mode++; - if (mode & 2) { + if ((mode & 2) && !radio_on) { ao_set_monitor(0); ao_packet_slave_stop(); ao_radio_get(); RFST = RFST_STX; + radio_on = 1; } if (mode == 3) { printf ("Hit a character to stop..."); flush(); getchar(); putchar('\n'); } - if (mode & 1) { + if ((mode & 1) && radio_on) { ao_radio_idle(); ao_radio_put(); + radio_on = 0; } } -- cgit v1.2.3 From ed7cf7d262fcf7c0c677c2fb981582b571de9e5e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 23 Nov 2010 19:04:55 -0800 Subject: altosui: Make AltosSerial.flush_input keep reading while non-empty Flushing the input buffer can take a while, especially over the packet link. Keep reading while stuff is appearing on the reply queue. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosSerial.java | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/ao-tools/altosui/AltosSerial.java b/ao-tools/altosui/AltosSerial.java index ab74486b..8a6ad05e 100644 --- a/ao-tools/altosui/AltosSerial.java +++ b/ao-tools/altosui/AltosSerial.java @@ -114,16 +114,20 @@ public class AltosSerial implements Runnable { public void flush_input() { flush_output(); - try { - Thread.sleep(200); - } catch (InterruptedException ie) { - } - synchronized(this) { - if (!"VERSION".startsWith(line) && - !line.startsWith("VERSION")) - line = ""; - reply_queue.clear(); - } + boolean got_some; + do { + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + } + got_some = !reply_queue.isEmpty(); + synchronized(this) { + if (!"VERSION".startsWith(line) && + !line.startsWith("VERSION")) + line = ""; + reply_queue.clear(); + } + } while (got_some); } public String get_reply() throws InterruptedException { -- cgit v1.2.3 From 6cd9be22f06f21d12ee2f668989d83d3c61d14c0 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 23 Nov 2010 19:08:07 -0800 Subject: altosui: New AltosSerial.set_radio function sets channel/call Use this anytime you need to set the device radio channel and call sign, either for telemetry reception or packet mode origination. This uses the saved callsign and per-device radio channel number. Do not use this when opening a telemetrum as there won't be a saved channel number. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosConfig.java | 7 +++---- ao-tools/altosui/AltosEepromDownload.java | 7 +++---- ao-tools/altosui/AltosFlightUI.java | 1 - ao-tools/altosui/AltosIgnite.java | 7 +++---- ao-tools/altosui/AltosSerial.java | 9 +++++++-- ao-tools/altosui/AltosTelemetryReader.java | 6 ++---- 6 files changed, 18 insertions(+), 19 deletions(-) diff --git a/ao-tools/altosui/AltosConfig.java b/ao-tools/altosui/AltosConfig.java index 6bda20d8..52dbfd79 100644 --- a/ao-tools/altosui/AltosConfig.java +++ b/ao-tools/altosui/AltosConfig.java @@ -109,9 +109,8 @@ public class AltosConfig implements Runnable, ActionListener { void start_serial() throws InterruptedException { if (remote) { - serial_line.set_channel(AltosPreferences.channel(device.getSerial())); - serial_line.set_callsign(AltosPreferences.callsign()); - serial_line.printf("p\n"); + serial_line.set_radio(); + serial_line.printf("p\nE 0\n"); serial_line.flush_input(); } } @@ -128,7 +127,7 @@ public class AltosConfig implements Runnable, ActionListener { start_serial(); serial_line.printf("c s\nv\n"); for (;;) { - String line = serial_line.get_reply(1000); + String line = serial_line.get_reply(5000); if (line == null) throw new TimeoutException(); get_int(line, "serial-number", serial); diff --git a/ao-tools/altosui/AltosEepromDownload.java b/ao-tools/altosui/AltosEepromDownload.java index fb5dcfc0..02fc36f2 100644 --- a/ao-tools/altosui/AltosEepromDownload.java +++ b/ao-tools/altosui/AltosEepromDownload.java @@ -97,7 +97,7 @@ public class AltosEepromDownload implements Runnable { /* Pull the serial number out of the version information */ for (;;) { - String line = serial_line.get_reply(1000); + String line = serial_line.get_reply(5000); if (line == null) throw new TimeoutException(); @@ -127,7 +127,7 @@ 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(1000); + String line = serial_line.get_reply(5000); if (line == null) throw new TimeoutException(); int[] values = ParseHex(line); @@ -216,8 +216,7 @@ public class AltosEepromDownload implements Runnable { public void run () { if (remote) { - serial_line.set_channel(AltosPreferences.channel(device.getSerial())); - serial_line.set_callsign(AltosPreferences.callsign()); + serial_line.set_radio(); serial_line.printf("p\nE 0\n"); serial_line.flush_input(); } diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 732f7395..24d25bd7 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -148,7 +148,6 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { public void actionPerformed(ActionEvent e) { int channel = channels.getSelectedIndex(); reader.set_channel(channel); - AltosPreferences.set_channel(serial, channel); } }); c.gridx = 0; diff --git a/ao-tools/altosui/AltosIgnite.java b/ao-tools/altosui/AltosIgnite.java index 8e92ec1b..75c0a17a 100644 --- a/ao-tools/altosui/AltosIgnite.java +++ b/ao-tools/altosui/AltosIgnite.java @@ -35,9 +35,8 @@ public class AltosIgnite { 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.set_radio(); + serial.printf("p\nE 0\n"); serial.flush_input(); } } @@ -100,7 +99,7 @@ public class AltosIgnite { start_serial(); serial.printf("t\n"); for (;;) { - String line = serial.get_reply(1000); + String line = serial.get_reply(5000); if (line == null) throw new TimeoutException(); if (get_string(line, "Igniter: drogue Status: ", status_name)) diff --git a/ao-tools/altosui/AltosSerial.java b/ao-tools/altosui/AltosSerial.java index 8a6ad05e..b19143e5 100644 --- a/ao-tools/altosui/AltosSerial.java +++ b/ao-tools/altosui/AltosSerial.java @@ -198,13 +198,18 @@ public class AltosSerial implements Runnable { devices_opened.add(device.getPath()); } altos = libaltos.altos_open(device); - if (altos == null) + if (altos == null) { + close(); throw new FileNotFoundException(device.toShortString()); + } input_thread = new Thread(this); input_thread.start(); print("~\nE 0\n"); + set_monitor(false); flush_output(); - set_monitor(monitor_mode); + } + + public void set_radio() { set_channel(AltosPreferences.channel(device.getSerial())); set_callsign(AltosPreferences.callsign()); } diff --git a/ao-tools/altosui/AltosTelemetryReader.java b/ao-tools/altosui/AltosTelemetryReader.java index de5f50e9..6c5a9397 100644 --- a/ao-tools/altosui/AltosTelemetryReader.java +++ b/ao-tools/altosui/AltosTelemetryReader.java @@ -44,10 +44,7 @@ class AltosTelemetryReader extends AltosFlightReader { void set_channel(int channel) { serial.set_channel(channel); - } - - void set_callsign(String callsign) { - serial.set_callsign(callsign); + AltosPreferences.set_channel(device.getSerial(), channel); } public AltosTelemetryReader (AltosDevice in_device) @@ -58,6 +55,7 @@ class AltosTelemetryReader extends AltosFlightReader { name = device.toShortString(); telem = new LinkedBlockingQueue(); + serial.set_radio(); serial.add_monitor(telem); } } -- cgit v1.2.3 From f3233985a132e1d660e6df12d0056b6729f16faf Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 23 Nov 2010 19:09:31 -0800 Subject: altosui: Disable radio configation over packet link. Attempting to configure the radio over the packet link will only end up confusing the user, so disable it. This also works around a bug in older TM code which would lock up when trying to do this. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosConfig.java | 8 +++++--- ao-tools/altosui/AltosConfigUI.java | 6 +++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ao-tools/altosui/AltosConfig.java b/ao-tools/altosui/AltosConfig.java index 52dbfd79..b1acd410 100644 --- a/ao-tools/altosui/AltosConfig.java +++ b/ao-tools/altosui/AltosConfig.java @@ -149,7 +149,7 @@ public class AltosConfig implements Runnable, ActionListener { } void init_ui () throws InterruptedException, TimeoutException { - config_ui = new AltosConfigUI(owner); + config_ui = new AltosConfigUI(owner, remote); config_ui.addActionListener(this); set_ui(); } @@ -191,8 +191,10 @@ public class AltosConfig implements Runnable, ActionListener { start_serial(); serial_line.printf("c m %d\n", main_deploy.get()); serial_line.printf("c d %d\n", apogee_delay.get()); - serial_line.printf("c r %d\n", radio_channel.get()); - serial_line.printf("c f %d\n", radio_calibration.get()); + if (!remote) { + serial_line.printf("c r %d\n", radio_channel.get()); + serial_line.printf("c f %d\n", radio_calibration.get()); + } serial_line.printf("c c %s\n", callsign.get()); serial_line.printf("c w\n"); } catch (InterruptedException ie) { diff --git a/ao-tools/altosui/AltosConfigUI.java b/ao-tools/altosui/AltosConfigUI.java index ca89f58d..cfa5d7b9 100644 --- a/ao-tools/altosui/AltosConfigUI.java +++ b/ao-tools/altosui/AltosConfigUI.java @@ -98,7 +98,7 @@ public class AltosConfigUI } /* Build the UI using a grid bag */ - public AltosConfigUI(JFrame in_owner) { + public AltosConfigUI(JFrame in_owner, boolean remote) { super (in_owner, "Configure TeleMetrum", false); owner = in_owner; @@ -244,6 +244,8 @@ public class AltosConfigUI radio_channel_value = new JComboBox(radio_channel_values); radio_channel_value.setEditable(false); radio_channel_value.addItemListener(this); + if (remote) + radio_channel_value.setEnabled(false); pane.add(radio_channel_value, c); /* Radio Calibration */ @@ -267,6 +269,8 @@ public class AltosConfigUI c.ipady = 5; radio_calibration_value = new JTextField(String.format("%d", 1186611)); radio_calibration_value.getDocument().addDocumentListener(this); + if (remote) + radio_calibration_value.setEnabled(false); pane.add(radio_calibration_value, c); /* Callsign */ -- cgit v1.2.3 From 7d90e2f6009e060fb59c519f7e564483a7ca6872 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 23 Nov 2010 20:17:44 -0800 Subject: altosui: Let people fire igniters that don't read as 'ready' This provides for igniter testing with LEDs or other materials that don't look like regular igniters. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosIgniteUI.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ao-tools/altosui/AltosIgniteUI.java b/ao-tools/altosui/AltosIgniteUI.java index 0207e39f..d542729c 100644 --- a/ao-tools/altosui/AltosIgniteUI.java +++ b/ao-tools/altosui/AltosIgniteUI.java @@ -145,13 +145,11 @@ public class AltosIgniteUI if (cmd.equals("apogee") && apogee.isSelected()) { main.setSelected(false); - if (apogee_status == AltosIgnite.Ready) - arm.setEnabled(true); + arm.setEnabled(true); } if (cmd.equals("main") && main.isSelected()) { apogee.setSelected(false); - if (main_status == AltosIgnite.Ready) - arm.setEnabled(true); + arm.setEnabled(true); } if (cmd.equals("arm")) { -- cgit v1.2.3 From 7a50837ea0d92db3f469f197ec8210aee22aa143 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 24 Nov 2010 10:55:18 -0800 Subject: altosui: Make sure packet mode is turned off when the connection fails When the packet connection times out, turn packet mode off when closing the serial port. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosConfig.java | 10 +++++++++- ao-tools/altosui/AltosIgnite.java | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/ao-tools/altosui/AltosConfig.java b/ao-tools/altosui/AltosConfig.java index b1acd410..1c42870f 100644 --- a/ao-tools/altosui/AltosConfig.java +++ b/ao-tools/altosui/AltosConfig.java @@ -74,7 +74,7 @@ public class AltosConfig implements Runnable, ActionListener { string_ref product; string_ref callsign; AltosConfigUI config_ui; - + boolean serial_started; boolean get_int(String line, String label, int_ref x) { if (line.startsWith(label)) { @@ -108,6 +108,7 @@ public class AltosConfig implements Runnable, ActionListener { } void start_serial() throws InterruptedException { + serial_started = true; if (remote) { serial_line.set_radio(); serial_line.printf("p\nE 0\n"); @@ -116,6 +117,9 @@ public class AltosConfig implements Runnable, ActionListener { } void stop_serial() throws InterruptedException { + if (!serial_started) + return; + serial_started = false; if (remote) { serial_line.printf("~"); serial_line.flush_output(); @@ -160,6 +164,10 @@ public class AltosConfig implements Runnable, ActionListener { device.toShortString()), "Connection Failed", JOptionPane.ERROR_MESSAGE); + try { + stop_serial(); + } catch (InterruptedException ie) { + } serial_line.close(); serial_line = null; } diff --git a/ao-tools/altosui/AltosIgnite.java b/ao-tools/altosui/AltosIgnite.java index 75c0a17a..3cbd8a75 100644 --- a/ao-tools/altosui/AltosIgnite.java +++ b/ao-tools/altosui/AltosIgnite.java @@ -24,6 +24,7 @@ public class AltosIgnite { AltosDevice device; AltosSerial serial; boolean remote; + boolean serial_started; final static int None = 0; final static int Apogee = 1; final static int Main = 2; @@ -34,6 +35,7 @@ public class AltosIgnite { final static int Open = 3; private void start_serial() throws InterruptedException { + serial_started = true; if (remote) { serial.set_radio(); serial.printf("p\nE 0\n"); @@ -42,6 +44,9 @@ public class AltosIgnite { } private void stop_serial() throws InterruptedException { + if (!serial_started) + return; + serial_started = false; if (serial == null) return; if (remote) { @@ -148,6 +153,10 @@ public class AltosIgnite { } public void close() { + try { + stop_serial(); + } catch (InterruptedException ie) { + } serial.close(); serial = null; } -- cgit v1.2.3 From 357826aa9c7b42c59f5d52b8eb016d73b6da0c7f Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Thu, 25 Nov 2010 09:07:34 +1000 Subject: docs: Document altosui "Graph Data" button --- doc/telemetrum-doc.xsl | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/doc/telemetrum-doc.xsl b/doc/telemetrum-doc.xsl index 5c3e4c38..8f554d88 100644 --- a/doc/telemetrum-doc.xsl +++ b/doc/telemetrum-doc.xsl @@ -1087,7 +1087,30 @@
Graph Data - This section should be written by AJ. + 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 acceleration (blue), + velocity (green) and altitude (red) of the flight are plotted and + displayed, measured in metric units. + + + The graph can be zoomed into a particular area by clicking and + dragging down and to the right. Once zoomed, the graph can be + reset by clicking and dragging up and to the left. Holding down + control and clicking and dragging allows the graph to be panned. + The right mouse button causes a popup menu to be displayed, giving + you the option save or print the plot. + + + Note that telemetry files will generally produce poor graphs + due to the lower sampling rate and missed telemetry packets, + and will also often have significant amounts of data received + while the rocket was waiting on the pad. Use saved flight data + for graphing where possible.
-- cgit v1.2.3 From 3fbefb3eea981d34a09496cf8abf0119de2e35bf Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 24 Nov 2010 14:57:57 -0800 Subject: Move altosui to the top level, placing libaltos inside it. Signed-off-by: Keith Packard --- altosui/.gitignore | 19 + .../01altosui-contents.xml | 1 + .../01altosui.xml | 1 + .../AltOS Package Configuration.pmdoc/index.xml | 1 + altosui/Altos.java | 218 +++++ altosui/AltosAscent.java | 335 +++++++ altosui/AltosCRCException.java | 26 + altosui/AltosCSV.java | 252 +++++ altosui/AltosCSVUI.java | 108 ++ altosui/AltosChannelMenu.java | 44 + altosui/AltosConfig.java | 295 ++++++ altosui/AltosConfigUI.java | 466 +++++++++ altosui/AltosConfigureUI.java | 187 ++++ altosui/AltosConvert.java | 192 ++++ altosui/AltosDataChooser.java | 79 ++ altosui/AltosDataPoint.java | 29 + altosui/AltosDataPointReader.java | 72 ++ altosui/AltosDebug.java | 267 +++++ altosui/AltosDescent.java | 353 +++++++ altosui/AltosDevice.java | 170 ++++ altosui/AltosDeviceDialog.java | 164 ++++ altosui/AltosDisplayThread.java | 249 +++++ altosui/AltosEepromDownload.java | 285 ++++++ altosui/AltosEepromIterable.java | 423 ++++++++ altosui/AltosEepromMonitor.java | 176 ++++ altosui/AltosEepromRecord.java | 115 +++ altosui/AltosFile.java | 44 + altosui/AltosFlash.java | 344 +++++++ altosui/AltosFlashUI.java | 218 +++++ altosui/AltosFlightDisplay.java | 24 + altosui/AltosFlightInfoTableModel.java | 84 ++ altosui/AltosFlightReader.java | 38 + altosui/AltosFlightStatus.java | 154 +++ altosui/AltosFlightStatusTableModel.java | 61 ++ altosui/AltosFlightUI.java | 221 +++++ altosui/AltosGPS.java | 215 ++++ altosui/AltosGraph.java | 25 + altosui/AltosGraphTime.java | 233 +++++ altosui/AltosGraphUI.java | 274 ++++++ altosui/AltosGreatCircle.java | 100 ++ altosui/AltosHexfile.java | 252 +++++ altosui/AltosIgnite.java | 173 ++++ altosui/AltosIgniteUI.java | 317 ++++++ altosui/AltosInfoTable.java | 190 ++++ altosui/AltosKML.java | 169 ++++ altosui/AltosLanded.java | 212 ++++ altosui/AltosLed.java | 54 + altosui/AltosLights.java | 73 ++ altosui/AltosLine.java | 30 + altosui/AltosLog.java | 115 +++ altosui/AltosPad.java | 269 +++++ altosui/AltosParse.java | 79 ++ altosui/AltosPreferences.java | 205 ++++ altosui/AltosReader.java | 28 + altosui/AltosRecord.java | 219 +++++ altosui/AltosRecordIterable.java | 34 + altosui/AltosReplayReader.java | 57 ++ altosui/AltosRomconfig.java | 147 +++ altosui/AltosRomconfigUI.java | 186 ++++ altosui/AltosSerial.java | 253 +++++ altosui/AltosSerialInUseException.java | 28 + altosui/AltosSerialMonitor.java | 22 + altosui/AltosSiteMap.java | 388 ++++++++ altosui/AltosSiteMapCache.java | 103 ++ altosui/AltosSiteMapTile.java | 112 +++ altosui/AltosState.java | 197 ++++ altosui/AltosTelemetry.java | 143 +++ altosui/AltosTelemetryIterable.java | 82 ++ altosui/AltosTelemetryReader.java | 61 ++ altosui/AltosUI.app/Contents/Info.plist | 38 + .../AltosUI.app/Contents/MacOS/JavaApplicationStub | Bin 0 -> 61296 bytes altosui/AltosUI.app/Contents/PkgInfo | 1 + .../Contents/Resources/AltosUIIcon.icns | Bin 0 -> 129010 bytes altosui/AltosUI.java | 405 ++++++++ altosui/AltosVoice.java | 95 ++ altosui/AltosWriter.java | 32 + altosui/GrabNDrag.java | 54 + altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi | 84 ++ .../Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe | Bin 0 -> 51831 bytes altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c | 704 ++++++++++++++ altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp | 110 +++ altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw | 29 + altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt | 141 +++ altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf | 137 +++ altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys | Bin 0 -> 30464 bytes altosui/Instdrv/NSIS/Plugins/InstDrv.dll | Bin 0 -> 6656 bytes altosui/Makefile-standalone | 184 ++++ altosui/Makefile.am | 272 ++++++ altosui/altos-windows.nsi | 113 +++ altosui/altosui-fat | 4 + altosui/altosui.1 | 46 + altosui/altusmetrum.jpg | Bin 0 -> 72868 bytes altosui/libaltos/.gitignore | 12 + altosui/libaltos/Makefile-standalone | 126 +++ altosui/libaltos/Makefile.am | 41 + altosui/libaltos/altos.dll | Bin 0 -> 31765 bytes altosui/libaltos/cjnitest.c | 43 + altosui/libaltos/libaltos.c | 1028 ++++++++++++++++++++ altosui/libaltos/libaltos.dylib | Bin 0 -> 54176 bytes altosui/libaltos/libaltos.h | 102 ++ altosui/libaltos/libaltos.i0 | 5 + ao-tools/Makefile.am | 2 +- ao-tools/altosui/.gitignore | 19 - .../01altosui-contents.xml | 1 - .../01altosui.xml | 1 - .../AltOS Package Configuration.pmdoc/index.xml | 1 - ao-tools/altosui/Altos.java | 218 ----- ao-tools/altosui/AltosAscent.java | 335 ------- ao-tools/altosui/AltosCRCException.java | 26 - ao-tools/altosui/AltosCSV.java | 252 ----- ao-tools/altosui/AltosCSVUI.java | 108 -- ao-tools/altosui/AltosChannelMenu.java | 44 - ao-tools/altosui/AltosConfig.java | 295 ------ ao-tools/altosui/AltosConfigUI.java | 466 --------- ao-tools/altosui/AltosConfigureUI.java | 187 ---- ao-tools/altosui/AltosConvert.java | 192 ---- ao-tools/altosui/AltosDataChooser.java | 79 -- ao-tools/altosui/AltosDataPoint.java | 29 - ao-tools/altosui/AltosDataPointReader.java | 72 -- ao-tools/altosui/AltosDebug.java | 267 ----- ao-tools/altosui/AltosDescent.java | 353 ------- ao-tools/altosui/AltosDevice.java | 170 ---- ao-tools/altosui/AltosDeviceDialog.java | 164 ---- ao-tools/altosui/AltosDisplayThread.java | 249 ----- ao-tools/altosui/AltosEepromDownload.java | 285 ------ ao-tools/altosui/AltosEepromIterable.java | 423 -------- ao-tools/altosui/AltosEepromMonitor.java | 176 ---- ao-tools/altosui/AltosEepromRecord.java | 115 --- ao-tools/altosui/AltosFile.java | 44 - ao-tools/altosui/AltosFlash.java | 344 ------- ao-tools/altosui/AltosFlashUI.java | 218 ----- ao-tools/altosui/AltosFlightDisplay.java | 24 - ao-tools/altosui/AltosFlightInfoTableModel.java | 84 -- ao-tools/altosui/AltosFlightReader.java | 38 - ao-tools/altosui/AltosFlightStatus.java | 154 --- ao-tools/altosui/AltosFlightStatusTableModel.java | 61 -- ao-tools/altosui/AltosFlightUI.java | 221 ----- ao-tools/altosui/AltosGPS.java | 215 ---- ao-tools/altosui/AltosGraph.java | 25 - ao-tools/altosui/AltosGraphTime.java | 233 ----- ao-tools/altosui/AltosGraphUI.java | 274 ------ ao-tools/altosui/AltosGreatCircle.java | 100 -- ao-tools/altosui/AltosHexfile.java | 252 ----- ao-tools/altosui/AltosIgnite.java | 173 ---- ao-tools/altosui/AltosIgniteUI.java | 317 ------ ao-tools/altosui/AltosInfoTable.java | 190 ---- ao-tools/altosui/AltosKML.java | 169 ---- ao-tools/altosui/AltosLanded.java | 212 ---- ao-tools/altosui/AltosLed.java | 54 - ao-tools/altosui/AltosLights.java | 73 -- ao-tools/altosui/AltosLine.java | 30 - ao-tools/altosui/AltosLog.java | 115 --- ao-tools/altosui/AltosPad.java | 269 ----- ao-tools/altosui/AltosParse.java | 79 -- ao-tools/altosui/AltosPreferences.java | 205 ---- ao-tools/altosui/AltosReader.java | 28 - ao-tools/altosui/AltosRecord.java | 219 ----- ao-tools/altosui/AltosRecordIterable.java | 34 - ao-tools/altosui/AltosReplayReader.java | 57 -- ao-tools/altosui/AltosRomconfig.java | 147 --- ao-tools/altosui/AltosRomconfigUI.java | 186 ---- ao-tools/altosui/AltosSerial.java | 253 ----- ao-tools/altosui/AltosSerialInUseException.java | 28 - ao-tools/altosui/AltosSerialMonitor.java | 22 - ao-tools/altosui/AltosSiteMap.java | 388 -------- ao-tools/altosui/AltosSiteMapCache.java | 103 -- ao-tools/altosui/AltosSiteMapTile.java | 112 --- ao-tools/altosui/AltosState.java | 197 ---- ao-tools/altosui/AltosTelemetry.java | 143 --- ao-tools/altosui/AltosTelemetryIterable.java | 82 -- ao-tools/altosui/AltosTelemetryReader.java | 61 -- ao-tools/altosui/AltosUI.app/Contents/Info.plist | 38 - .../AltosUI.app/Contents/MacOS/JavaApplicationStub | Bin 61296 -> 0 bytes ao-tools/altosui/AltosUI.app/Contents/PkgInfo | 1 - .../Contents/Resources/AltosUIIcon.icns | Bin 129010 -> 0 bytes ao-tools/altosui/AltosUI.java | 405 -------- ao-tools/altosui/AltosVoice.java | 95 -- ao-tools/altosui/AltosWriter.java | 32 - ao-tools/altosui/GrabNDrag.java | 54 - .../Instdrv/NSIS/Contrib/InstDrv/Example.nsi | 84 -- .../Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe | Bin 51831 -> 0 bytes .../altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c | 704 -------------- .../Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp | 110 --- .../Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw | 29 - .../Instdrv/NSIS/Contrib/InstDrv/Readme.txt | 141 --- .../Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf | 137 --- .../Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys | Bin 30464 -> 0 bytes ao-tools/altosui/Instdrv/NSIS/Plugins/InstDrv.dll | Bin 6656 -> 0 bytes ao-tools/altosui/Makefile-standalone | 184 ---- ao-tools/altosui/Makefile.am | 271 ------ ao-tools/altosui/altos-windows.nsi | 113 --- ao-tools/altosui/altosui-fat | 4 - ao-tools/altosui/altosui.1 | 46 - ao-tools/altosui/altusmetrum.jpg | Bin 72868 -> 0 bytes ao-tools/libaltos/.gitignore | 12 - ao-tools/libaltos/Makefile-standalone | 126 --- ao-tools/libaltos/Makefile.am | 41 - ao-tools/libaltos/altos.dll | Bin 31765 -> 0 bytes ao-tools/libaltos/cjnitest.c | 43 - ao-tools/libaltos/libaltos.c | 1028 -------------------- ao-tools/libaltos/libaltos.dylib | Bin 54176 -> 0 bytes ao-tools/libaltos/libaltos.h | 102 -- ao-tools/libaltos/libaltos.i0 | 5 - configure.ac | 4 +- 204 files changed, 14569 insertions(+), 14568 deletions(-) create mode 100644 altosui/.gitignore create mode 100644 altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml create mode 100644 altosui/AltOS Package Configuration.pmdoc/01altosui.xml create mode 100644 altosui/AltOS Package Configuration.pmdoc/index.xml create mode 100644 altosui/Altos.java create mode 100644 altosui/AltosAscent.java create mode 100644 altosui/AltosCRCException.java create mode 100644 altosui/AltosCSV.java create mode 100644 altosui/AltosCSVUI.java create mode 100644 altosui/AltosChannelMenu.java create mode 100644 altosui/AltosConfig.java create mode 100644 altosui/AltosConfigUI.java create mode 100644 altosui/AltosConfigureUI.java create mode 100644 altosui/AltosConvert.java create mode 100644 altosui/AltosDataChooser.java create mode 100644 altosui/AltosDataPoint.java create mode 100644 altosui/AltosDataPointReader.java create mode 100644 altosui/AltosDebug.java create mode 100644 altosui/AltosDescent.java create mode 100644 altosui/AltosDevice.java create mode 100644 altosui/AltosDeviceDialog.java create mode 100644 altosui/AltosDisplayThread.java create mode 100644 altosui/AltosEepromDownload.java create mode 100644 altosui/AltosEepromIterable.java create mode 100644 altosui/AltosEepromMonitor.java create mode 100644 altosui/AltosEepromRecord.java create mode 100644 altosui/AltosFile.java create mode 100644 altosui/AltosFlash.java create mode 100644 altosui/AltosFlashUI.java create mode 100644 altosui/AltosFlightDisplay.java create mode 100644 altosui/AltosFlightInfoTableModel.java create mode 100644 altosui/AltosFlightReader.java create mode 100644 altosui/AltosFlightStatus.java create mode 100644 altosui/AltosFlightStatusTableModel.java create mode 100644 altosui/AltosFlightUI.java create mode 100644 altosui/AltosGPS.java create mode 100644 altosui/AltosGraph.java create mode 100644 altosui/AltosGraphTime.java create mode 100644 altosui/AltosGraphUI.java create mode 100644 altosui/AltosGreatCircle.java create mode 100644 altosui/AltosHexfile.java create mode 100644 altosui/AltosIgnite.java create mode 100644 altosui/AltosIgniteUI.java create mode 100644 altosui/AltosInfoTable.java create mode 100644 altosui/AltosKML.java create mode 100644 altosui/AltosLanded.java create mode 100644 altosui/AltosLed.java create mode 100644 altosui/AltosLights.java create mode 100644 altosui/AltosLine.java create mode 100644 altosui/AltosLog.java create mode 100644 altosui/AltosPad.java create mode 100644 altosui/AltosParse.java create mode 100644 altosui/AltosPreferences.java create mode 100644 altosui/AltosReader.java create mode 100644 altosui/AltosRecord.java create mode 100644 altosui/AltosRecordIterable.java create mode 100644 altosui/AltosReplayReader.java create mode 100644 altosui/AltosRomconfig.java create mode 100644 altosui/AltosRomconfigUI.java create mode 100644 altosui/AltosSerial.java create mode 100644 altosui/AltosSerialInUseException.java create mode 100644 altosui/AltosSerialMonitor.java create mode 100644 altosui/AltosSiteMap.java create mode 100644 altosui/AltosSiteMapCache.java create mode 100644 altosui/AltosSiteMapTile.java create mode 100644 altosui/AltosState.java create mode 100644 altosui/AltosTelemetry.java create mode 100644 altosui/AltosTelemetryIterable.java create mode 100644 altosui/AltosTelemetryReader.java create mode 100644 altosui/AltosUI.app/Contents/Info.plist create mode 100755 altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub create mode 100644 altosui/AltosUI.app/Contents/PkgInfo create mode 100644 altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns create mode 100644 altosui/AltosUI.java create mode 100644 altosui/AltosVoice.java create mode 100644 altosui/AltosWriter.java create mode 100644 altosui/GrabNDrag.java create mode 100644 altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi create mode 100644 altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe create mode 100644 altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c create mode 100644 altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp create mode 100644 altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw create mode 100644 altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt create mode 100644 altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf create mode 100644 altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys create mode 100644 altosui/Instdrv/NSIS/Plugins/InstDrv.dll create mode 100644 altosui/Makefile-standalone create mode 100644 altosui/Makefile.am create mode 100644 altosui/altos-windows.nsi create mode 100755 altosui/altosui-fat create mode 100644 altosui/altosui.1 create mode 100644 altosui/altusmetrum.jpg create mode 100644 altosui/libaltos/.gitignore create mode 100644 altosui/libaltos/Makefile-standalone create mode 100644 altosui/libaltos/Makefile.am create mode 100755 altosui/libaltos/altos.dll create mode 100644 altosui/libaltos/cjnitest.c create mode 100644 altosui/libaltos/libaltos.c create mode 100755 altosui/libaltos/libaltos.dylib create mode 100644 altosui/libaltos/libaltos.h create mode 100644 altosui/libaltos/libaltos.i0 delete mode 100644 ao-tools/altosui/.gitignore delete mode 100644 ao-tools/altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml delete mode 100644 ao-tools/altosui/AltOS Package Configuration.pmdoc/01altosui.xml delete mode 100644 ao-tools/altosui/AltOS Package Configuration.pmdoc/index.xml delete mode 100644 ao-tools/altosui/Altos.java delete mode 100644 ao-tools/altosui/AltosAscent.java delete mode 100644 ao-tools/altosui/AltosCRCException.java delete mode 100644 ao-tools/altosui/AltosCSV.java delete mode 100644 ao-tools/altosui/AltosCSVUI.java delete mode 100644 ao-tools/altosui/AltosChannelMenu.java delete mode 100644 ao-tools/altosui/AltosConfig.java delete mode 100644 ao-tools/altosui/AltosConfigUI.java delete mode 100644 ao-tools/altosui/AltosConfigureUI.java delete mode 100644 ao-tools/altosui/AltosConvert.java delete mode 100644 ao-tools/altosui/AltosDataChooser.java delete mode 100644 ao-tools/altosui/AltosDataPoint.java delete mode 100644 ao-tools/altosui/AltosDataPointReader.java delete mode 100644 ao-tools/altosui/AltosDebug.java delete mode 100644 ao-tools/altosui/AltosDescent.java delete mode 100644 ao-tools/altosui/AltosDevice.java delete mode 100644 ao-tools/altosui/AltosDeviceDialog.java delete mode 100644 ao-tools/altosui/AltosDisplayThread.java delete mode 100644 ao-tools/altosui/AltosEepromDownload.java delete mode 100644 ao-tools/altosui/AltosEepromIterable.java delete mode 100644 ao-tools/altosui/AltosEepromMonitor.java delete mode 100644 ao-tools/altosui/AltosEepromRecord.java delete mode 100644 ao-tools/altosui/AltosFile.java delete mode 100644 ao-tools/altosui/AltosFlash.java delete mode 100644 ao-tools/altosui/AltosFlashUI.java delete mode 100644 ao-tools/altosui/AltosFlightDisplay.java delete mode 100644 ao-tools/altosui/AltosFlightInfoTableModel.java delete mode 100644 ao-tools/altosui/AltosFlightReader.java delete mode 100644 ao-tools/altosui/AltosFlightStatus.java delete mode 100644 ao-tools/altosui/AltosFlightStatusTableModel.java delete mode 100644 ao-tools/altosui/AltosFlightUI.java delete mode 100644 ao-tools/altosui/AltosGPS.java delete mode 100644 ao-tools/altosui/AltosGraph.java delete mode 100644 ao-tools/altosui/AltosGraphTime.java delete mode 100644 ao-tools/altosui/AltosGraphUI.java delete mode 100644 ao-tools/altosui/AltosGreatCircle.java delete mode 100644 ao-tools/altosui/AltosHexfile.java delete mode 100644 ao-tools/altosui/AltosIgnite.java delete mode 100644 ao-tools/altosui/AltosIgniteUI.java delete mode 100644 ao-tools/altosui/AltosInfoTable.java delete mode 100644 ao-tools/altosui/AltosKML.java delete mode 100644 ao-tools/altosui/AltosLanded.java delete mode 100644 ao-tools/altosui/AltosLed.java delete mode 100644 ao-tools/altosui/AltosLights.java delete mode 100644 ao-tools/altosui/AltosLine.java delete mode 100644 ao-tools/altosui/AltosLog.java delete mode 100644 ao-tools/altosui/AltosPad.java delete mode 100644 ao-tools/altosui/AltosParse.java delete mode 100644 ao-tools/altosui/AltosPreferences.java delete mode 100644 ao-tools/altosui/AltosReader.java delete mode 100644 ao-tools/altosui/AltosRecord.java delete mode 100644 ao-tools/altosui/AltosRecordIterable.java delete mode 100644 ao-tools/altosui/AltosReplayReader.java delete mode 100644 ao-tools/altosui/AltosRomconfig.java delete mode 100644 ao-tools/altosui/AltosRomconfigUI.java delete mode 100644 ao-tools/altosui/AltosSerial.java delete mode 100644 ao-tools/altosui/AltosSerialInUseException.java delete mode 100644 ao-tools/altosui/AltosSerialMonitor.java delete mode 100644 ao-tools/altosui/AltosSiteMap.java delete mode 100644 ao-tools/altosui/AltosSiteMapCache.java delete mode 100644 ao-tools/altosui/AltosSiteMapTile.java delete mode 100644 ao-tools/altosui/AltosState.java delete mode 100644 ao-tools/altosui/AltosTelemetry.java delete mode 100644 ao-tools/altosui/AltosTelemetryIterable.java delete mode 100644 ao-tools/altosui/AltosTelemetryReader.java delete mode 100644 ao-tools/altosui/AltosUI.app/Contents/Info.plist delete mode 100755 ao-tools/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub delete mode 100644 ao-tools/altosui/AltosUI.app/Contents/PkgInfo delete mode 100644 ao-tools/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns delete mode 100644 ao-tools/altosui/AltosUI.java delete mode 100644 ao-tools/altosui/AltosVoice.java delete mode 100644 ao-tools/altosui/AltosWriter.java delete mode 100644 ao-tools/altosui/GrabNDrag.java delete mode 100644 ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi delete mode 100644 ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe delete mode 100644 ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c delete mode 100644 ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp delete mode 100644 ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw delete mode 100644 ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt delete mode 100644 ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf delete mode 100644 ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys delete mode 100644 ao-tools/altosui/Instdrv/NSIS/Plugins/InstDrv.dll delete mode 100644 ao-tools/altosui/Makefile-standalone delete mode 100644 ao-tools/altosui/Makefile.am delete mode 100644 ao-tools/altosui/altos-windows.nsi delete mode 100755 ao-tools/altosui/altosui-fat delete mode 100644 ao-tools/altosui/altosui.1 delete mode 100644 ao-tools/altosui/altusmetrum.jpg delete mode 100644 ao-tools/libaltos/.gitignore delete mode 100644 ao-tools/libaltos/Makefile-standalone delete mode 100644 ao-tools/libaltos/Makefile.am delete mode 100755 ao-tools/libaltos/altos.dll delete mode 100644 ao-tools/libaltos/cjnitest.c delete mode 100644 ao-tools/libaltos/libaltos.c delete mode 100755 ao-tools/libaltos/libaltos.dylib delete mode 100644 ao-tools/libaltos/libaltos.h delete mode 100644 ao-tools/libaltos/libaltos.i0 diff --git a/altosui/.gitignore b/altosui/.gitignore new file mode 100644 index 00000000..89be1d53 --- /dev/null +++ b/altosui/.gitignore @@ -0,0 +1,19 @@ +windows/ +linux/ +macosx/ +fat/ +Manifest.txt +Manifest-fat.txt +libaltosJNI +classes +altosui +altosui-test +classaltosui.stamp +Altos-Linux-*.tar.bz2 +Altos-Mac-*.zip +Altos-Windows-*.exe +*.dll +*.dylib +*.so +*.jar +*.class diff --git a/altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml b/altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml new file mode 100644 index 00000000..18e00fe4 --- /dev/null +++ b/altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/altosui/AltOS Package Configuration.pmdoc/01altosui.xml b/altosui/AltOS Package Configuration.pmdoc/01altosui.xml new file mode 100644 index 00000000..6170931b --- /dev/null +++ b/altosui/AltOS Package Configuration.pmdoc/01altosui.xml @@ -0,0 +1 @@ +org.altusmetrum.altosUi.AltosUI.pkg0.7AltosUI.app/Applications/AltosUI.appinstallTo.pathinstallFrom.isRelativeTypeversionparentrequireAuthorizationinstallTo01altosui-contents.xml/CVS$/\.svn$/\.cvsignore$/\.cvspass$/\.DS_Store$ \ No newline at end of file diff --git a/altosui/AltOS Package Configuration.pmdoc/index.xml b/altosui/AltOS Package Configuration.pmdoc/index.xml new file mode 100644 index 00000000..fabe54a6 --- /dev/null +++ b/altosui/AltOS Package Configuration.pmdoc/index.xml @@ -0,0 +1 @@ +AltOS UI/Users/keithp/altos/ao-tools/altosui/AltosUI.pkgorg.altusmetrumInstall AltOS User Interfacealtusmetrum.jpg01altosui.xmlproperties.anywhereDomainproperties.titleproperties.customizeOptiondescriptionproperties.userDomainproperties.systemDomain \ No newline at end of file diff --git a/altosui/Altos.java b/altosui/Altos.java new file mode 100644 index 00000000..8ee94e04 --- /dev/null +++ b/altosui/Altos.java @@ -0,0 +1,218 @@ +/* + * 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.util.*; +import java.text.*; + +public class Altos { + /* EEProm command letters */ + static final int AO_LOG_FLIGHT = 'F'; + static final int AO_LOG_SENSOR = 'A'; + static final int AO_LOG_TEMP_VOLT = 'T'; + static final int AO_LOG_DEPLOY = 'D'; + static final int AO_LOG_STATE = 'S'; + static final int AO_LOG_GPS_TIME = 'G'; + static final int AO_LOG_GPS_LAT = 'N'; + static final int AO_LOG_GPS_LON = 'W'; + static final int AO_LOG_GPS_ALT = 'H'; + static final int AO_LOG_GPS_SAT = 'V'; + static final int AO_LOG_GPS_DATE = 'Y'; + + /* Added for header fields in eeprom files */ + static final int AO_LOG_CONFIG_VERSION = 1000; + static final int AO_LOG_MAIN_DEPLOY = 1001; + static final int AO_LOG_APOGEE_DELAY = 1002; + static final int AO_LOG_RADIO_CHANNEL = 1003; + static final int AO_LOG_CALLSIGN = 1004; + static final int AO_LOG_ACCEL_CAL = 1005; + static final int AO_LOG_RADIO_CAL = 1006; + static final int AO_LOG_MANUFACTURER = 1007; + static final int AO_LOG_PRODUCT = 1008; + static final int AO_LOG_SERIAL_NUMBER = 1009; + static final int AO_LOG_SOFTWARE_VERSION = 1010; + + /* Added to flag invalid records */ + static final int AO_LOG_INVALID = -1; + + /* Flight state numbers and names */ + static final int ao_flight_startup = 0; + static final int ao_flight_idle = 1; + static final int ao_flight_pad = 2; + static final int ao_flight_boost = 3; + static final int ao_flight_fast = 4; + static final int ao_flight_coast = 5; + static final int ao_flight_drogue = 6; + static final int ao_flight_main = 7; + static final int ao_flight_landed = 8; + static final int ao_flight_invalid = 9; + + static HashMap string_to_state = new HashMap(); + + static boolean map_initialized = false; + + static final int tab_elt_pad = 5; + + static final Font label_font = new Font("Dialog", Font.PLAIN, 22); + static final Font value_font = new Font("Monospaced", Font.PLAIN, 22); + static final Font status_font = new Font("SansSerif", Font.BOLD, 24); + + static final int text_width = 16; + + static void initialize_map() + { + string_to_state.put("startup", ao_flight_startup); + string_to_state.put("idle", ao_flight_idle); + string_to_state.put("pad", ao_flight_pad); + string_to_state.put("boost", ao_flight_boost); + string_to_state.put("fast", ao_flight_fast); + string_to_state.put("coast", ao_flight_coast); + string_to_state.put("drogue", ao_flight_drogue); + string_to_state.put("main", ao_flight_main); + string_to_state.put("landed", ao_flight_landed); + string_to_state.put("invalid", ao_flight_invalid); + map_initialized = true; + } + + static String[] state_to_string = { + "startup", + "idle", + "pad", + "boost", + "fast", + "coast", + "drogue", + "main", + "landed", + "invalid", + }; + + static public int state(String state) { + if (!map_initialized) + initialize_map(); + if (string_to_state.containsKey(state)) + return string_to_state.get(state); + return ao_flight_invalid; + } + + static public String state_name(int state) { + if (state < 0 || state_to_string.length <= state) + return "invalid"; + return state_to_string[state]; + } + + static final int AO_GPS_VALID = (1 << 4); + static final int AO_GPS_RUNNING = (1 << 5); + static final int AO_GPS_DATE_VALID = (1 << 6); + static final int AO_GPS_NUM_SAT_SHIFT = 0; + static final int AO_GPS_NUM_SAT_MASK = 0xf; + + static boolean isspace(int c) { + switch (c) { + case ' ': + case '\t': + return true; + } + return false; + } + + static boolean ishex(int c) { + if ('0' <= c && c <= '9') + return true; + if ('a' <= c && c <= 'f') + return true; + if ('A' <= c && c <= 'F') + return true; + return false; + } + + static boolean ishex(String s) { + for (int i = 0; i < s.length(); i++) + if (!ishex(s.charAt(i))) + return false; + return true; + } + + static int fromhex(int c) { + if ('0' <= c && c <= '9') + return c - '0'; + if ('a' <= c && c <= 'f') + return c - 'a' + 10; + if ('A' <= c && c <= 'F') + return c - 'A' + 10; + return -1; + } + + static int fromhex(String s) throws NumberFormatException { + int c, v = 0; + for (int i = 0; i < s.length(); i++) { + c = s.charAt(i); + if (!ishex(c)) { + if (i == 0) + throw new NumberFormatException(String.format("invalid hex \"%s\"", s)); + return v; + } + v = v * 16 + fromhex(c); + } + return v; + } + + static boolean isdec(int c) { + if ('0' <= c && c <= '9') + return true; + return false; + } + + static boolean isdec(String s) { + for (int i = 0; i < s.length(); i++) + if (!isdec(s.charAt(i))) + return false; + return true; + } + + static int fromdec(int c) { + if ('0' <= c && c <= '9') + return c - '0'; + return -1; + } + + static int fromdec(String s) throws NumberFormatException { + int c, v = 0; + int sign = 1; + for (int i = 0; i < s.length(); i++) { + c = s.charAt(i); + if (i == 0 && c == '-') { + sign = -1; + } else if (!isdec(c)) { + if (i == 0) + throw new NumberFormatException(String.format("invalid number \"%s\"", s)); + return v; + } else + v = v * 10 + fromdec(c); + } + return v * sign; + } + + static String replace_extension(String input, String extension) { + int dot = input.lastIndexOf("."); + if (dot > 0) + input = input.substring(0,dot); + return input.concat(extension); + } +} diff --git a/altosui/AltosAscent.java b/altosui/AltosAscent.java new file mode 100644 index 00000000..64bdcf30 --- /dev/null +++ b/altosui/AltosAscent.java @@ -0,0 +1,335 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosAscent extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + + public class AscentStatus { + JLabel label; + JTextField value; + AltosLights lights; + + void show(AltosState state, int crc_errors) {} + void reset() { + value.setText(""); + lights.set(false); + } + + public AscentStatus (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + lights = new AltosLights(); + c.gridx = 0; c.gridy = y; + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(lights, c); + add(lights); + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(Altos.text_width); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.gridwidth = 2; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + + } + } + + public class AscentValue { + JLabel label; + JTextField value; + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + } + public AscentValue (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(Altos.text_width); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.gridwidth = 2; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + } + } + + public class AscentValueHold { + JLabel label; + JTextField value; + JTextField max_value; + double max; + + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + max_value.setText(""); + max = 0; + } + + void show(String format, double v) { + value.setText(String.format(format, v)); + if (v > max) { + max_value.setText(String.format(format, v)); + max = v; + } + } + public AscentValueHold (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(Altos.text_width); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + + max_value = new JTextField(Altos.text_width); + max_value.setFont(Altos.value_font); + max_value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 3; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(max_value, c); + add(max_value); + } + } + + + class Height extends AscentValueHold { + void show (AltosState state, int crc_errors) { + show("%6.0f m", state.height); + } + public Height (GridBagLayout layout, int y) { + super (layout, y, "Height"); + } + } + + Height height; + + class Speed extends AscentValueHold { + void show (AltosState state, int crc_errors) { + double speed = state.speed; + if (!state.ascent) + speed = state.baro_speed; + show("%6.0f m/s", speed); + } + public Speed (GridBagLayout layout, int y) { + super (layout, y, "Speed"); + } + } + + Speed speed; + + class Accel extends AscentValueHold { + void show (AltosState state, int crc_errors) { + show("%6.0f m/s²", state.acceleration); + } + public Accel (GridBagLayout layout, int y) { + super (layout, y, "Acceleration"); + } + } + + Accel accel; + + String pos(double p, String pos, String neg) { + String h = pos; + if (p < 0) { + h = neg; + p = -p; + } + int deg = (int) Math.floor(p); + double min = (p - Math.floor(p)) * 60.0; + return String.format("%s %4d° %9.6f", h, deg, min); + } + + class Apogee extends AscentStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.drogue_sense)); + lights.set(state.drogue_sense > 3.2); + } + public Apogee (GridBagLayout layout, int y) { + super(layout, y, "Apogee Igniter Voltage"); + } + } + + Apogee apogee; + + class Main extends AscentStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.main_sense)); + lights.set(state.main_sense > 3.2); + } + public Main (GridBagLayout layout, int y) { + super(layout, y, "Main Igniter Voltage"); + } + } + + Main main; + + class Lat extends AscentValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + value.setText(pos(state.gps.lat,"N", "S")); + else + value.setText("???"); + } + public Lat (GridBagLayout layout, int y) { + super (layout, y, "Latitude"); + } + } + + Lat lat; + + class Lon extends AscentValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + value.setText(pos(state.gps.lon,"E", "W")); + else + value.setText("???"); + } + public Lon (GridBagLayout layout, int y) { + super (layout, y, "Longitude"); + } + } + + Lon lon; + + public void reset() { + lat.reset(); + lon.reset(); + main.reset(); + apogee.reset(); + height.reset(); + speed.reset(); + accel.reset(); + } + + public void show(AltosState state, int crc_errors) { + lat.show(state, crc_errors); + lon.show(state, crc_errors); + height.show(state, crc_errors); + main.show(state, crc_errors); + apogee.show(state, crc_errors); + speed.show(state, crc_errors); + accel.show(state, crc_errors); + } + + public void labels(GridBagLayout layout, int y) { + GridBagConstraints c; + JLabel cur, max; + + cur = new JLabel("Current"); + cur.setFont(Altos.label_font); + c = new GridBagConstraints(); + c.gridx = 2; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + layout.setConstraints(cur, c); + add(cur); + + max = new JLabel("Maximum"); + max.setFont(Altos.label_font); + c.gridx = 3; c.gridy = y; + layout.setConstraints(max, c); + add(max); + } + + public AltosAscent() { + layout = new GridBagLayout(); + + setLayout(layout); + + /* Elements in ascent display: + * + * lat + * lon + * height + */ + labels(layout, 0); + height = new Height(layout, 1); + speed = new Speed(layout, 2); + accel = new Accel(layout, 3); + lat = new Lat(layout, 4); + lon = new Lon(layout, 5); + apogee = new Apogee(layout, 6); + main = new Main(layout, 7); + } +} diff --git a/altosui/AltosCRCException.java b/altosui/AltosCRCException.java new file mode 100644 index 00000000..4a529bcf --- /dev/null +++ b/altosui/AltosCRCException.java @@ -0,0 +1,26 @@ +/* + * 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; + +public class AltosCRCException extends Exception { + public int rssi; + + public AltosCRCException (int in_rssi) { + rssi = in_rssi; + } +} diff --git a/altosui/AltosCSV.java b/altosui/AltosCSV.java new file mode 100644 index 00000000..df98b2b4 --- /dev/null +++ b/altosui/AltosCSV.java @@ -0,0 +1,252 @@ +/* + * 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.lang.*; +import java.io.*; +import java.text.*; +import java.util.*; + +public class AltosCSV implements AltosWriter { + File name; + PrintStream out; + boolean header_written; + boolean seen_boost; + int boost_tick; + LinkedList pad_records; + AltosState state; + + static final int ALTOS_CSV_VERSION = 2; + + /* Version 2 format: + * + * General info + * version number + * serial number + * flight number + * callsign + * time (seconds since boost) + * rssi + * link quality + * + * Flight status + * state + * state name + * + * Basic sensors + * acceleration (m/s²) + * pressure (mBar) + * altitude (m) + * height (m) + * accelerometer speed (m/s) + * barometer speed (m/s) + * temp (°C) + * battery (V) + * drogue (V) + * main (V) + * + * GPS data + * connected (1/0) + * locked (1/0) + * nsat (used for solution) + * latitude (°) + * longitude (°) + * altitude (m) + * year (e.g. 2010) + * month (1-12) + * day (1-31) + * hour (0-23) + * minute (0-59) + * second (0-59) + * from_pad_dist (m) + * from_pad_azimuth (deg true) + * from_pad_range (m) + * from_pad_elevation (deg from horizon) + * hdop + * + * GPS Sat data + * C/N0 data for all 32 valid SDIDs + */ + + void write_general_header() { + out.printf("version,serial,flight,call,time,rssi,lqi"); + } + + void write_general(AltosRecord record) { + out.printf("%s, %d, %d, %s, %8.2f, %4d, %3d", + ALTOS_CSV_VERSION, record.serial, record.flight, record.callsign, + (double) record.time, + record.rssi, + record.status & 0x7f); + } + + void write_flight_header() { + out.printf("state,state_name"); + } + + void write_flight(AltosRecord record) { + out.printf("%d,%8s", record.state, record.state()); + } + + void write_basic_header() { + out.printf("acceleration,pressure,altitude,height,accel_speed,baro_speed,temperature,battery_voltage,drogue_voltage,main_voltage"); + } + + void write_basic(AltosRecord record) { + out.printf("%8.2f,%10.2f,%8.2f,%8.2f,%8.2f,%8.2f,%5.1f,%5.2f,%5.2f,%5.2f", + record.acceleration(), + record.raw_pressure(), + record.raw_altitude(), + record.raw_height(), + record.accel_speed(), + state.baro_speed, + record.temperature(), + record.battery_voltage(), + record.drogue_voltage(), + record.main_voltage()); + } + + void write_gps_header() { + out.printf("connected,locked,nsat,latitude,longitude,altitude,year,month,day,hour,minute,second,pad_dist,pad_range,pad_az,pad_el,hdop"); + } + + void write_gps(AltosRecord record) { + AltosGPS gps = record.gps; + if (gps == null) + gps = new AltosGPS(); + + AltosGreatCircle from_pad = state.from_pad; + if (from_pad == null) + from_pad = new AltosGreatCircle(); + + out.printf("%2d,%2d,%3d,%12.7f,%12.7f,%6d,%5d,%3d,%3d,%3d,%3d,%3d,%9.0f,%9.0f,%4.0f,%4.0f,%6.1f", + gps.connected?1:0, + gps.locked?1:0, + gps.nsat, + gps.lat, + gps.lon, + gps.alt, + gps.year, + gps.month, + gps.day, + gps.hour, + gps.minute, + gps.second, + from_pad.distance, + state.range, + from_pad.bearing, + state.elevation, + gps.hdop); + } + + void write_gps_sat_header() { + for(int i = 1; i <= 32; i++) { + out.printf("sat%02d", i); + if (i != 32) + out.printf(","); + } + } + + void write_gps_sat(AltosRecord record) { + AltosGPS gps = record.gps; + for(int i = 1; i <= 32; i++) { + int c_n0 = 0; + if (gps != null && gps.cc_gps_sat != null) { + for(int j = 0; j < gps.cc_gps_sat.length; j++) + if (gps.cc_gps_sat[j].svid == i) { + c_n0 = gps.cc_gps_sat[j].c_n0; + break; + } + } + out.printf ("%3d", c_n0); + if (i != 32) + out.printf(","); + } + } + + void write_header() { + out.printf("#"); write_general_header(); + out.printf(","); write_flight_header(); + out.printf(","); write_basic_header(); + out.printf(","); write_gps_header(); + out.printf(","); write_gps_sat_header(); + out.printf ("\n"); + } + + void write_one(AltosRecord record) { + state = new AltosState(record, state); + write_general(record); out.printf(","); + write_flight(record); out.printf(","); + write_basic(record); out.printf(","); + write_gps(record); out.printf(","); + write_gps_sat(record); + out.printf ("\n"); + } + + void flush_pad() { + while (!pad_records.isEmpty()) { + write_one (pad_records.remove()); + } + } + + public void write(AltosRecord record) { + if (!header_written) { + write_header(); + header_written = true; + } + if (!seen_boost) { + if (record.state >= Altos.ao_flight_boost) { + seen_boost = true; + boost_tick = record.tick; + flush_pad(); + } + } + if (seen_boost) + write_one(record); + else + pad_records.add(record); + } + + public PrintStream out() { + return out; + } + + public void close() { + if (!pad_records.isEmpty()) { + boost_tick = pad_records.element().tick; + flush_pad(); + } + out.close(); + } + + public void write(AltosRecordIterable iterable) { + iterable.write_comments(out()); + for (AltosRecord r : iterable) + write(r); + } + + public AltosCSV(File in_name) throws FileNotFoundException { + name = in_name; + out = new PrintStream(name); + pad_records = new LinkedList(); + } + + public AltosCSV(String in_string) throws FileNotFoundException { + this(new File(in_string)); + } +} diff --git a/altosui/AltosCSVUI.java b/altosui/AltosCSVUI.java new file mode 100644 index 00000000..e1b6002d --- /dev/null +++ b/altosui/AltosCSVUI.java @@ -0,0 +1,108 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosCSVUI + extends JDialog + implements ActionListener +{ + JFileChooser csv_chooser; + JPanel accessory; + JComboBox combo_box; + AltosRecordIterable iterable; + AltosWriter writer; + + static String[] combo_box_items = { "Comma Separated Values (.CSV)", "Googleearth Data (.KML)" }; + + void set_default_file() { + File current = csv_chooser.getSelectedFile(); + String current_name = current.getName(); + String new_name = null; + String selected = (String) combo_box.getSelectedItem(); + + if (selected.contains("CSV")) + new_name = Altos.replace_extension(current_name, ".csv"); + 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 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()); + + 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); + + combo_box = new JComboBox(combo_box_items); + combo_box.addActionListener(this); + 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(); + try { + 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); + } + } + } +} diff --git a/altosui/AltosChannelMenu.java b/altosui/AltosChannelMenu.java new file mode 100644 index 00000000..abbb86f4 --- /dev/null +++ b/altosui/AltosChannelMenu.java @@ -0,0 +1,44 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosChannelMenu extends JComboBox implements ActionListener { + int channel; + + public AltosChannelMenu(int current_channel) { + + channel = current_channel; + + 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/altosui/AltosConfig.java b/altosui/AltosConfig.java new file mode 100644 index 00000000..1c42870f --- /dev/null +++ b/altosui/AltosConfig.java @@ -0,0 +1,295 @@ +/* + * 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.*; +import java.util.concurrent.*; + +import libaltosJNI.*; + +public class AltosConfig implements Runnable, ActionListener { + + class int_ref { + int value; + + public int get() { + return value; + } + public void set(int i) { + value = i; + } + public int_ref(int i) { + value = i; + } + } + + class string_ref { + String value; + + public String get() { + return value; + } + public void set(String i) { + value = i; + } + public string_ref(String i) { + value = i; + } + } + + JFrame owner; + AltosDevice device; + AltosSerial serial_line; + boolean remote; + Thread config_thread; + int_ref serial; + int_ref main_deploy; + int_ref apogee_delay; + int_ref radio_channel; + int_ref radio_calibration; + string_ref version; + string_ref product; + string_ref callsign; + AltosConfigUI config_ui; + boolean serial_started; + + boolean get_int(String line, String label, int_ref x) { + if (line.startsWith(label)) { + try { + String tail = line.substring(label.length()).trim(); + String[] tokens = tail.split("\\s+"); + if (tokens.length > 0) { + int i = Integer.parseInt(tokens[0]); + x.set(i); + return true; + } + } catch (NumberFormatException ne) { + } + } + return false; + } + + 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; + } + } + + void start_serial() throws InterruptedException { + serial_started = true; + if (remote) { + serial_line.set_radio(); + serial_line.printf("p\nE 0\n"); + serial_line.flush_input(); + } + } + + void stop_serial() throws InterruptedException { + if (!serial_started) + return; + serial_started = false; + if (remote) { + serial_line.printf("~"); + serial_line.flush_output(); + } + } + + void get_data() throws InterruptedException, TimeoutException { + try { + start_serial(); + serial_line.printf("c s\nv\n"); + for (;;) { + String line = serial_line.get_reply(5000); + 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); + get_int(line, "Radio channel:", radio_channel); + get_int(line, "Radio cal:", radio_calibration); + get_string(line, "Callsign:", callsign); + get_string(line,"software-version", version); + get_string(line,"product", product); + + /* signals the end of the version info */ + if (line.startsWith("software-version")) + break; + } + } finally { + stop_serial(); + } + } + + void init_ui () throws InterruptedException, TimeoutException { + config_ui = new AltosConfigUI(owner, remote); + config_ui.addActionListener(this); + set_ui(); + } + + void abort() { + JOptionPane.showMessageDialog(owner, + String.format("Connection to \"%s\" failed", + device.toShortString()), + "Connection Failed", + JOptionPane.ERROR_MESSAGE); + try { + stop_serial(); + } catch (InterruptedException ie) { + } + 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() { + } + + void save_data() { + main_deploy.set(config_ui.main_deploy()); + apogee_delay.set(config_ui.apogee_delay()); + radio_channel.set(config_ui.radio_channel()); + radio_calibration.set(config_ui.radio_calibration()); + callsign.set(config_ui.callsign()); + try { + start_serial(); + serial_line.printf("c m %d\n", main_deploy.get()); + serial_line.printf("c d %d\n", apogee_delay.get()); + if (!remote) { + serial_line.printf("c r %d\n", radio_channel.get()); + serial_line.printf("c f %d\n", radio_calibration.get()); + } + serial_line.printf("c c %s\n", callsign.get()); + serial_line.printf("c w\n"); + } catch (InterruptedException ie) { + } finally { + try { + stop_serial(); + } catch (InterruptedException ie) { + } + } + } + + public void actionPerformed(ActionEvent e) { + String cmd = e.getActionCommand(); + 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"); + serial_line.flush_output(); + stop_serial(); + serial_line.close(); + } + } else if (cmd.equals("Close")) { + if (serial_line != null) + serial_line.close(); + } + } catch (InterruptedException ie) { + abort(); + } catch (TimeoutException te) { + abort(); + } + } + + public void run () { + try { + init_ui(); + config_ui.make_visible(); + } catch (InterruptedException ie) { + abort(); + } catch (TimeoutException te) { + abort(); + } + } + + public AltosConfig(JFrame given_owner) { + owner = given_owner; + + serial = new int_ref(0); + main_deploy = new int_ref(250); + apogee_delay = new int_ref(0); + radio_channel = new int_ref(0); + radio_calibration = new int_ref(1186611); + callsign = new string_ref("N0CALL"); + version = new string_ref("unknown"); + product = new string_ref("unknown"); + + device = AltosDeviceDialog.show(owner, AltosDevice.product_any); + if (device != null) { + try { + serial_line = new AltosSerial(device); + if (!device.matchProduct(AltosDevice.product_telemetrum)) + remote = true; + config_thread = new Thread(this); + config_thread.start(); + } catch (FileNotFoundException ee) { + JOptionPane.showMessageDialog(owner, + String.format("Cannot open device \"%s\"", + device.toShortString()), + "Cannot open target device", + JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(owner, + String.format("Device \"%s\" already in use", + device.toShortString()), + "Device in use", + JOptionPane.ERROR_MESSAGE); + } catch (IOException ee) { + JOptionPane.showMessageDialog(owner, + device.toShortString(), + ee.getLocalizedMessage(), + JOptionPane.ERROR_MESSAGE); + } + } + } +} \ No newline at end of file diff --git a/altosui/AltosConfigUI.java b/altosui/AltosConfigUI.java new file mode 100644 index 00000000..cfa5d7b9 --- /dev/null +++ b/altosui/AltosConfigUI.java @@ -0,0 +1,466 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +import libaltosJNI.*; + +public class AltosConfigUI + extends JDialog + implements ActionListener, ItemListener, DocumentListener +{ + + Container pane; + Box box; + JLabel product_label; + JLabel version_label; + JLabel serial_label; + JLabel main_deploy_label; + JLabel apogee_delay_label; + JLabel radio_channel_label; + JLabel radio_calibration_label; + JLabel callsign_label; + + public boolean dirty; + + JFrame owner; + JLabel product_value; + JLabel version_value; + JLabel serial_value; + JComboBox main_deploy_value; + JComboBox apogee_delay_value; + JComboBox radio_channel_value; + JTextField radio_calibration_value; + JTextField callsign_value; + + JButton save; + JButton reset; + JButton reboot; + JButton close; + + ActionListener listener; + + static String[] main_deploy_values = { + "100", "150", "200", "250", "300", "350", + "400", "450", "500" + }; + + static String[] apogee_delay_values = { + "0", "1", "2", "3", "4", "5" + }; + + static String[] radio_channel_values = new String[10]; + { + for (int i = 0; i <= 9; i++) + radio_channel_values[i] = String.format("Channel %1d (%7.3fMHz)", + i, 434.550 + i * 0.1); + } + + /* A window listener to catch closing events and tell the config code */ + class ConfigListener extends WindowAdapter { + AltosConfigUI ui; + + public ConfigListener(AltosConfigUI this_ui) { + ui = this_ui; + } + + public void windowClosing(WindowEvent e) { + ui.actionPerformed(new ActionEvent(e.getSource(), + ActionEvent.ACTION_PERFORMED, + "Close")); + } + } + + /* Build the UI using a grid bag */ + public AltosConfigUI(JFrame in_owner, boolean remote) { + super (in_owner, "Configure TeleMetrum", false); + + owner = in_owner; + GridBagConstraints c; + + Insets il = new Insets(4,4,4,4); + Insets ir = new Insets(4,4,4,4); + + pane = getContentPane(); + pane.setLayout(new GridBagLayout()); + + /* Product */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 0; + c.gridwidth = 4; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + product_label = new JLabel("Product:"); + pane.add(product_label, c); + + c = new GridBagConstraints(); + c.gridx = 4; c.gridy = 0; + c.gridwidth = 4; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + product_value = new JLabel(""); + pane.add(product_value, c); + + /* Version */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 1; + c.gridwidth = 4; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + c.ipady = 5; + version_label = new JLabel("Software version:"); + pane.add(version_label, c); + + c = new GridBagConstraints(); + c.gridx = 4; c.gridy = 1; + c.gridwidth = 4; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + c.ipady = 5; + version_value = new JLabel(""); + pane.add(version_value, c); + + /* Serial */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 2; + c.gridwidth = 4; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + c.ipady = 5; + serial_label = new JLabel("Serial:"); + pane.add(serial_label, c); + + c = new GridBagConstraints(); + c.gridx = 4; c.gridy = 2; + c.gridwidth = 4; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + c.ipady = 5; + serial_value = new JLabel(""); + pane.add(serial_value, c); + + /* Main deploy */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 3; + c.gridwidth = 4; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + c.ipady = 5; + main_deploy_label = new JLabel("Main Deploy Altitude(m):"); + pane.add(main_deploy_label, c); + + c = new GridBagConstraints(); + c.gridx = 4; c.gridy = 3; + c.gridwidth = 4; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + c.ipady = 5; + main_deploy_value = new JComboBox(main_deploy_values); + main_deploy_value.setEditable(true); + main_deploy_value.addItemListener(this); + pane.add(main_deploy_value, c); + + /* Apogee delay */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 4; + c.gridwidth = 4; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + c.ipady = 5; + apogee_delay_label = new JLabel("Apogee Delay(s):"); + pane.add(apogee_delay_label, c); + + c = new GridBagConstraints(); + c.gridx = 4; c.gridy = 4; + c.gridwidth = 4; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + c.ipady = 5; + apogee_delay_value = new JComboBox(apogee_delay_values); + apogee_delay_value.setEditable(true); + apogee_delay_value.addItemListener(this); + pane.add(apogee_delay_value, c); + + /* Radio channel */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 5; + c.gridwidth = 4; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + c.ipady = 5; + radio_channel_label = new JLabel("Radio Channel:"); + pane.add(radio_channel_label, c); + + c = new GridBagConstraints(); + c.gridx = 4; c.gridy = 5; + c.gridwidth = 4; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + c.ipady = 5; + radio_channel_value = new JComboBox(radio_channel_values); + radio_channel_value.setEditable(false); + radio_channel_value.addItemListener(this); + if (remote) + radio_channel_value.setEnabled(false); + pane.add(radio_channel_value, c); + + /* Radio Calibration */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 6; + c.gridwidth = 4; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + c.ipady = 5; + radio_calibration_label = new JLabel("RF Calibration:"); + pane.add(radio_calibration_label, c); + + c = new GridBagConstraints(); + c.gridx = 4; c.gridy = 6; + c.gridwidth = 4; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + c.ipady = 5; + radio_calibration_value = new JTextField(String.format("%d", 1186611)); + radio_calibration_value.getDocument().addDocumentListener(this); + if (remote) + radio_calibration_value.setEnabled(false); + pane.add(radio_calibration_value, c); + + /* Callsign */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 7; + c.gridwidth = 4; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + c.ipady = 5; + callsign_label = new JLabel("Callsign:"); + pane.add(callsign_label, c); + + c = new GridBagConstraints(); + c.gridx = 4; c.gridy = 7; + c.gridwidth = 4; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + c.ipady = 5; + callsign_value = new JTextField(AltosPreferences.callsign()); + callsign_value.getDocument().addDocumentListener(this); + pane.add(callsign_value, c); + + /* Buttons */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 8; + c.gridwidth = 2; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + save = new JButton("Save"); + pane.add(save, c); + save.addActionListener(this); + save.setActionCommand("Save"); + + c = new GridBagConstraints(); + c.gridx = 2; c.gridy = 8; + c.gridwidth = 2; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.insets = il; + reset = new JButton("Reset"); + pane.add(reset, c); + reset.addActionListener(this); + reset.setActionCommand("Reset"); + + c = new GridBagConstraints(); + c.gridx = 4; c.gridy = 8; + c.gridwidth = 2; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.insets = il; + reboot = new JButton("Reboot"); + pane.add(reboot, c); + reboot.addActionListener(this); + reboot.setActionCommand("Reboot"); + + c = new GridBagConstraints(); + c.gridx = 6; c.gridy = 8; + c.gridwidth = 2; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_END; + c.insets = il; + close = new JButton("Close"); + pane.add(close, c); + close.addActionListener(this); + close.setActionCommand("Close"); + + addWindowListener(new ConfigListener(this)); + } + + /* Once the initial values are set, the config code will show the dialog */ + public void make_visible() { + pack(); + setLocationRelativeTo(owner); + setVisible(true); + } + + /* If any values have been changed, confirm before closing */ + public boolean check_dirty(String operation) { + if (dirty) { + Object[] options = { String.format("%s anyway", operation), "Keep editing" }; + int i; + i = JOptionPane.showOptionDialog(this, + String.format("Configuration modified. %s anyway?", operation), + "Configuration Modified", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[1]); + if (i != 0) + return false; + } + return true; + } + + /* Listen for events from our buttons */ + public void actionPerformed(ActionEvent e) { + String cmd = e.getActionCommand(); + + if (cmd.equals("Close") || cmd.equals("Reboot")) + if (!check_dirty(cmd)) + return; + listener.actionPerformed(e); + if (cmd.equals("Close") || cmd.equals("Reboot")) { + setVisible(false); + dispose(); + } + dirty = false; + } + + /* ItemListener interface method */ + public void itemStateChanged(ItemEvent e) { + dirty = true; + } + + /* DocumentListener interface methods */ + public void changedUpdate(DocumentEvent e) { + dirty = true; + } + + public void insertUpdate(DocumentEvent e) { + dirty = true; + } + + public void removeUpdate(DocumentEvent e) { + dirty = true; + } + + /* Let the config code hook on a listener */ + public void addActionListener(ActionListener l) { + listener = l; + } + + /* set and get all of the dialog values */ + public void set_product(String product) { + product_value.setText(product); + } + + public void set_version(String version) { + version_value.setText(version); + } + + public void set_serial(int serial) { + serial_value.setText(String.format("%d", serial)); + } + + public void set_main_deploy(int new_main_deploy) { + main_deploy_value.setSelectedItem(Integer.toString(new_main_deploy)); + } + + public int main_deploy() { + return Integer.parseInt(main_deploy_value.getSelectedItem().toString()); + } + + public void set_apogee_delay(int new_apogee_delay) { + apogee_delay_value.setSelectedItem(Integer.toString(new_apogee_delay)); + } + + public int apogee_delay() { + return Integer.parseInt(apogee_delay_value.getSelectedItem().toString()); + } + + public void set_radio_channel(int new_radio_channel) { + radio_channel_value.setSelectedIndex(new_radio_channel); + } + + public int radio_channel() { + return radio_channel_value.getSelectedIndex(); + } + + public void set_radio_calibration(int new_radio_calibration) { + radio_calibration_value.setText(String.format("%d", new_radio_calibration)); + } + + public int radio_calibration() { + return Integer.parseInt(radio_calibration_value.getText()); + } + + public void set_callsign(String new_callsign) { + callsign_value.setText(new_callsign); + } + + public String callsign() { + return callsign_value.getText(); + } + + public void set_clean() { + dirty = false; + } + + } \ No newline at end of file diff --git a/altosui/AltosConfigureUI.java b/altosui/AltosConfigureUI.java new file mode 100644 index 00000000..153c59fd --- /dev/null +++ b/altosui/AltosConfigureUI.java @@ -0,0 +1,187 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosConfigureUI + extends JDialog + implements DocumentListener +{ + JFrame owner; + AltosVoice voice; + Container pane; + + JRadioButton enable_voice; + JButton test_voice; + JButton close; + + JButton configure_log; + JTextField log_directory; + + JLabel callsign_label; + JTextField callsign_value; + + /* DocumentListener interface methods */ + public void changedUpdate(DocumentEvent e) { + AltosPreferences.set_callsign(callsign_value.getText()); + } + + public void insertUpdate(DocumentEvent e) { + changedUpdate(e); + } + + public void removeUpdate(DocumentEvent e) { + changedUpdate(e); + } + + public AltosConfigureUI(JFrame in_owner, AltosVoice in_voice) { + super(in_owner, "Configure AltosUI", false); + + GridBagConstraints c; + + Insets insets = new Insets(4, 4, 4, 4); + + owner = in_owner; + voice = in_voice; + pane = getContentPane(); + pane.setLayout(new GridBagLayout()); + + c = new GridBagConstraints(); + c.insets = insets; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + + /* Nice label at the top */ + c.gridx = 0; + c.gridy = 0; + 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(); + boolean enabled = item.isSelected(); + AltosPreferences.set_voice(enabled); + if (enabled) + voice.speak_always("Enable voice."); + else + voice.speak_always("Disable voice."); + } + }); + c.gridx = 1; + 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) { + voice.speak("That's one small step for man; one giant leap for mankind."); + } + }); + pane.add(test_voice, c); + + /* 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(); + configure_log.setText(AltosPreferences.logdir().getPath()); + } + }); + c.gridx = 1; + c.gridy = 2; + c.gridwidth = 2; + c.fill = GridBagConstraints.BOTH; + c.anchor = GridBagConstraints.WEST; + pane.add(configure_log, c); + + /* Callsign setting */ + c.gridx = 0; + c.gridy = 3; + 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) { + setVisible(false); + } + }); + c.gridx = 0; + c.gridy = 4; + c.gridwidth = 3; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + pane.add(close, c); + + pack(); + setLocationRelativeTo(owner); + setVisible(true); + } +} diff --git a/altosui/AltosConvert.java b/altosui/AltosConvert.java new file mode 100644 index 00000000..8cc1df27 --- /dev/null +++ b/altosui/AltosConvert.java @@ -0,0 +1,192 @@ +/* + * 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. + */ + +/* + * Sensor data conversion functions + */ +package altosui; + +public class AltosConvert { + /* + * Pressure Sensor Model, version 1.1 + * + * written by Holly Grimes + * + * Uses the International Standard Atmosphere as described in + * "A Quick Derivation relating altitude to air pressure" (version 1.03) + * from the Portland State Aerospace Society, except that the atmosphere + * is divided into layers with each layer having a different lapse rate. + * + * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007 + * at site MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */ + return 0; + + /* calculate the base temperature and pressure for the atmospheric layer + associated with the inputted altitude */ + for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) { + delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + base_pressure *= Math.exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + base_pressure *= Math.pow(base, exponent); + } + base_temperature += delta_z * lapse_rate[layer_number]; + } + + /* calculate the pressure at the inputted altitude */ + delta_z = altitude - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + pressure = base_pressure * Math.exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + pressure = base_pressure * Math.pow(base, exponent); + } + + return pressure; + } + + +/* outputs the altitude associated with the given pressure. the altitude + returned is measured with respect to the mean sea level */ + static double + pressure_to_altitude(double pressure) + { + + double next_base_temperature = LAYER0_BASE_TEMPERATURE; + double next_base_pressure = LAYER0_BASE_PRESSURE; + + double altitude; + double base_pressure; + double base_temperature; + double base; /* base for function to determine base pressure of next layer */ + double exponent; /* exponent for function to determine base pressure + of next layer */ + double coefficient; + int layer_number; /* identifies layer in the atmosphere */ + int delta_z; /* difference between two altitudes */ + + if (pressure < 0) /* illegal pressure */ + return -1; + if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */ + return MAXIMUM_ALTITUDE; + + /* calculate the base temperature and pressure for the atmospheric layer + associated with the inputted pressure. */ + layer_number = -1; + do { + layer_number++; + base_pressure = next_base_pressure; + base_temperature = next_base_temperature; + delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + next_base_pressure *= Math.exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + next_base_pressure *= Math.pow(base, exponent); + } + next_base_temperature += delta_z * lapse_rate[layer_number]; + } + while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure); + + /* calculate the altitude associated with the inputted pressure */ + if (lapse_rate[layer_number] == 0.0) { + coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION) + * base_temperature; + altitude = base_altitude[layer_number] + + coefficient * Math.log(pressure / base_pressure); + } + else { + base = pressure / base_pressure; + exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number] + / GRAVITATIONAL_ACCELERATION; + coefficient = base_temperature / lapse_rate[layer_number]; + altitude = base_altitude[layer_number] + + coefficient * (Math.pow(base, exponent) - 1); + } + + return altitude; + } + + static double + cc_battery_to_voltage(double battery) + { + return battery / 32767.0 * 5.0; + } + + static double + cc_ignitor_to_voltage(double ignite) + { + return ignite / 32767 * 15.0; + } +} diff --git a/altosui/AltosDataChooser.java b/altosui/AltosDataChooser.java new file mode 100644 index 00000000..15de05c2 --- /dev/null +++ b/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/altosui/AltosDataPoint.java b/altosui/AltosDataPoint.java new file mode 100644 index 00000000..66313e03 --- /dev/null +++ b/altosui/AltosDataPoint.java @@ -0,0 +1,29 @@ + +// Copyright (c) 2010 Anthony Towns +// GPL v2 or later + +package altosui; + +interface AltosDataPoint { + int version(); + int serial(); + int flight(); + String callsign(); + double time(); + double rssi(); + + int state(); + String state_name(); + + double acceleration(); + double pressure(); + double altitude(); + double height(); + double accel_speed(); + double baro_speed(); + double temperature(); + double battery_voltage(); + double drogue_voltage(); + double main_voltage(); +} + diff --git a/altosui/AltosDataPointReader.java b/altosui/AltosDataPointReader.java new file mode 100644 index 00000000..7704310b --- /dev/null +++ b/altosui/AltosDataPointReader.java @@ -0,0 +1,72 @@ + +// Copyright (c) 2010 Anthony Towns +// GPL v2 or later + +package altosui; + +import java.io.IOException; +import java.text.ParseException; +import java.lang.UnsupportedOperationException; +import java.util.NoSuchElementException; +import java.util.Iterator; + +class AltosDataPointReader implements Iterable { + Iterator iter; + AltosState state; + AltosRecord record; + + public AltosDataPointReader(Iterable reader) { + this.iter = reader.iterator(); + this.state = null; + } + + private void read_next_record() + throws NoSuchElementException + { + record = iter.next(); + state = new AltosState(record, state); + } + + private AltosDataPoint current_dp() { + assert this.record != null; + + return new AltosDataPoint() { + public int version() { return record.version; } + public int serial() { return record.serial; } + public int flight() { return record.flight; } + public String callsign() { return record.callsign; } + public double time() { return record.time; } + public double rssi() { return record.rssi; } + + public int state() { return record.state; } + public String state_name() { return record.state(); } + + public double acceleration() { return record.acceleration(); } + public double pressure() { return record.raw_pressure(); } + public double altitude() { return record.raw_altitude(); } + public double height() { return record.raw_height(); } + public double accel_speed() { return record.accel_speed(); } + public double baro_speed() { return state.baro_speed; } + public double temperature() { return record.temperature(); } + public double battery_voltage() { return record.battery_voltage(); } + public double drogue_voltage() { return record.drogue_voltage(); } + public double main_voltage() { return record.main_voltage(); } + }; + } + + public Iterator iterator() { + return new Iterator() { + public void remove() { + throw new UnsupportedOperationException(); + } + public boolean hasNext() { + return iter.hasNext(); + } + public AltosDataPoint next() { + read_next_record(); + return current_dp(); + } + }; + } +} + diff --git a/altosui/AltosDebug.java b/altosui/AltosDebug.java new file mode 100644 index 00000000..8d435b66 --- /dev/null +++ b/altosui/AltosDebug.java @@ -0,0 +1,267 @@ +/* + * 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.lang.*; +import java.io.*; +import java.util.concurrent.*; +import java.util.*; + +import libaltosJNI.*; + +public class AltosDebug extends AltosSerial { + + public static final byte WR_CONFIG = 0x1d; + public static final byte RD_CONFIG = 0x24; + public static final byte CONFIG_TIMERS_OFF = (1 << 3); + public static final byte CONFIG_DMA_PAUSE = (1 << 2); + public static final byte CONFIG_TIMER_SUSPEND = (1 << 1); + public static final byte SET_FLASH_INFO_PAGE = (1 << 0); + + public static final byte GET_PC = 0x28; + public static final byte READ_STATUS = 0x34; + public static final byte STATUS_CHIP_ERASE_DONE = (byte) (1 << 7); + public static final byte STATUS_PCON_IDLE = (1 << 6); + public static final byte STATUS_CPU_HALTED = (1 << 5); + public static final byte STATUS_POWER_MODE_0 = (1 << 4); + public static final byte STATUS_HALT_STATUS = (1 << 3); + public static final byte STATUS_DEBUG_LOCKED = (1 << 2); + public static final byte STATUS_OSCILLATOR_STABLE = (1 << 1); + public static final byte STATUS_STACK_OVERFLOW = (1 << 0); + + public static final byte SET_HW_BRKPNT = 0x3b; + public static byte HW_BRKPNT_N(byte n) { return (byte) ((n) << 3); } + public static final byte HW_BRKPNT_N_MASK = (0x3 << 3); + public static final byte HW_BRKPNT_ENABLE = (1 << 2); + + public static final byte HALT = 0x44; + public static final byte RESUME = 0x4c; + public static byte DEBUG_INSTR(byte n) { return (byte) (0x54|(n)); } + public static final byte STEP_INSTR = 0x5c; + public static byte STEP_REPLACE(byte n) { return (byte) (0x64|(n)); } + public static final byte GET_CHIP_ID = 0x68; + + + boolean debug_mode; + + void ensure_debug_mode() { + if (!debug_mode) { + printf("D\n"); + flush_input(); + debug_mode = true; + } + } + + void dump_memory(String header, int address, byte[] bytes, int start, int len) { + System.out.printf("%s\n", header); + for (int j = 0; j < len; j++) { + if ((j & 15) == 0) { + if (j != 0) + System.out.printf("\n"); + System.out.printf ("%04x:", address + j); + } + System.out.printf(" %02x", bytes[start + j]); + } + System.out.printf("\n"); + } + + /* + * Write target memory + */ + public void write_memory(int address, byte[] bytes, int start, int len) { + ensure_debug_mode(); +// dump_memory("write_memory", address, bytes, start, len); + printf("O %x %x\n", len, address); + for (int i = 0; i < len; i++) + printf("%02x", bytes[start + i]); + } + + public void write_memory(int address, byte[] bytes) { + write_memory(address, bytes, 0, bytes.length); + } + + /* + * Read target memory + */ + public byte[] read_memory(int address, int length) + throws IOException, InterruptedException { + byte[] data = new byte[length]; + + flush_input(); + ensure_debug_mode(); + printf("I %x %x\n", length, address); + int i = 0; + int start = 0; + while (i < length) { + String line = get_reply().trim(); + if (!Altos.ishex(line) || line.length() % 2 != 0) + throw new IOException( + String.format + ("Invalid reply \"%s\"", line)); + int this_time = line.length() / 2; + for (int j = 0; j < this_time; j++) + data[start + j] = (byte) ((Altos.fromhex(line.charAt(j*2)) << 4) + + Altos.fromhex(line.charAt(j*2+1))); + start += this_time; + i += this_time; + } +// dump_memory("read_memory", address, data, 0, length); + + return data; + } + + /* + * Write raw bytes to the debug link using the 'P' command + */ + public void write_bytes(byte[] bytes) throws IOException { + int i = 0; + ensure_debug_mode(); + while (i < bytes.length) { + int this_time = bytes.length - i; + if (this_time > 8) + this_time = 0; + printf("P"); + for (int j = 0; j < this_time; j++) + printf(" %02x", bytes[i+j]); + printf("\n"); + i += this_time; + } + } + + public void write_byte(byte b) throws IOException { + byte[] bytes = { b }; + write_bytes(bytes); + } + + /* + * Read raw bytes from the debug link using the 'G' command + */ + public byte[] read_bytes(int length) + throws IOException, InterruptedException { + + flush_input(); + ensure_debug_mode(); + printf("G %x\n", length); + int i = 0; + byte[] data = new byte[length]; + while (i < length) { + String line = get_reply().trim(); + String tokens[] = line.split("\\s+"); + for (int j = 0; j < tokens.length; j++) { + if (!Altos.ishex(tokens[j]) || + tokens[j].length() != 2) + throw new IOException( + String.format + ("Invalid read_bytes reply \"%s\"", line)); + try { + data[i + j] = (byte) Integer.parseInt(tokens[j], 16); + } catch (NumberFormatException ne) { + throw new IOException( + String.format + ("Invalid read_bytes reply \"%s\"", line)); + } + } + i += tokens.length; + } + return data; + } + + public byte read_byte() throws IOException, InterruptedException { + return read_bytes(1)[0]; + } + + public byte debug_instr(byte[] instruction) throws IOException, InterruptedException { + byte[] command = new byte[1 + instruction.length]; + command[0] = DEBUG_INSTR((byte) instruction.length); + for (int i = 0; i < instruction.length; i++) + command[i+1] = instruction[i]; + write_bytes(command); + return read_byte(); + } + + public byte resume() throws IOException, InterruptedException { + write_byte(RESUME); + return read_byte(); + } + + public int read_uint16() throws IOException, InterruptedException { + byte[] d = read_bytes(2); + return ((int) (d[0] & 0xff) << 8) | (d[1] & 0xff); + } + + public int read_uint8() throws IOException, InterruptedException { + byte[] d = read_bytes(1); + return (int) (d[0] & 0xff); + } + + public int get_chip_id() throws IOException, InterruptedException { + write_byte(GET_CHIP_ID); + return read_uint16(); + } + + public int get_pc() throws IOException, InterruptedException { + write_byte(GET_PC); + return read_uint16(); + } + + public byte read_status() throws IOException, InterruptedException { + write_byte(READ_STATUS); + return read_byte(); + } + + static final byte LJMP = 0x02; + + public void set_pc(int pc) throws IOException, InterruptedException { + byte high = (byte) (pc >> 8); + byte low = (byte) pc; + byte[] jump_mem = { LJMP, high, low }; + debug_instr(jump_mem); + } + + public boolean check_connection() throws IOException, InterruptedException { + byte reply = read_status(); + if ((reply & STATUS_CHIP_ERASE_DONE) == 0) + return false; + if ((reply & STATUS_PCON_IDLE) != 0) + return false; + if ((reply & STATUS_POWER_MODE_0) == 0) + return false; + return true; + } + + public AltosRomconfig romconfig() { + try { + byte[] bytes = read_memory(0xa0, 10); + return new AltosRomconfig(bytes, 0); + } catch (IOException ie) { + } catch (InterruptedException ie) { + } + return new AltosRomconfig(); + } + + /* + * Reset target + */ + public void reset() { + printf ("R\n"); + } + + public AltosDebug (AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException { + super(in_device); + } +} \ No newline at end of file diff --git a/altosui/AltosDescent.java b/altosui/AltosDescent.java new file mode 100644 index 00000000..16ccd458 --- /dev/null +++ b/altosui/AltosDescent.java @@ -0,0 +1,353 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosDescent extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + + public abstract class DescentStatus { + JLabel label; + JTextField value; + AltosLights lights; + + abstract void show(AltosState state, int crc_errors); + void reset() { + value.setText(""); + lights.set(false); + } + + public DescentStatus (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + lights = new AltosLights(); + c.gridx = 0; c.gridy = y; + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(lights, c); + add(lights); + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.gridwidth = 3; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(Altos.text_width); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 4; c.gridy = y; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + + } + } + + public abstract class DescentValue { + JLabel label; + JTextField value; + + void reset() { + value.setText(""); + } + + abstract void show(AltosState state, int crc_errors); + + void show(String format, double v) { + value.setText(String.format(format, v)); + } + + void show(String v) { + value.setText(v); + } + + public DescentValue (GridBagLayout layout, int x, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = x + 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + add(label, c); + + value = new JTextField(Altos.text_width); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = x + 2; c.gridy = y; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + add(value, c); + } + } + + public abstract class DescentDualValue { + JLabel label; + JTextField value1; + JTextField value2; + + void reset() { + value1.setText(""); + value2.setText(""); + } + + abstract void show(AltosState state, int crc_errors); + void show(String v1, String v2) { + value1.setText(v1); + value2.setText(v2); + } + void show(String f1, double v1, String f2, double v2) { + value1.setText(String.format(f1, v1)); + value2.setText(String.format(f2, v2)); + } + + public DescentDualValue (GridBagLayout layout, int x, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = x + 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value1 = new JTextField(Altos.text_width); + value1.setFont(Altos.value_font); + value1.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = x + 2; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value1, c); + add(value1); + + value2 = new JTextField(Altos.text_width); + value2.setFont(Altos.value_font); + value2.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = x + 4; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.gridwidth = 1; + layout.setConstraints(value2, c); + add(value2); + } + } + + class Height extends DescentValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m", state.height); + } + public Height (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Height"); + } + } + + Height height; + + class Speed extends DescentValue { + void show (AltosState state, int crc_errors) { + double speed = state.speed; + if (!state.ascent) + speed = state.baro_speed; + show("%6.0f m/s", speed); + } + public Speed (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Speed"); + } + } + + Speed speed; + + String pos(double p, String pos, String neg) { + String h = pos; + if (p < 0) { + h = neg; + p = -p; + } + int deg = (int) Math.floor(p); + double min = (p - Math.floor(p)) * 60.0; + return String.format("%s %d° %9.6f", h, deg, min); + } + + class Lat extends DescentValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + show(pos(state.gps.lat,"N", "S")); + else + show("???"); + } + public Lat (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Latitude"); + } + } + + Lat lat; + + class Lon extends DescentValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + show(pos(state.gps.lon,"W", "E")); + else + show("???"); + } + public Lon (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Longitude"); + } + } + + Lon lon; + + class Apogee extends DescentStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.drogue_sense)); + lights.set(state.drogue_sense > 3.2); + } + public Apogee (GridBagLayout layout, int y) { + super(layout, y, "Apogee Igniter Voltage"); + } + } + + Apogee apogee; + + class Main extends DescentStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.main_sense)); + lights.set(state.main_sense > 3.2); + } + public Main (GridBagLayout layout, int y) { + super(layout, y, "Main Igniter Voltage"); + } + } + + Main main; + + class Bearing extends DescentDualValue { + void show (AltosState state, int crc_errors) { + if (state.from_pad != null) { + show( String.format("%3.0f°", state.from_pad.bearing), + state.from_pad.bearing_words( + AltosGreatCircle.BEARING_LONG)); + } else { + show("???", "???"); + } + } + public Bearing (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Bearing"); + } + } + + Bearing bearing; + + class Range extends DescentValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m", state.range); + } + public Range (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Range"); + } + } + + Range range; + + class Elevation extends DescentValue { + void show (AltosState state, int crc_errors) { + show("%3.0f°", state.elevation); + } + public Elevation (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Elevation"); + } + } + + Elevation elevation; + + public void reset() { + lat.reset(); + lon.reset(); + height.reset(); + speed.reset(); + bearing.reset(); + range.reset(); + elevation.reset(); + main.reset(); + apogee.reset(); + } + + public void show(AltosState state, int crc_errors) { + height.show(state, crc_errors); + speed.show(state, crc_errors); + bearing.show(state, crc_errors); + range.show(state, crc_errors); + elevation.show(state, crc_errors); + lat.show(state, crc_errors); + lon.show(state, crc_errors); + main.show(state, crc_errors); + apogee.show(state, crc_errors); + } + + public AltosDescent() { + layout = new GridBagLayout(); + + setLayout(layout); + + /* Elements in descent display */ + speed = new Speed(layout, 0, 0); + height = new Height(layout, 2, 0); + elevation = new Elevation(layout, 0, 1); + range = new Range(layout, 2, 1); + bearing = new Bearing(layout, 0, 2); + lat = new Lat(layout, 0, 3); + lon = new Lon(layout, 2, 3); + + apogee = new Apogee(layout, 4); + main = new Main(layout, 5); + } +} diff --git a/altosui/AltosDevice.java b/altosui/AltosDevice.java new file mode 100644 index 00000000..f0fda57b --- /dev/null +++ b/altosui/AltosDevice.java @@ -0,0 +1,170 @@ +/* + * 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.lang.*; +import java.util.*; +import libaltosJNI.*; + +public class AltosDevice extends altos_device { + + static public boolean initialized = false; + static public boolean loaded_library = false; + + public static boolean load_library() { + if (!initialized) { + try { + System.loadLibrary("altos"); + libaltos.altos_init(); + loaded_library = true; + } catch (UnsatisfiedLinkError e) { + loaded_library = false; + } + initialized = true; + } + return loaded_library; + } + + static int usb_vendor_altusmetrum() { + if (load_library()) + return libaltosConstants.USB_VENDOR_ALTUSMETRUM; + return 0x000a; + } + + static int usb_product_altusmetrum() { + if (load_library()) + return libaltosConstants.USB_PRODUCT_ALTUSMETRUM; + return 0x000a; + } + + static int usb_product_altusmetrum_min() { + if (load_library()) + return libaltosConstants.USB_PRODUCT_ALTUSMETRUM_MIN; + return 0x000a; + } + + static int usb_product_altusmetrum_max() { + if (load_library()) + return libaltosConstants.USB_PRODUCT_ALTUSMETRUM_MAX; + return 0x000d; + } + + static int usb_product_telemetrum() { + if (load_library()) + return libaltosConstants.USB_PRODUCT_TELEMETRUM; + return 0x000b; + } + + static int usb_product_teledongle() { + if (load_library()) + return libaltosConstants.USB_PRODUCT_TELEDONGLE; + return 0x000c; + } + + static int usb_product_teleterra() { + if (load_library()) + return libaltosConstants.USB_PRODUCT_TELETERRA; + return 0x000d; + } + + public final static int vendor_altusmetrum = usb_vendor_altusmetrum(); + public final static int product_altusmetrum = usb_product_altusmetrum(); + public final static int product_telemetrum = usb_product_telemetrum(); + public final static int product_teledongle = usb_product_teledongle(); + public final static int product_teleterra = usb_product_teleterra(); + public final static int product_altusmetrum_min = usb_product_altusmetrum_min(); + public final static int product_altusmetrum_max = usb_product_altusmetrum_max(); + + + public final static int product_any = 0x10000; + public final static int product_basestation = 0x10000 + 1; + + public String toString() { + String name = getName(); + if (name == null) + name = "Altus Metrum"; + return String.format("%-20.20s %4d %s", + 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; + if (getProduct() < product_altusmetrum_min) + return false; + if (getProduct() > product_altusmetrum_max) + return false; + return true; + } + + public boolean matchProduct(int want_product) { + + if (!isAltusMetrum()) + return false; + + if (want_product == product_any) + return true; + + if (want_product == product_basestation) + return matchProduct(product_teledongle) || matchProduct(product_teleterra); + + int have_product = getProduct(); + + if (have_product == product_altusmetrum) /* old devices match any request */ + return true; + + if (want_product == have_product) + return true; + + return false; + } + + static AltosDevice[] list(int product) { + if (!load_library()) + return null; + + SWIGTYPE_p_altos_list list = libaltos.altos_list_start(); + + ArrayList device_list = new ArrayList(); + if (list != null) { + SWIGTYPE_p_altos_file file; + + for (;;) { + AltosDevice device = new AltosDevice(); + if (libaltos.altos_list_next(list, device) == 0) + break; + if (device.matchProduct(product)) + device_list.add(device); + } + libaltos.altos_list_finish(list); + } + + AltosDevice[] devices = new AltosDevice[device_list.size()]; + for (int i = 0; i < device_list.size(); i++) + devices[i] = device_list.get(i); + return devices; + } +} \ No newline at end of file diff --git a/altosui/AltosDeviceDialog.java b/altosui/AltosDeviceDialog.java new file mode 100644 index 00000000..2966ad1e --- /dev/null +++ b/altosui/AltosDeviceDialog.java @@ -0,0 +1,164 @@ +/* + * 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.lang.*; +import java.util.*; +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import libaltosJNI.libaltos; +import libaltosJNI.altos_device; +import libaltosJNI.SWIGTYPE_p_altos_file; +import libaltosJNI.SWIGTYPE_p_altos_list; + +public class AltosDeviceDialog extends JDialog implements ActionListener { + + private static AltosDeviceDialog dialog; + private static AltosDevice value = null; + private JList list; + + public static AltosDevice show (Component frameComp, int product) { + + Frame frame = JOptionPane.getFrameForComponent(frameComp); + AltosDevice[] devices; + devices = AltosDevice.list(product); + + if (devices != null && devices.length > 0) { + value = null; + dialog = new AltosDeviceDialog(frame, frameComp, + devices, + devices[0]); + + dialog.setVisible(true); + return value; + } else { + /* check for missing altos JNI library, which + * will put up its own error dialog + */ + if (AltosUI.load_library(frame)) { + JOptionPane.showMessageDialog(frame, + "No AltOS devices available", + "No AltOS devices", + JOptionPane.ERROR_MESSAGE); + } + return null; + } + } + + private AltosDeviceDialog (Frame frame, Component location, + AltosDevice[] devices, + AltosDevice initial) { + super(frame, "Device Selection", true); + + value = null; + + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(this); + + final JButton selectButton = new JButton("Select"); + selectButton.setActionCommand("select"); + selectButton.addActionListener(this); + getRootPane().setDefaultButton(selectButton); + + list = new JList(devices) { + //Subclass JList to workaround bug 4832765, which can cause the + //scroll pane to not let the user easily scroll up to the beginning + //of the list. An alternative would be to set the unitIncrement + //of the JScrollBar to a fixed value. You wouldn't get the nice + //aligned scrolling, but it should work. + public int getScrollableUnitIncrement(Rectangle visibleRect, + int orientation, + int direction) { + int row; + if (orientation == SwingConstants.VERTICAL && + direction < 0 && (row = getFirstVisibleIndex()) != -1) { + Rectangle r = getCellBounds(row, row); + if ((r.y == visibleRect.y) && (row != 0)) { + Point loc = r.getLocation(); + loc.y--; + int prevIndex = locationToIndex(loc); + Rectangle prevR = getCellBounds(prevIndex, prevIndex); + + if (prevR == null || prevR.y >= r.y) { + return 0; + } + return prevR.height; + } + } + return super.getScrollableUnitIncrement( + visibleRect, orientation, direction); + } + }; + + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + list.setLayoutOrientation(JList.HORIZONTAL_WRAP); + list.setVisibleRowCount(-1); + list.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + selectButton.doClick(); //emulate button click + } + } + }); + JScrollPane listScroller = new JScrollPane(list); + listScroller.setPreferredSize(new Dimension(400, 80)); + listScroller.setAlignmentX(LEFT_ALIGNMENT); + + //Create a container so that we can add a title around + //the scroll pane. Can't add a title directly to the + //scroll pane because its background would be white. + //Lay out the label and scroll pane from top to bottom. + JPanel listPane = new JPanel(); + listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS)); + + JLabel label = new JLabel("Select Device"); + label.setLabelFor(list); + listPane.add(label); + listPane.add(Box.createRigidArea(new Dimension(0,5))); + listPane.add(listScroller); + listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); + + //Lay out the buttons from left to right. + JPanel buttonPane = new JPanel(); + buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS)); + buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10)); + buttonPane.add(Box.createHorizontalGlue()); + buttonPane.add(cancelButton); + buttonPane.add(Box.createRigidArea(new Dimension(10, 0))); + buttonPane.add(selectButton); + + //Put everything together, using the content pane's BorderLayout. + Container contentPane = getContentPane(); + contentPane.add(listPane, BorderLayout.CENTER); + contentPane.add(buttonPane, BorderLayout.PAGE_END); + + //Initialize values. + list.setSelectedValue(initial, true); + pack(); + setLocationRelativeTo(location); + } + + //Handle clicks on the Set and Cancel buttons. + public void actionPerformed(ActionEvent e) { + if ("select".equals(e.getActionCommand())) + AltosDeviceDialog.value = (AltosDevice)(list.getSelectedValue()); + AltosDeviceDialog.dialog.setVisible(false); + } + +} diff --git a/altosui/AltosDisplayThread.java b/altosui/AltosDisplayThread.java new file mode 100644 index 00000000..3e719130 --- /dev/null +++ b/altosui/AltosDisplayThread.java @@ -0,0 +1,249 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosDisplayThread extends Thread { + + Frame parent; + IdleThread idle_thread; + AltosVoice voice; + String name; + AltosFlightReader reader; + int crc_errors; + AltosFlightDisplay display; + + synchronized void show(AltosState state, int crc_errors) { + if (state != null) + display.show(state, crc_errors); + } + + class IdleThread extends Thread { + + boolean started; + private AltosState state; + int reported_landing; + int report_interval; + long report_time; + + public synchronized void report(boolean last) { + if (state == null) + return; + + /* reset the landing count once we hear about a new flight */ + if (state.state < Altos.ao_flight_drogue) + reported_landing = 0; + + /* Shut up once the rocket is on the ground */ + if (reported_landing > 2) { + return; + } + + /* If the rocket isn't on the pad, then report height */ + if (Altos.ao_flight_drogue <= state.state && + state.state < Altos.ao_flight_landed && + state.range >= 0) + { + voice.speak("Height %d, bearing %s %d, elevation %d, range %d.\n", + (int) (state.height + 0.5), + state.from_pad.bearing_words( + AltosGreatCircle.BEARING_VOICE), + (int) (state.from_pad.bearing + 0.5), + (int) (state.elevation + 0.5), + (int) (state.range + 0.5)); + } else if (state.state > Altos.ao_flight_pad) { + voice.speak("%d meters", (int) (state.height + 0.5)); + } else { + reported_landing = 0; + } + + /* If the rocket is coming down, check to see if it has landed; + * either we've got a landed report or we haven't heard from it in + * a long time + */ + if (state.state >= Altos.ao_flight_drogue && + (last || + System.currentTimeMillis() - state.report_time >= 15000 || + state.state == Altos.ao_flight_landed)) + { + if (Math.abs(state.baro_speed) < 20 && state.height < 100) + voice.speak("rocket landed safely"); + else + voice.speak("rocket may have crashed"); + if (state.from_pad != null) + voice.speak("Bearing %d degrees, range %d meters.", + (int) (state.from_pad.bearing + 0.5), + (int) (state.from_pad.distance + 0.5)); + ++reported_landing; + if (state.state != Altos.ao_flight_landed) { + state.state = Altos.ao_flight_landed; + show(state, 0); + } + } + } + + long now () { + return System.currentTimeMillis(); + } + + void set_report_time() { + report_time = now() + report_interval; + } + + public void run () { + try { + for (;;) { + set_report_time(); + for (;;) { + voice.drain(); + synchronized (this) { + long sleep_time = report_time - now(); + if (sleep_time <= 0) + break; + wait(sleep_time); + } + } + report(false); + } + } catch (InterruptedException ie) { + try { + voice.drain(); + } catch (InterruptedException iie) { } + } + } + + public synchronized void notice(AltosState new_state, boolean spoken) { + AltosState old_state = state; + state = new_state; + if (!started && state.state > Altos.ao_flight_pad) { + started = true; + start(); + } + + if (state.state < Altos.ao_flight_drogue) + report_interval = 10000; + else + report_interval = 20000; + if (old_state != null && old_state.state != state.state) { + report_time = now(); + this.notify(); + } else if (spoken) + set_report_time(); + } + + public IdleThread() { + state = null; + reported_landing = 0; + report_interval = 10000; + } + } + + boolean tell(AltosState state, AltosState old_state) { + boolean ret = false; + if (old_state == null || old_state.state != state.state) { + voice.speak(state.data.state()); + if ((old_state == null || old_state.state <= Altos.ao_flight_boost) && + state.state > Altos.ao_flight_boost) { + voice.speak("max speed: %d meters per second.", + (int) (state.max_speed + 0.5)); + ret = true; + } else if ((old_state == null || old_state.state < Altos.ao_flight_drogue) && + state.state >= Altos.ao_flight_drogue) { + voice.speak("max height: %d meters.", + (int) (state.max_height + 0.5)); + ret = true; + } + } + if (old_state == null || old_state.gps_ready != state.gps_ready) { + if (state.gps_ready) { + voice.speak("GPS ready"); + ret = true; + } + else if (old_state != null) { + voice.speak("GPS lost"); + ret = true; + } + } + old_state = state; + return ret; + } + + public void run() { + boolean interrupted = false; + String line; + AltosState state = null; + AltosState old_state = null; + boolean told; + + idle_thread = new IdleThread(); + + display.reset(); + try { + for (;;) { + try { + AltosRecord record = reader.read(); + if (record == null) + break; + old_state = state; + state = new AltosState(record, state); + reader.update(state); + show(state, crc_errors); + told = tell(state, old_state); + idle_thread.notice(state, told); + } catch (ParseException pp) { + System.out.printf("Parse error: %d \"%s\"\n", pp.getErrorOffset(), pp.getMessage()); + } catch (AltosCRCException ce) { + ++crc_errors; + show(state, crc_errors); + } + } + } catch (InterruptedException ee) { + interrupted = true; + } catch (IOException ie) { + JOptionPane.showMessageDialog(parent, + String.format("Error reading from \"%s\"", name), + "Telemetry Read Error", + JOptionPane.ERROR_MESSAGE); + } finally { + if (!interrupted) + idle_thread.report(true); + reader.close(interrupted); + idle_thread.interrupt(); + try { + idle_thread.join(); + } catch (InterruptedException ie) {} + } + } + + public AltosDisplayThread(Frame in_parent, AltosVoice in_voice, AltosFlightDisplay in_display, AltosFlightReader in_reader) { + parent = in_parent; + voice = in_voice; + display = in_display; + reader = in_reader; + } +} diff --git a/altosui/AltosEepromDownload.java b/altosui/AltosEepromDownload.java new file mode 100644 index 00000000..02fc36f2 --- /dev/null +++ b/altosui/AltosEepromDownload.java @@ -0,0 +1,285 @@ +/* + * 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.*; +import java.util.concurrent.*; + +import libaltosJNI.*; + +public class AltosEepromDownload implements Runnable { + + static final String[] state_names = { + "startup", + "idle", + "pad", + "boost", + "fast", + "coast", + "drogue", + "main", + "landed", + "invalid", + }; + + int[] ParseHex(String line) { + String[] tokens = line.split("\\s+"); + int[] array = new int[tokens.length]; + + for (int i = 0; i < tokens.length; i++) + try { + array[i] = Integer.parseInt(tokens[i], 16); + } catch (NumberFormatException ne) { + return null; + } + return array; + } + + int checksum(int[] line) { + int csum = 0x5a; + for (int i = 1; i < line.length; i++) + csum += line[i]; + return csum & 0xff; + } + + void FlushPending(FileWriter file, LinkedList pending) throws IOException { + while (!pending.isEmpty()) { + file.write(pending.remove()); + } + } + + JFrame frame; + AltosDevice device; + AltosSerial serial_line; + boolean remote; + Thread eeprom_thread; + AltosEepromMonitor monitor; + + void CaptureLog() throws IOException, InterruptedException, TimeoutException { + int serial = 0; + int block, state_block = 0; + int addr; + int flight = 0; + int year = 0, month = 0, day = 0; + int state = 0; + boolean done = false; + boolean want_file = false; + boolean any_valid; + FileWriter eeprom_file = null; + AltosFile eeprom_name; + LinkedList eeprom_pending = new LinkedList(); + + serial_line.printf("\nc s\nv\n"); + + /* Pull the serial number out of the version information */ + + for (;;) { + String line = serial_line.get_reply(5000); + + if (line == null) + throw new TimeoutException(); + if (line.startsWith("serial-number")) { + try { + serial = Integer.parseInt(line.substring(13).trim()); + } catch (NumberFormatException ne) { + serial = 0; + } + } + + eeprom_pending.add(String.format("%s\n", line)); + + /* signals the end of the version info */ + if (line.startsWith("software-version")) + break; + } + if (serial == 0) + throw new IOException("no serial number found"); + + monitor.set_serial(serial); + /* Now scan the eeprom, reading blocks of data and converting to .eeprom file form */ + + state = 0; state_block = 0; + for (block = 0; !done && block < 511; block++) { + serial_line.printf("e %x\n", block); + any_valid = false; + monitor.set_value(state_names[state], state, block - state_block); + for (addr = 0; addr < 0x100;) { + String line = serial_line.get_reply(5000); + if (line == null) + throw new TimeoutException(); + int[] values = ParseHex(line); + + if (values == null) { + System.out.printf("invalid line: %s\n", line); + continue; + } else if (values[0] != addr) { + System.out.printf("data address out of sync at 0x%x\n", + block * 256 + values[0]); + } else if (checksum(values) != 0) { + System.out.printf("invalid checksum at 0x%x\n", + block * 256 + values[0]); + } else { + any_valid = true; + int cmd = values[1]; + int tick = values[3] + (values[4] << 8); + int a = values[5] + (values[6] << 8); + int b = values[7] + (values[8] << 8); + + if (cmd == Altos.AO_LOG_FLIGHT) { + flight = b; + monitor.set_flight(flight); + } + + /* Monitor state transitions to update display */ + if (cmd == Altos.AO_LOG_STATE && a <= Altos.ao_flight_landed) { + if (a > Altos.ao_flight_pad) + want_file = true; + if (a > state) + state_block = block; + state = a; + } + + if (cmd == Altos.AO_LOG_GPS_DATE) { + year = 2000 + (a & 0xff); + month = (a >> 8) & 0xff; + day = (b & 0xff); + want_file = true; + } + + if (eeprom_file == null) { + if (serial != 0 && flight != 0 && want_file) { + if (year != 0 && month != 0 && day != 0) + eeprom_name = new AltosFile(year, month, day, serial, flight, "eeprom"); + else + eeprom_name = new AltosFile(serial, flight, "eeprom"); + + monitor.set_file(eeprom_name.getName()); + eeprom_file = new FileWriter(eeprom_name); + if (eeprom_file != null) { + FlushPending(eeprom_file, eeprom_pending); + eeprom_pending = null; + } + } + } + + String log_line = String.format("%c %4x %4x %4x\n", + cmd, tick, a, b); + if (eeprom_file != null) + eeprom_file.write(log_line); + else + eeprom_pending.add(log_line); + + if (cmd == Altos.AO_LOG_STATE && a == Altos.ao_flight_landed) { + done = true; + } + } + addr += 8; + } + if (!any_valid) + done = true; + } + if (eeprom_file == null) { + eeprom_name = new AltosFile(serial,flight,"eeprom"); + eeprom_file = new FileWriter(eeprom_name); + if (eeprom_file != null) { + FlushPending(eeprom_file, eeprom_pending); + } + } + if (eeprom_file != null) { + eeprom_file.flush(); + eeprom_file.close(); + } + } + + public void run () { + if (remote) { + serial_line.set_radio(); + serial_line.printf("p\nE 0\n"); + serial_line.flush_input(); + } + + monitor = new AltosEepromMonitor(frame, Altos.ao_flight_boost, Altos.ao_flight_landed); + monitor.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + eeprom_thread.interrupt(); + } + }); + try { + CaptureLog(); + } catch (IOException ee) { + JOptionPane.showMessageDialog(frame, + device.toShortString(), + ee.getLocalizedMessage(), + JOptionPane.ERROR_MESSAGE); + } catch (InterruptedException ie) { + } catch (TimeoutException te) { + JOptionPane.showMessageDialog(frame, + String.format("Connection to \"%s\" failed", + device.toShortString()), + "Connection Failed", + JOptionPane.ERROR_MESSAGE); + } + if (remote) + serial_line.printf("~"); + monitor.done(); + serial_line.flush_output(); + serial_line.close(); + } + + public AltosEepromDownload(JFrame given_frame) { + frame = given_frame; + device = AltosDeviceDialog.show(frame, AltosDevice.product_any); + + remote = false; + + if (device != null) { + try { + serial_line = new AltosSerial(device); + if (!device.matchProduct(AltosDevice.product_telemetrum)) + remote = true; + eeprom_thread = new Thread(this); + eeprom_thread.start(); + } catch (FileNotFoundException ee) { + JOptionPane.showMessageDialog(frame, + String.format("Cannot open device \"%s\"", + device.toShortString()), + "Cannot open target device", + JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(frame, + String.format("Device \"%s\" already in use", + device.toShortString()), + "Device in use", + JOptionPane.ERROR_MESSAGE); + } catch (IOException ee) { + JOptionPane.showMessageDialog(frame, + device.toShortString(), + ee.getLocalizedMessage(), + JOptionPane.ERROR_MESSAGE); + } + } + } +} diff --git a/altosui/AltosEepromIterable.java b/altosui/AltosEepromIterable.java new file mode 100644 index 00000000..f8e6d7e5 --- /dev/null +++ b/altosui/AltosEepromIterable.java @@ -0,0 +1,423 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +/* + * AltosRecords with an index field so they can be sorted by tick while preserving + * the original ordering for elements with matching ticks + */ +class AltosOrderedRecord extends AltosEepromRecord implements Comparable { + + public int index; + + public AltosOrderedRecord(String line, int in_index, int prev_tick, boolean prev_tick_valid) + throws ParseException { + super(line); + if (prev_tick_valid) { + tick |= (prev_tick & ~0xffff); + if (tick < prev_tick) { + if (prev_tick - tick > 0x8000) + tick += 0x10000; + } else { + if (tick - prev_tick > 0x8000) + tick -= 0x10000; + } + } + index = in_index; + } + + public AltosOrderedRecord(int in_cmd, int in_tick, int in_a, int in_b, int in_index) { + super(in_cmd, in_tick, in_a, in_b); + index = in_index; + } + + public int compareTo(AltosOrderedRecord o) { + int tick_diff = tick - o.tick; + if (tick_diff != 0) + return tick_diff; + return index - o.index; + } +} + +public class AltosEepromIterable extends AltosRecordIterable { + + static final int seen_flight = 1; + static final int seen_sensor = 2; + static final int seen_temp_volt = 4; + static final int seen_deploy = 8; + static final int seen_gps_time = 16; + static final int seen_gps_lat = 32; + static final int seen_gps_lon = 64; + + static final int seen_basic = seen_flight|seen_sensor|seen_temp_volt|seen_deploy; + + AltosEepromRecord flight_record; + AltosEepromRecord gps_date_record; + + TreeSet records; + + LinkedList list; + + class EepromState { + int seen; + int n_pad_samples; + double ground_pres; + int gps_tick; + int boost_tick; + + EepromState() { + seen = 0; + n_pad_samples = 0; + ground_pres = 0.0; + gps_tick = 0; + } + } + + void update_state(AltosRecord state, AltosEepromRecord record, EepromState eeprom) { + state.tick = record.tick; + switch (record.cmd) { + case Altos.AO_LOG_FLIGHT: + eeprom.seen |= seen_flight; + state.ground_accel = record.a; + state.flight_accel = record.a; + state.flight = record.b; + eeprom.boost_tick = record.tick; + break; + case Altos.AO_LOG_SENSOR: + state.accel = record.a; + state.pres = record.b; + if (state.state < Altos.ao_flight_boost) { + eeprom.n_pad_samples++; + eeprom.ground_pres += state.pres; + state.ground_pres = (int) (eeprom.ground_pres / eeprom.n_pad_samples); + state.flight_pres = state.ground_pres; + } else { + state.flight_pres = (state.flight_pres * 15 + state.pres) / 16; + state.flight_accel = (state.flight_accel * 15 + state.accel) / 16; + state.flight_vel += (state.accel_plus_g - state.accel); + } + eeprom.seen |= seen_sensor; + break; + case Altos.AO_LOG_TEMP_VOLT: + state.temp = record.a; + state.batt = record.b; + eeprom.seen |= seen_temp_volt; + break; + case Altos.AO_LOG_DEPLOY: + state.drogue = record.a; + state.main = record.b; + eeprom.seen |= seen_deploy; + break; + case Altos.AO_LOG_STATE: + state.state = record.a; + break; + case Altos.AO_LOG_GPS_TIME: + eeprom.gps_tick = state.tick; + AltosGPS old = state.gps; + state.gps = new AltosGPS(); + + /* GPS date doesn't get repeated through the file */ + if (old != null) { + state.gps.year = old.year; + state.gps.month = old.month; + state.gps.day = old.day; + } + state.gps.hour = (record.a & 0xff); + state.gps.minute = (record.a >> 8); + state.gps.second = (record.b & 0xff); + + int flags = (record.b >> 8); + state.gps.connected = (flags & Altos.AO_GPS_RUNNING) != 0; + state.gps.locked = (flags & Altos.AO_GPS_VALID) != 0; + state.gps.date_valid = (flags & Altos.AO_GPS_DATE_VALID) != 0; + state.gps.nsat = (flags & Altos.AO_GPS_NUM_SAT_MASK) >> + Altos.AO_GPS_NUM_SAT_SHIFT; + break; + case Altos.AO_LOG_GPS_LAT: + int lat32 = record.a | (record.b << 16); + state.gps.lat = (double) lat32 / 1e7; + break; + case Altos.AO_LOG_GPS_LON: + int lon32 = record.a | (record.b << 16); + state.gps.lon = (double) lon32 / 1e7; + break; + case Altos.AO_LOG_GPS_ALT: + state.gps.alt = record.a; + break; + case Altos.AO_LOG_GPS_SAT: + if (state.tick == eeprom.gps_tick) { + int svid = record.a; + int c_n0 = record.b >> 8; + state.gps.add_sat(svid, c_n0); + } + break; + case Altos.AO_LOG_GPS_DATE: + state.gps.year = (record.a & 0xff) + 2000; + state.gps.month = record.a >> 8; + state.gps.day = record.b & 0xff; + break; + + case Altos.AO_LOG_CONFIG_VERSION: + break; + case Altos.AO_LOG_MAIN_DEPLOY: + break; + case Altos.AO_LOG_APOGEE_DELAY: + break; + case Altos.AO_LOG_RADIO_CHANNEL: + break; + case Altos.AO_LOG_CALLSIGN: + state.callsign = record.data; + break; + case Altos.AO_LOG_ACCEL_CAL: + state.accel_plus_g = record.a; + state.accel_minus_g = record.b; + break; + case Altos.AO_LOG_RADIO_CAL: + break; + case Altos.AO_LOG_MANUFACTURER: + break; + case Altos.AO_LOG_PRODUCT: + break; + case Altos.AO_LOG_SERIAL_NUMBER: + state.serial = record.a; + break; + case Altos.AO_LOG_SOFTWARE_VERSION: + break; + } + } + + LinkedList make_list() { + LinkedList list = new LinkedList(); + Iterator iterator = records.iterator(); + AltosOrderedRecord record = null; + AltosRecord state = new AltosRecord(); + boolean last_reported = false; + EepromState eeprom = new EepromState(); + + state.state = Altos.ao_flight_pad; + state.accel_plus_g = 15758; + state.accel_minus_g = 16294; + + /* Pull in static data from the flight and gps_date records */ + if (flight_record != null) + update_state(state, flight_record, eeprom); + if (gps_date_record != null) + update_state(state, gps_date_record, eeprom); + + while (iterator.hasNext()) { + record = iterator.next(); + if ((eeprom.seen & seen_basic) == seen_basic && record.tick != state.tick) { + AltosRecord r = new AltosRecord(state); + r.time = (r.tick - eeprom.boost_tick) / 100.0; + list.add(r); + } + update_state(state, record, eeprom); + } + AltosRecord r = new AltosRecord(state); + r.time = (r.tick - eeprom.boost_tick) / 100.0; + list.add(r); + return list; + } + + public Iterator iterator() { + if (list == null) + list = make_list(); + return list.iterator(); + } + + public void write_comments(PrintStream out) { + Iterator iterator = records.iterator(); + out.printf("# Comments\n"); + while (iterator.hasNext()) { + AltosOrderedRecord record = iterator.next(); + switch (record.cmd) { + case Altos.AO_LOG_CONFIG_VERSION: + out.printf("# Config version: %s\n", record.data); + break; + case Altos.AO_LOG_MAIN_DEPLOY: + out.printf("# Main deploy: %s\n", record.a); + break; + case Altos.AO_LOG_APOGEE_DELAY: + out.printf("# Apogee delay: %s\n", record.a); + break; + case Altos.AO_LOG_RADIO_CHANNEL: + out.printf("# Radio channel: %s\n", record.a); + break; + case Altos.AO_LOG_CALLSIGN: + out.printf("# Callsign: %s\n", record.data); + break; + case Altos.AO_LOG_ACCEL_CAL: + out.printf ("# Accel cal: %d %d\n", record.a, record.b); + break; + case Altos.AO_LOG_RADIO_CAL: + out.printf ("# Radio cal: %d\n", record.a); + break; + case Altos.AO_LOG_MANUFACTURER: + out.printf ("# Manufacturer: %s\n", record.data); + break; + case Altos.AO_LOG_PRODUCT: + out.printf ("# Product: %s\n", record.data); + break; + case Altos.AO_LOG_SERIAL_NUMBER: + out.printf ("# Serial number: %d\n", record.a); + break; + case Altos.AO_LOG_SOFTWARE_VERSION: + out.printf ("# Software version: %s\n", record.data); + break; + } + } + } + + /* + * Given an AO_LOG_GPS_TIME record with correct time, and one + * missing time, rewrite the missing time values with the good + * ones, assuming that the difference between them is 'diff' seconds + */ + void update_time(AltosOrderedRecord good, AltosOrderedRecord bad) { + + int diff = (bad.tick - good.tick + 50) / 100; + + int hour = (good.a & 0xff); + int minute = (good.a >> 8); + int second = (good.b & 0xff); + int flags = (good.b >> 8); + int seconds = hour * 3600 + minute * 60 + second; + + /* Make sure this looks like a good GPS value */ + if ((flags & Altos.AO_GPS_NUM_SAT_MASK) >> Altos.AO_GPS_NUM_SAT_SHIFT < 4) + flags = (flags & ~Altos.AO_GPS_NUM_SAT_MASK) | (4 << Altos.AO_GPS_NUM_SAT_SHIFT); + flags |= Altos.AO_GPS_RUNNING; + flags |= Altos.AO_GPS_VALID; + + int new_seconds = seconds + diff; + if (new_seconds < 0) + new_seconds += 24 * 3600; + int new_second = (new_seconds % 60); + int new_minutes = (new_seconds / 60); + int new_minute = (new_minutes % 60); + int new_hours = (new_minutes / 60); + int new_hour = (new_hours % 24); + + bad.a = new_hour + (new_minute << 8); + bad.b = new_second + (flags << 8); + } + + /* + * Read the whole file, dumping records into a RB tree so + * we can enumerate them in time order -- the eeprom data + * are sometimes out of order with GPS data getting timestamps + * matching the first packet out of the GPS unit but not + * written until the final GPS packet has been received. + */ + public AltosEepromIterable (FileInputStream input) { + records = new TreeSet(); + + AltosOrderedRecord last_gps_time = null; + + int index = 0; + int prev_tick = 0; + boolean prev_tick_valid = false; + boolean missing_time = false; + + try { + for (;;) { + String line = AltosRecord.gets(input); + if (line == null) + break; + AltosOrderedRecord record = new AltosOrderedRecord(line, index++, prev_tick, prev_tick_valid); + if (record == null) + break; + if (record.cmd == Altos.AO_LOG_INVALID) + continue; + prev_tick = record.tick; + if (record.cmd < Altos.AO_LOG_CONFIG_VERSION) + prev_tick_valid = true; + if (record.cmd == Altos.AO_LOG_FLIGHT) { + flight_record = record; + continue; + } + + /* Two firmware bugs caused the loss of some GPS data. + * The flight date would never be recorded, and often + * the flight time would get overwritten by another + * record. Detect the loss of the GPS date and fix up the + * missing time records + */ + if (record.cmd == Altos.AO_LOG_GPS_DATE) { + gps_date_record = record; + continue; + } + + /* go back and fix up any missing time values */ + if (record.cmd == Altos.AO_LOG_GPS_TIME) { + last_gps_time = record; + if (missing_time) { + Iterator iterator = records.iterator(); + while (iterator.hasNext()) { + AltosOrderedRecord old = iterator.next(); + if (old.cmd == Altos.AO_LOG_GPS_TIME && + old.a == -1 && old.b == -1) + { + update_time(record, old); + } + } + missing_time = false; + } + } + + if (record.cmd == Altos.AO_LOG_GPS_LAT) { + if (last_gps_time == null || last_gps_time.tick != record.tick) { + AltosOrderedRecord add_gps_time = new AltosOrderedRecord(Altos.AO_LOG_GPS_TIME, + record.tick, + -1, -1, index-1); + if (last_gps_time != null) + update_time(last_gps_time, add_gps_time); + else + missing_time = true; + + records.add(add_gps_time); + record.index = index++; + } + } + records.add(record); + + /* Bail after reading the 'landed' record; we're all done */ + if (record.cmd == Altos.AO_LOG_STATE && + record.a == Altos.ao_flight_landed) + break; + } + } catch (IOException io) { + } catch (ParseException pe) { + } + try { + input.close(); + } catch (IOException ie) { + } + } +} diff --git a/altosui/AltosEepromMonitor.java b/altosui/AltosEepromMonitor.java new file mode 100644 index 00000000..7ff00ead --- /dev/null +++ b/altosui/AltosEepromMonitor.java @@ -0,0 +1,176 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosEepromMonitor extends JDialog { + + Container pane; + Box box; + JLabel serial_label; + JLabel flight_label; + JLabel file_label; + JLabel serial_value; + JLabel flight_value; + JLabel file_value; + JButton cancel; + JProgressBar pbar; + int min_state, max_state; + + public AltosEepromMonitor(JFrame owner, int in_min_state, int in_max_state) { + super (owner, "Download Flight Data", false); + + GridBagConstraints c; + Insets il = new Insets(4,4,4,4); + Insets ir = new Insets(4,4,4,4); + + pane = getContentPane(); + pane.setLayout(new GridBagLayout()); + + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 0; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + serial_label = new JLabel("Serial:"); + pane.add(serial_label, c); + + c = new GridBagConstraints(); + c.gridx = 1; c.gridy = 0; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + serial_value = new JLabel(""); + pane.add(serial_value, c); + + c = new GridBagConstraints(); + c.fill = GridBagConstraints.NONE; + c.gridx = 0; c.gridy = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + flight_label = new JLabel("Flight:"); + pane.add(flight_label, c); + + c = new GridBagConstraints(); + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.gridx = 1; c.gridy = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + flight_value = new JLabel(""); + pane.add(flight_value, c); + + c = new GridBagConstraints(); + c.fill = GridBagConstraints.NONE; + c.gridx = 0; c.gridy = 2; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + file_label = new JLabel("File:"); + pane.add(file_label, c); + + c = new GridBagConstraints(); + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.gridx = 1; c.gridy = 2; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + file_value = new JLabel(""); + pane.add(file_value, c); + + min_state = in_min_state; + max_state = in_max_state; + pbar = new JProgressBar(); + pbar.setMinimum(0); + pbar.setMaximum((max_state - min_state) * 100); + pbar.setValue(0); + pbar.setString("startup"); + pbar.setStringPainted(true); + pbar.setPreferredSize(new Dimension(600, 20)); + c = new GridBagConstraints(); + c.fill = GridBagConstraints.HORIZONTAL; + c.anchor = GridBagConstraints.CENTER; + c.gridx = 0; c.gridy = 3; + c.gridwidth = GridBagConstraints.REMAINDER; + Insets ib = new Insets(4,4,4,4); + c.insets = ib; + pane.add(pbar, c); + + + cancel = new JButton("Cancel"); + c = new GridBagConstraints(); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.gridx = 0; c.gridy = 4; + c.gridwidth = GridBagConstraints.REMAINDER; + Insets ic = new Insets(4,4,4,4); + c.insets = ic; + pane.add(cancel, c); + + pack(); + setLocationRelativeTo(owner); + setVisible(true); + } + + public void addActionListener (ActionListener l) { + cancel.addActionListener(l); + } + + public void set_value(String state_name, int in_state, int in_block) { + int block = in_block; + int state = in_state; + + if (block > 100) + block = 100; + if (state < min_state) state = min_state; + if (state >= max_state) state = max_state - 1; + state -= min_state; + + int pos = state * 100 + block; + + pbar.setString(state_name); + pbar.setValue(pos); + } + + public void set_serial(int serial) { + serial_value.setText(String.format("%d", serial)); + } + + public void set_flight(int flight) { + flight_value.setText(String.format("%d", flight)); + } + + public void set_file(String file) { + file_value.setText(String.format("%s", file)); + } + + public void done() { + setVisible(false); + dispose(); + } +} diff --git a/altosui/AltosEepromRecord.java b/altosui/AltosEepromRecord.java new file mode 100644 index 00000000..5a673817 --- /dev/null +++ b/altosui/AltosEepromRecord.java @@ -0,0 +1,115 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosEepromRecord { + public int cmd; + public int tick; + public int a; + public int b; + public String data; + public boolean tick_valid; + + public AltosEepromRecord (String line) { + tick_valid = false; + tick = 0; + a = 0; + b = 0; + data = null; + if (line == null) { + cmd = Altos.AO_LOG_INVALID; + data = ""; + } else { + try { + String[] tokens = line.split("\\s+"); + + if (tokens[0].length() == 1) { + if (tokens.length != 4) { + cmd = Altos.AO_LOG_INVALID; + data = line; + } else { + cmd = tokens[0].codePointAt(0); + tick = Integer.parseInt(tokens[1],16); + tick_valid = true; + a = Integer.parseInt(tokens[2],16); + b = Integer.parseInt(tokens[3],16); + } + } else if (tokens[0].equals("Config") && tokens[1].equals("version:")) { + cmd = Altos.AO_LOG_CONFIG_VERSION; + data = tokens[2]; + } else if (tokens[0].equals("Main") && tokens[1].equals("deploy:")) { + cmd = Altos.AO_LOG_MAIN_DEPLOY; + a = Integer.parseInt(tokens[2]); + } else if (tokens[0].equals("Apogee") && tokens[1].equals("delay:")) { + cmd = Altos.AO_LOG_APOGEE_DELAY; + a = Integer.parseInt(tokens[2]); + } else if (tokens[0].equals("Radio") && tokens[1].equals("channel:")) { + cmd = Altos.AO_LOG_RADIO_CHANNEL; + a = Integer.parseInt(tokens[2]); + } else if (tokens[0].equals("Callsign:")) { + cmd = Altos.AO_LOG_CALLSIGN; + data = tokens[1].replaceAll("\"",""); + } else if (tokens[0].equals("Accel") && tokens[1].equals("cal")) { + cmd = Altos.AO_LOG_ACCEL_CAL; + a = Integer.parseInt(tokens[3]); + b = Integer.parseInt(tokens[5]); + } else if (tokens[0].equals("Radio") && tokens[1].equals("cal:")) { + cmd = Altos.AO_LOG_RADIO_CAL; + a = Integer.parseInt(tokens[2]); + } else if (tokens[0].equals("manufacturer")) { + cmd = Altos.AO_LOG_MANUFACTURER; + data = tokens[1]; + } else if (tokens[0].equals("product")) { + cmd = Altos.AO_LOG_PRODUCT; + data = tokens[1]; + } else if (tokens[0].equals("serial-number")) { + cmd = Altos.AO_LOG_SERIAL_NUMBER; + a = Integer.parseInt(tokens[1]); + } else if (tokens[0].equals("software-version")) { + cmd = Altos.AO_LOG_SOFTWARE_VERSION; + data = tokens[1]; + } else { + cmd = Altos.AO_LOG_INVALID; + data = line; + } + } catch (NumberFormatException ne) { + cmd = Altos.AO_LOG_INVALID; + data = line; + } + } + } + + public AltosEepromRecord(int in_cmd, int in_tick, int in_a, int in_b) { + tick_valid = true; + cmd = in_cmd; + tick = in_tick; + a = in_a; + b = in_b; + } +} diff --git a/altosui/AltosFile.java b/altosui/AltosFile.java new file mode 100644 index 00000000..06360572 --- /dev/null +++ b/altosui/AltosFile.java @@ -0,0 +1,44 @@ +/* + * 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.lang.*; +import java.io.File; +import java.util.*; + +class AltosFile extends File { + + public AltosFile(int year, int month, int day, int serial, int flight, String extension) { + super (AltosPreferences.logdir(), + String.format("%04d-%02d-%02d-serial-%03d-flight-%03d.%s", + year, month, day, serial, flight, extension)); + } + + public AltosFile(int serial, int flight, String extension) { + this(Calendar.getInstance().get(Calendar.YEAR), + Calendar.getInstance().get(Calendar.MONTH) + 1, + Calendar.getInstance().get(Calendar.DAY_OF_MONTH), + serial, + flight, + extension); + } + + public AltosFile(AltosTelemetry telem) { + this(telem.serial, telem.flight, "telem"); + } +} diff --git a/altosui/AltosFlash.java b/altosui/AltosFlash.java new file mode 100644 index 00000000..3af25c23 --- /dev/null +++ b/altosui/AltosFlash.java @@ -0,0 +1,344 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosFlash { + File file; + FileInputStream input; + AltosHexfile image; + JFrame frame; + AltosDevice debug_dongle; + AltosDebug debug; + AltosRomconfig rom_config; + ActionListener listener; + boolean aborted; + + static final byte MOV_direct_data = (byte) 0x75; + static final byte MOV_DPTR_data16 = (byte) 0x90; + static final byte MOV_A_data = (byte) 0x74; + static final byte MOVX_atDPTR_A = (byte) 0xf0; + static final byte MOVX_A_atDPTR = (byte) 0xe0; + static final byte INC_DPTR = (byte) 0xa3; + static final byte TRAP = (byte) 0xa5; + + static final byte JB = (byte) 0x20; + + static final byte MOV_A_direct = (byte) 0xe5; + static final byte MOV_direct1_direct2 = (byte) 0x85; + static final byte MOV_direct_A = (byte) 0xf5; + static final byte MOV_R0_data = (byte) (0x78 | 0); + static final byte MOV_R1_data = (byte) (0x78 | 1); + static final byte MOV_R2_data = (byte) (0x78 | 2); + static final byte MOV_R3_data = (byte) (0x78 | 3); + static final byte MOV_R4_data = (byte) (0x78 | 4); + static final byte MOV_R5_data = (byte) (0x78 | 5); + static final byte MOV_R6_data = (byte) (0x78 | 6); + static final byte MOV_R7_data = (byte) (0x78 | 7); + static final byte DJNZ_R0_rel = (byte) (0xd8 | 0); + static final byte DJNZ_R1_rel = (byte) (0xd8 | 1); + static final byte DJNZ_R2_rel = (byte) (0xd8 | 2); + static final byte DJNZ_R3_rel = (byte) (0xd8 | 3); + static final byte DJNZ_R4_rel = (byte) (0xd8 | 4); + static final byte DJNZ_R5_rel = (byte) (0xd8 | 5); + static final byte DJNZ_R6_rel = (byte) (0xd8 | 6); + static final byte DJNZ_R7_rel = (byte) (0xd8 | 7); + + static final byte P1DIR = (byte) 0xFE; + static final byte P1 = (byte) 0x90; + + /* flash controller */ + static final byte FWT = (byte) 0xAB; + static final byte FADDRL = (byte) 0xAC; + static final byte FADDRH = (byte) 0xAD; + static final byte FCTL = (byte) 0xAE; + static final byte FCTL_BUSY = (byte) 0x80; + static final byte FCTL_BUSY_BIT = (byte) 7; + static final byte FCTL_SWBSY = (byte) 0x40; + static final byte FCTL_SWBSY_BIT = (byte) 6; + static final byte FCTL_CONTRD = (byte) 0x10; + static final byte FCTL_WRITE = (byte) 0x02; + static final byte FCTL_ERASE = (byte) 0x01; + static final byte FWDATA = (byte) 0xAF; + + static final byte ACC = (byte) 0xE0; + + /* offsets within the flash_page program */ + static final int FLASH_ADDR_HIGH = 8; + static final int FLASH_ADDR_LOW = 11; + static final int RAM_ADDR_HIGH = 13; + static final int RAM_ADDR_LOW = 14; + static final int FLASH_WORDS_HIGH = 16; + static final int FLASH_WORDS_LOW = 18; + static final int FLASH_TIMING = 21; + + /* sleep mode control */ + static final int SLEEP = (byte) 0xbe; + static final int SLEEP_USB_EN = (byte) 0x80; + static final int SLEEP_XOSC_STB = (byte) 0x40; + static final int SLEEP_HFRC_STB = (byte) 0x20; + static final int SLEEP_RST_MASK = (byte) 0x18; + static final int SLEEP_RST_POWERON = (byte) 0x00; + static final int SLEEP_RST_EXTERNAL = (byte) 0x10; + static final int SLEEP_RST_WATCHDOG = (byte) 0x08; + static final int SLEEP_OSC_PD = (byte) 0x04; + static final int SLEEP_MODE_MASK = (byte) 0x03; + static final int SLEEP_MODE_PM0 = (byte) 0x00; + static final int SLEEP_MODE_PM1 = (byte) 0x01; + static final int SLEEP_MODE_PM2 = (byte) 0x02; + static final int SLEEP_MODE_PM3 = (byte) 0x03; + + /* clock controller */ + static final byte CLKCON = (byte) 0xC6; + static final byte CLKCON_OSC32K = (byte) 0x80; + static final byte CLKCON_OSC = (byte) 0x40; + static final byte CLKCON_TICKSPD = (byte) 0x38; + static final byte CLKCON_CLKSPD = (byte) 0x07; + + static final byte[] flash_page_proto = { + + MOV_direct_data, P1DIR, (byte) 0x02, + MOV_direct_data, P1, (byte) 0xFF, + + MOV_direct_data, FADDRH, 0, /* FLASH_ADDR_HIGH */ + + MOV_direct_data, FADDRL, 0, /* FLASH_ADDR_LOW */ + + MOV_DPTR_data16, 0, 0, /* RAM_ADDR_HIGH, RAM_ADDR_LOW */ + + MOV_R7_data, 0, /* FLASH_WORDS_HIGH */ + + MOV_R6_data, 0, /* FLASH_WORDS_LOW */ + + + MOV_direct_data, FWT, 0x20, /* FLASH_TIMING */ + + MOV_direct_data, FCTL, FCTL_ERASE, +/* eraseWaitLoop: */ + MOV_A_direct, FCTL, + JB, ACC|FCTL_BUSY_BIT, (byte) 0xfb, + + MOV_direct_data, P1, (byte) 0xfd, + + MOV_direct_data, FCTL, FCTL_WRITE, +/* writeLoop: */ + MOV_R5_data, 2, +/* writeWordLoop: */ + MOVX_A_atDPTR, + INC_DPTR, + MOV_direct_A, FWDATA, + DJNZ_R5_rel, (byte) 0xfa, /* writeWordLoop */ +/* writeWaitLoop: */ + MOV_A_direct, FCTL, + JB, ACC|FCTL_SWBSY_BIT, (byte) 0xfb, /* writeWaitLoop */ + DJNZ_R6_rel, (byte) 0xf1, /* writeLoop */ + DJNZ_R7_rel, (byte) 0xef, /* writeLoop */ + + MOV_direct_data, P1DIR, (byte) 0x00, + MOV_direct_data, P1, (byte) 0xFF, + TRAP, + }; + + public byte[] make_flash_page(int flash_addr, int ram_addr, int byte_count) { + int flash_word_addr = flash_addr >> 1; + int flash_word_count = ((byte_count + 1) >> 1); + + byte[] flash_page = new byte[flash_page_proto.length]; + for (int i = 0; i < flash_page.length; i++) + flash_page[i] = flash_page_proto[i]; + + flash_page[FLASH_ADDR_HIGH] = (byte) (flash_word_addr >> 8); + flash_page[FLASH_ADDR_LOW] = (byte) (flash_word_addr); + flash_page[RAM_ADDR_HIGH] = (byte) (ram_addr >> 8); + flash_page[RAM_ADDR_LOW] = (byte) (ram_addr); + + byte flash_words_low = (byte) (flash_word_count); + byte flash_words_high = (byte) (flash_word_count >> 8); + /* the flashing code has a minor 'bug' */ + if (flash_words_low != 0) + flash_words_high++; + + flash_page[FLASH_WORDS_HIGH] = (byte) flash_words_high; + flash_page[FLASH_WORDS_LOW] = (byte) flash_words_low; + return flash_page; + } + + static byte[] set_clkcon_fast = { + MOV_direct_data, CLKCON, 0x00 + }; + + static byte[] get_sleep = { + MOV_A_direct, SLEEP + }; + + public void clock_init() throws IOException, InterruptedException { + debug.debug_instr(set_clkcon_fast); + + byte status; + for (int times = 0; times < 20; times++) { + Thread.sleep(1); + status = debug.debug_instr(get_sleep); + if ((status & SLEEP_XOSC_STB) != 0) + return; + } + throw new IOException("Failed to initialize target clock"); + } + + void action(String s, int percent) { + if (listener != null && !aborted) + listener.actionPerformed(new ActionEvent(this, + percent, + s)); + } + + void action(int part, int total) { + int percent = 100 * part / total; + action(String.format("%d/%d (%d%%)", + part, total, percent), + percent); + } + + void run(int pc) throws IOException, InterruptedException { + debug.set_pc(pc); + int set_pc = debug.get_pc(); + if (pc != set_pc) + throw new IOException("Failed to set target program counter"); + debug.resume(); + + for (int times = 0; times < 20; times++) { + byte status = debug.read_status(); + if ((status & AltosDebug.STATUS_CPU_HALTED) != 0) + return; + } + + throw new IOException("Failed to execute program on target"); + } + + public void flash() throws IOException, FileNotFoundException, InterruptedException { + if (!check_rom_config()) + throw new IOException("Invalid rom config settings"); + if (image.address + image.data.length > 0x8000) + throw new IOException(String.format("Flash image too long %d", + image.address + + image.data.length)); + if ((image.address & 0x3ff) != 0) + throw new IOException(String.format("Flash image must start on page boundary (is 0x%x)", + image.address)); + int ram_address = 0xf000; + int flash_prog = 0xf400; + + /* + * Store desired config values into image + */ + rom_config.write(image); + /* + * Bring up the clock + */ + clock_init(); + + int remain = image.data.length; + int flash_addr = image.address; + int image_start = 0; + + action("start", 0); + action(0, image.data.length); + while (remain > 0 && !aborted) { + int this_time = remain; + if (this_time > 0x400) + this_time = 0x400; + + /* write the data */ + debug.write_memory(ram_address, image.data, + image_start, this_time); + + /* write the flash program */ + byte[] flash_page = make_flash_page(flash_addr, + ram_address, + this_time); + debug.write_memory(flash_prog, flash_page); + + run(flash_prog); + + byte[] check = debug.read_memory(flash_addr, this_time); + for (int i = 0; i < this_time; i++) + if (check[i] != image.data[image_start + i]) + throw new IOException(String.format("Flash write failed at 0x%x (%02x != %02x)", + image.address + image_start + i, + check[i], image.data[image_start + i])); + remain -= this_time; + flash_addr += this_time; + image_start += this_time; + + action(image.data.length - remain, image.data.length); + } + if (!aborted) { + action("done", 100); + debug.set_pc(image.address); + debug.resume(); + } + debug.close(); + } + + public void abort() { + aborted = true; + debug.close(); + } + + public void addActionListener(ActionListener l) { + listener = l; + } + + public boolean check_rom_config() { + if (rom_config == null) + rom_config = debug.romconfig(); + return rom_config != null && rom_config.valid(); + } + + public void set_romconfig (AltosRomconfig romconfig) { + rom_config = romconfig; + } + + public AltosRomconfig romconfig() { + if (!check_rom_config()) + return null; + return rom_config; + } + + public AltosFlash(File in_file, AltosDevice in_debug_dongle) + throws IOException, FileNotFoundException, AltosSerialInUseException, InterruptedException { + file = in_file; + debug_dongle = in_debug_dongle; + debug = new AltosDebug(in_debug_dongle); + input = new FileInputStream(file); + image = new AltosHexfile(input); + if (!debug.check_connection()) { + debug.close(); + throw new IOException("Debug port not connected"); + } + } +} \ No newline at end of file diff --git a/altosui/AltosFlashUI.java b/altosui/AltosFlashUI.java new file mode 100644 index 00000000..f63097ac --- /dev/null +++ b/altosui/AltosFlashUI.java @@ -0,0 +1,218 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosFlashUI + extends JDialog + implements Runnable, ActionListener +{ + Container pane; + Box box; + JLabel serial_label; + JLabel serial_value; + JLabel file_label; + JLabel file_value; + JProgressBar pbar; + JButton cancel; + + File file; + Thread thread; + JFrame frame; + AltosDevice debug_dongle; + AltosFlash flash; + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == cancel) { + abort(); + dispose(); + } else { + String cmd = e.getActionCommand(); + if (cmd.equals("done")) + ; + else if (cmd.equals("start")) { + setVisible(true); + } else { + pbar.setValue(e.getID()); + pbar.setString(cmd); + } + } + } + + public void run() { + try { + flash = new AltosFlash(file, debug_dongle); + flash.addActionListener(this); + AltosRomconfigUI romconfig_ui = new AltosRomconfigUI (frame); + + romconfig_ui.set(flash.romconfig()); + AltosRomconfig romconfig = romconfig_ui.showDialog(); + + if (romconfig != null && romconfig.valid()) { + flash.set_romconfig(romconfig); + serial_value.setText(String.format("%d", + flash.romconfig().serial_number)); + file_value.setText(file.toString()); + setVisible(true); + flash.flash(); + flash = null; + } + } catch (FileNotFoundException ee) { + JOptionPane.showMessageDialog(frame, + "Cannot open image", + file.toString(), + JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(frame, + String.format("Device \"%s\" already in use", + debug_dongle.toShortString()), + "Device in use", + JOptionPane.ERROR_MESSAGE); + } catch (IOException e) { + JOptionPane.showMessageDialog(frame, + e.getMessage(), + file.toString(), + JOptionPane.ERROR_MESSAGE); + } catch (InterruptedException ie) { + } finally { + abort(); + } + dispose(); + } + + public void abort() { + if (flash != null) + flash.abort(); + } + + public void build_dialog() { + GridBagConstraints c; + Insets il = new Insets(4,4,4,4); + Insets ir = new Insets(4,4,4,4); + + pane = getContentPane(); + pane.setLayout(new GridBagLayout()); + + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 0; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + serial_label = new JLabel("Serial:"); + pane.add(serial_label, c); + + c = new GridBagConstraints(); + c.gridx = 1; c.gridy = 0; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + serial_value = new JLabel(""); + pane.add(serial_value, c); + + c = new GridBagConstraints(); + c.fill = GridBagConstraints.NONE; + c.gridx = 0; c.gridy = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + file_label = new JLabel("File:"); + pane.add(file_label, c); + + c = new GridBagConstraints(); + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.gridx = 1; c.gridy = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + file_value = new JLabel(""); + pane.add(file_value, c); + + pbar = new JProgressBar(); + pbar.setMinimum(0); + pbar.setMaximum(100); + pbar.setValue(0); + pbar.setString(""); + pbar.setStringPainted(true); + pbar.setPreferredSize(new Dimension(600, 20)); + c = new GridBagConstraints(); + c.fill = GridBagConstraints.HORIZONTAL; + c.anchor = GridBagConstraints.CENTER; + c.gridx = 0; c.gridy = 2; + c.gridwidth = GridBagConstraints.REMAINDER; + Insets ib = new Insets(4,4,4,4); + c.insets = ib; + pane.add(pbar, c); + + cancel = new JButton("Cancel"); + c = new GridBagConstraints(); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.gridx = 0; c.gridy = 3; + c.gridwidth = GridBagConstraints.REMAINDER; + Insets ic = new Insets(4,4,4,4); + c.insets = ic; + pane.add(cancel, c); + cancel.addActionListener(this); + pack(); + setLocationRelativeTo(frame); + } + + public AltosFlashUI(JFrame in_frame) { + super(in_frame, "Program Altusmetrum Device", false); + + frame = in_frame; + + build_dialog(); + + debug_dongle = AltosDeviceDialog.show(frame, AltosDevice.product_any); + + if (debug_dongle == null) + return; + + JFileChooser hexfile_chooser = new JFileChooser(); + + File firmwaredir = AltosPreferences.firmwaredir(); + if (firmwaredir != null) + hexfile_chooser.setCurrentDirectory(firmwaredir); + + hexfile_chooser.setDialogTitle("Select Flash Image"); + hexfile_chooser.setFileFilter(new FileNameExtensionFilter("Flash Image", "ihx")); + int returnVal = hexfile_chooser.showOpenDialog(frame); + + if (returnVal != JFileChooser.APPROVE_OPTION) + return; + + file = hexfile_chooser.getSelectedFile(); + + if (file != null) + AltosPreferences.set_firmwaredir(file.getParentFile()); + + thread = new Thread(this); + thread.start(); + } +} \ No newline at end of file diff --git a/altosui/AltosFlightDisplay.java b/altosui/AltosFlightDisplay.java new file mode 100644 index 00000000..d18d1d1f --- /dev/null +++ b/altosui/AltosFlightDisplay.java @@ -0,0 +1,24 @@ +/* + * 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; + +public interface AltosFlightDisplay { + void reset(); + + void show(AltosState state, int crc_errors); +} diff --git a/altosui/AltosFlightInfoTableModel.java b/altosui/AltosFlightInfoTableModel.java new file mode 100644 index 00000000..e23eff68 --- /dev/null +++ b/altosui/AltosFlightInfoTableModel.java @@ -0,0 +1,84 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosFlightInfoTableModel extends AbstractTableModel { + final static private String[] columnNames = {"Field", "Value"}; + + int rows; + int cols; + private String[][] data; + + public int getColumnCount() { return cols; } + public int getRowCount() { return rows; } + public String getColumnName(int col) { return columnNames[col & 1]; } + + public Object getValueAt(int row, int col) { + if (row >= rows || col >= cols) + return ""; + return data[row][col]; + } + + int[] current_row; + + public void reset() { + for (int i = 0; i < cols / 2; i++) + current_row[i] = 0; + } + + public void clear() { + reset(); + for (int c = 0; c < cols; c++) + for (int r = 0; r < rows; r++) + data[r][c] = ""; + fireTableDataChanged(); + } + + public void addRow(int col, String name, String value) { + if (current_row[col] < rows) { + data[current_row[col]][col * 2] = name; + data[current_row[col]][col * 2 + 1] = value; + } + current_row[col]++; + } + + public void finish() { + for (int c = 0; c < cols / 2; c++) + while (current_row[c] < rows) + addRow(c, "", ""); + fireTableDataChanged(); + } + + public AltosFlightInfoTableModel (int in_rows, int in_cols) { + rows = in_rows; + cols = in_cols * 2; + data = new String[rows][cols]; + current_row = new int[in_cols]; + } +} diff --git a/altosui/AltosFlightReader.java b/altosui/AltosFlightReader.java new file mode 100644 index 00000000..3d59de9a --- /dev/null +++ b/altosui/AltosFlightReader.java @@ -0,0 +1,38 @@ +/* + * 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.lang.*; +import java.text.*; +import java.io.*; + +public class AltosFlightReader { + String name; + + int serial; + + void init() { } + + AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; } + + void close(boolean interrupted) { } + + void set_channel(int channel) { } + + void update(AltosState state) throws InterruptedException { } +} diff --git a/altosui/AltosFlightStatus.java b/altosui/AltosFlightStatus.java new file mode 100644 index 00000000..59c9e9db --- /dev/null +++ b/altosui/AltosFlightStatus.java @@ -0,0 +1,154 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosFlightStatus extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + + public class FlightValue { + JLabel label; + JTextField value; + + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + } + public FlightValue (GridBagLayout layout, int x, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(5, 5, 5, 5); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + + label = new JLabel(text); + label.setFont(Altos.status_font); + label.setHorizontalAlignment(SwingConstants.CENTER); + c.gridx = x; c.gridy = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(""); + value.setFont(Altos.status_font); + value.setHorizontalAlignment(SwingConstants.CENTER); + c.gridx = x; c.gridy = 1; + layout.setConstraints(value, c); + add(value); + } + } + + class Call extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(state.data.callsign); + } + public Call (GridBagLayout layout, int x) { + super (layout, x, "Callsign"); + } + } + + Call call; + + class Serial extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(String.format("%d", state.data.serial)); + } + public Serial (GridBagLayout layout, int x) { + super (layout, x, "Serial"); + } + } + + Serial serial; + + class Flight extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(String.format("%d", state.data.flight)); + } + public Flight (GridBagLayout layout, int x) { + super (layout, x, "Flight"); + } + } + + Flight flight; + + class FlightState extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(state.data.state()); + } + public FlightState (GridBagLayout layout, int x) { + super (layout, x, "State"); + } + } + + FlightState flight_state; + + class RSSI extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(String.format("%d", state.data.rssi)); + } + public RSSI (GridBagLayout layout, int x) { + super (layout, x, "RSSI (dBm)"); + } + } + + RSSI rssi; + + public void reset () { + call.reset(); + serial.reset(); + flight.reset(); + flight_state.reset(); + rssi.reset(); + } + + public void show (AltosState state, int crc_errors) { + call.show(state, crc_errors); + serial.show(state, crc_errors); + flight.show(state, crc_errors); + flight_state.show(state, crc_errors); + rssi.show(state, crc_errors); + } + + public int height() { + Dimension d = layout.preferredLayoutSize(this); + return d.height; + } + + public AltosFlightStatus() { + layout = new GridBagLayout(); + + setLayout(layout); + + call = new Call(layout, 0); + serial = new Serial(layout, 1); + flight = new Flight(layout, 2); + flight_state = new FlightState(layout, 3); + rssi = new RSSI(layout, 4); + } +} diff --git a/altosui/AltosFlightStatusTableModel.java b/altosui/AltosFlightStatusTableModel.java new file mode 100644 index 00000000..4c24b6ac --- /dev/null +++ b/altosui/AltosFlightStatusTableModel.java @@ -0,0 +1,61 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosFlightStatusTableModel extends AbstractTableModel { + private String[] columnNames = {"Height (m)", "State", "RSSI (dBm)", "Speed (m/s)" }; + private Object[] data = { 0, "idle", 0, 0 }; + + public int getColumnCount() { return columnNames.length; } + public int getRowCount() { return 2; } + public Object getValueAt(int row, int col) { + if (row == 0) + return columnNames[col]; + return data[col]; + } + + public void setValueAt(Object value, int col) { + data[col] = value; + fireTableCellUpdated(1, col); + } + + public void setValueAt(Object value, int row, int col) { + setValueAt(value, col); + } + + public void set(AltosState state) { + setValueAt(String.format("%1.0f", state.height), 0); + setValueAt(state.data.state(), 1); + setValueAt(state.data.rssi, 2); + double speed = state.baro_speed; + if (state.ascent) + speed = state.speed; + setValueAt(String.format("%1.0f", speed), 3); + } +} diff --git a/altosui/AltosFlightUI.java b/altosui/AltosFlightUI.java new file mode 100644 index 00000000..24d25bd7 --- /dev/null +++ b/altosui/AltosFlightUI.java @@ -0,0 +1,221 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosFlightUI extends JFrame implements AltosFlightDisplay { + String[] statusNames = { "Height (m)", "State", "RSSI (dBm)", "Speed (m/s)" }; + Object[][] statusData = { { "0", "pad", "-50", "0" } }; + + AltosVoice voice; + AltosFlightReader reader; + AltosDisplayThread thread; + + JTabbedPane pane; + + AltosPad pad; + AltosAscent ascent; + AltosDescent descent; + AltosLanded landed; + AltosSiteMap sitemap; + + private AltosFlightStatus flightStatus; + private AltosInfoTable flightInfo; + + static final int tab_pad = 1; + static final int tab_ascent = 2; + static final int tab_descent = 3; + static final int tab_landed = 4; + + int cur_tab = 0; + + boolean exit_on_close = false; + + int which_tab(AltosState state) { + if (state.state < Altos.ao_flight_boost) + return tab_pad; + if (state.state <= Altos.ao_flight_coast) + return tab_ascent; + if (state.state <= Altos.ao_flight_main) + return tab_descent; + return tab_landed; + } + + void stop_display() { + if (thread != null && thread.isAlive()) { + thread.interrupt(); + try { + thread.join(); + } catch (InterruptedException ie) {} + } + thread = null; + } + + void disconnect() { + stop_display(); + } + + public void reset() { + pad.reset(); + ascent.reset(); + descent.reset(); + landed.reset(); + flightInfo.clear(); + sitemap.reset(); + } + + public void show(AltosState state, int crc_errors) { + int tab = which_tab(state); + pad.show(state, crc_errors); + ascent.show(state, crc_errors); + descent.show(state, crc_errors); + landed.show(state, crc_errors); + if (tab != cur_tab) { + switch (tab) { + case tab_pad: + pane.setSelectedComponent(pad); + break; + case tab_ascent: + pane.setSelectedComponent(ascent); + break; + case tab_descent: + pane.setSelectedComponent(descent); + break; + case tab_landed: + pane.setSelectedComponent(landed); + } + cur_tab = tab; + } + flightStatus.show(state, crc_errors); + flightInfo.show(state, crc_errors); + sitemap.show(state, crc_errors); + } + + public void set_exit_on_close() { + exit_on_close = true; + } + + Container bag; + JComboBox channels; + + 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)); + + /* Stick channel selector at top of table for telemetry monitoring */ + if (serial >= 0) { + // Channel menu + channels = new AltosChannelMenu(AltosPreferences.channel(serial)); + channels.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + int channel = channels.getSelectedIndex(); + reader.set_channel(channel); + } + }); + c.gridx = 0; + c.gridy = 0; + c.anchor = GridBagConstraints.WEST; + bag.add (channels, c); + } + + /* Flight status is always visible */ + flightStatus = new AltosFlightStatus(); + c.gridx = 0; + c.gridy = 1; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + bag.add(flightStatus, c); + + /* The rest of the window uses a tabbed pane to + * show one of the alternate data views + */ + pane = new JTabbedPane(); + + pad = new AltosPad(); + pane.add("Launch Pad", pad); + + ascent = new AltosAscent(); + pane.add("Ascent", ascent); + + descent = new AltosDescent(); + pane.add("Descent", descent); + + landed = new AltosLanded(); + pane.add("Landed", landed); + + flightInfo = new AltosInfoTable(); + pane.add("Table", new JScrollPane(flightInfo)); + + sitemap = new AltosSiteMap(); + pane.add("Site Map", sitemap); + + /* Make the tabbed pane use the rest of the window space */ + c.gridx = 0; + c.gridy = 2; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + bag.add(pane, c); + + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + disconnect(); + setVisible(false); + dispose(); + if (exit_on_close) + System.exit(0); + } + }); + + pack(); + setVisible(true); + + thread = new AltosDisplayThread(this, voice, this, reader); + + thread.start(); + } + + public AltosFlightUI (AltosVoice in_voice, AltosFlightReader in_reader) { + this(in_voice, in_reader, -1); + } +} diff --git a/altosui/AltosGPS.java b/altosui/AltosGPS.java new file mode 100644 index 00000000..83821842 --- /dev/null +++ b/altosui/AltosGPS.java @@ -0,0 +1,215 @@ +/* + * 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.lang.*; +import java.text.*; + +public class AltosGPS { + public class AltosGPSSat { + int svid; + int c_n0; + } + + int nsat; + boolean locked; + boolean connected; + boolean date_valid; + double lat; /* degrees (+N -S) */ + double lon; /* degrees (+E -W) */ + int alt; /* m */ + int year; + int month; + int day; + int hour; + int minute; + int second; + + int gps_extended; /* has extra data */ + double ground_speed; /* m/s */ + int course; /* degrees */ + double climb_rate; /* m/s */ + double hdop; /* unitless? */ + int h_error; /* m */ + int v_error; /* m */ + + AltosGPSSat[] cc_gps_sat; /* tracking data */ + + void ParseGPSDate(String date) throws ParseException { + String[] ymd = date.split("-"); + if (ymd.length != 3) + throw new ParseException("error parsing GPS date " + date + " got " + ymd.length, 0); + year = AltosParse.parse_int(ymd[0]); + month = AltosParse.parse_int(ymd[1]); + day = AltosParse.parse_int(ymd[2]); + } + + void ParseGPSTime(String time) throws ParseException { + String[] hms = time.split(":"); + if (hms.length != 3) + throw new ParseException("Error parsing GPS time " + time + " got " + hms.length, 0); + hour = AltosParse.parse_int(hms[0]); + minute = AltosParse.parse_int(hms[1]); + second = AltosParse.parse_int(hms[2]); + } + + void ClearGPSTime() { + year = month = day = 0; + hour = minute = second = 0; + } + + public AltosGPS(String[] words, int i, int version) throws ParseException { + AltosParse.word(words[i++], "GPS"); + nsat = AltosParse.parse_int(words[i++]); + AltosParse.word(words[i++], "sat"); + + connected = false; + locked = false; + lat = lon = 0; + alt = 0; + ClearGPSTime(); + if ((words[i]).equals("unlocked")) { + connected = true; + i++; + } else if ((words[i]).equals("not-connected")) { + i++; + } else if (words.length >= 40) { + locked = true; + connected = true; + + if (version > 1) + ParseGPSDate(words[i++]); + else + year = month = day = 0; + ParseGPSTime(words[i++]); + lat = AltosParse.parse_coord(words[i++]); + lon = AltosParse.parse_coord(words[i++]); + alt = AltosParse.parse_int(words[i++]); + if (version > 1 || (i < words.length && !words[i].equals("SAT"))) { + ground_speed = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(H)")); + course = AltosParse.parse_int(words[i++]); + climb_rate = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(V)")); + hdop = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "(hdop)")); + h_error = AltosParse.parse_int(words[i++]); + v_error = AltosParse.parse_int(words[i++]); + } + } else { + i++; + } + if (i < words.length) { + AltosParse.word(words[i++], "SAT"); + int tracking_channels = 0; + if (words[i].equals("not-connected")) + tracking_channels = 0; + else + tracking_channels = AltosParse.parse_int(words[i]); + i++; + cc_gps_sat = new AltosGPS.AltosGPSSat[tracking_channels]; + for (int chan = 0; chan < tracking_channels; chan++) { + cc_gps_sat[chan] = new AltosGPS.AltosGPSSat(); + cc_gps_sat[chan].svid = AltosParse.parse_int(words[i++]); + /* Older versions included SiRF status bits */ + if (version < 2) + i++; + cc_gps_sat[chan].c_n0 = AltosParse.parse_int(words[i++]); + } + } else + cc_gps_sat = new AltosGPS.AltosGPSSat[0]; + } + + public void set_latitude(int in_lat) { + lat = in_lat / 10.0e7; + } + + public void set_longitude(int in_lon) { + lon = in_lon / 10.0e7; + } + + public void set_time(int hour, int minute, int second) { + hour = hour; + minute = minute; + second = second; + } + + public void set_date(int year, int month, int day) { + year = year; + month = month; + day = day; + } + + public void set_flags(int flags) { + flags = flags; + } + + public void set_altitude(int altitude) { + altitude = altitude; + } + + public void add_sat(int svid, int c_n0) { + if (cc_gps_sat == null) { + cc_gps_sat = new AltosGPS.AltosGPSSat[1]; + } else { + AltosGPSSat[] new_gps_sat = new AltosGPS.AltosGPSSat[cc_gps_sat.length + 1]; + for (int i = 0; i < cc_gps_sat.length; i++) + new_gps_sat[i] = cc_gps_sat[i]; + cc_gps_sat = new_gps_sat; + } + AltosGPS.AltosGPSSat sat = new AltosGPS.AltosGPSSat(); + sat.svid = svid; + sat.c_n0 = c_n0; + cc_gps_sat[cc_gps_sat.length - 1] = sat; + } + + public AltosGPS() { + ClearGPSTime(); + cc_gps_sat = null; + } + + public AltosGPS(AltosGPS old) { + nsat = old.nsat; + locked = old.locked; + connected = old.connected; + date_valid = old.date_valid; + lat = old.lat; /* degrees (+N -S) */ + lon = old.lon; /* degrees (+E -W) */ + alt = old.alt; /* m */ + year = old.year; + month = old.month; + day = old.day; + hour = old.hour; + minute = old.minute; + second = old.second; + + gps_extended = old.gps_extended; /* has extra data */ + ground_speed = old.ground_speed; /* m/s */ + course = old.course; /* degrees */ + climb_rate = old.climb_rate; /* m/s */ + hdop = old.hdop; /* unitless? */ + h_error = old.h_error; /* m */ + v_error = old.v_error; /* m */ + + if (old.cc_gps_sat != null) { + cc_gps_sat = new AltosGPSSat[old.cc_gps_sat.length]; + for (int i = 0; i < old.cc_gps_sat.length; i++) { + cc_gps_sat[i] = new AltosGPSSat(); + cc_gps_sat[i].svid = old.cc_gps_sat[i].svid; + cc_gps_sat[i].c_n0 = old.cc_gps_sat[i].c_n0; + } + } + } +} diff --git a/altosui/AltosGraph.java b/altosui/AltosGraph.java new file mode 100644 index 00000000..58c27979 --- /dev/null +++ b/altosui/AltosGraph.java @@ -0,0 +1,25 @@ + +// Copyright (c) 2010 Anthony Towns +// GPL v2 or later + +package altosui; + +import java.io.*; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.ChartUtilities; + +abstract class AltosGraph { + public String filename; + public abstract void addData(AltosDataPoint d); + public abstract JFreeChart createChart(); + public void toPNG() throws java.io.IOException { toPNG(300, 500); } + public void toPNG(int width, int height) + throws java.io.IOException + { + File pngout = new File(filename); + JFreeChart chart = createChart(); + ChartUtilities.saveChartAsPNG(pngout, chart, width, height); + System.out.println("Created " + filename); + } +} diff --git a/altosui/AltosGraphTime.java b/altosui/AltosGraphTime.java new file mode 100644 index 00000000..a5451280 --- /dev/null +++ b/altosui/AltosGraphTime.java @@ -0,0 +1,233 @@ + +// Copyright (c) 2010 Anthony Towns +// GPL v2 or later + +package altosui; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.HashMap; + +import org.jfree.chart.ChartUtilities; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.AxisLocation; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.labels.StandardXYToolTipGenerator; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.plot.ValueMarker; +import org.jfree.chart.renderer.xy.StandardXYItemRenderer; +import org.jfree.chart.renderer.xy.XYItemRenderer; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; +import org.jfree.ui.RectangleAnchor; +import org.jfree.ui.TextAnchor; + +class AltosGraphTime extends AltosGraph { + static interface Element { + void attachGraph(AltosGraphTime g); + void gotTimeData(double time, AltosDataPoint d); + void addToPlot(AltosGraphTime g, XYPlot plot); + } + + static class TimeAxis implements Element { + private int axis; + private Color color; + private String label; + private AxisLocation locn; + private double min_y = Double.NaN; + + public TimeAxis(int axis, String label, Color color, AxisLocation locn) + { + this.axis = axis; + this.color = color; + this.label = label; + this.locn = locn; + } + + public void setLowerBound(double min_y) { + this.min_y = min_y; + } + + public void attachGraph(AltosGraphTime g) { return; } + public void gotTimeData(double time, AltosDataPoint d) { return; } + + public void addToPlot(AltosGraphTime g, XYPlot plot) { + NumberAxis numAxis = new NumberAxis(label); + if (!Double.isNaN(min_y)) + numAxis.setLowerBound(min_y); + plot.setRangeAxis(axis, numAxis); + plot.setRangeAxisLocation(axis, locn); + numAxis.setLabelPaint(color); + numAxis.setTickLabelPaint(color); + numAxis.setAutoRangeIncludesZero(false); + } + } + + abstract static class TimeSeries implements Element { + protected XYSeries series; + private String axisName; + private Color color; + + public TimeSeries(String axisName, String label, Color color) { + this.series = new XYSeries(label); + this.axisName = axisName; + this.color = color; + } + + public void attachGraph(AltosGraphTime g) { + g.setAxis(this, axisName, color); + } + abstract public void gotTimeData(double time, AltosDataPoint d); + + public void addToPlot(AltosGraphTime g, XYPlot plot) { + XYSeriesCollection dataset = new XYSeriesCollection(); + dataset.addSeries(this.series); + + XYItemRenderer renderer = new StandardXYItemRenderer(); + renderer.setSeriesPaint(0, color); + + int dataNum = g.getDataNum(this); + int axisNum = g.getAxisNum(this); + + plot.setDataset(dataNum, dataset); + plot.mapDatasetToRangeAxis(dataNum, axisNum); + plot.setRenderer(dataNum, renderer); + } + } + + static class StateMarker implements Element { + private double val = Double.NaN; + private String name; + private int state; + + StateMarker(int state, String name) { + this.state = state; + this.name = name; + } + + public void attachGraph(AltosGraphTime g) { return; } + public void gotTimeData(double time, AltosDataPoint d) { + if (Double.isNaN(val) || time < val) { + if (d.state() == state) { + val = time; + } + } + } + + public void addToPlot(AltosGraphTime g, XYPlot plot) { + if (Double.isNaN(val)) + return; + + ValueMarker m = new ValueMarker(val); + m.setLabel(name); + m.setLabelAnchor(RectangleAnchor.TOP_RIGHT); + m.setLabelTextAnchor(TextAnchor.TOP_LEFT); + plot.addDomainMarker(m); + } + } + + private String callsign = null; + private Integer serial = null; + private Integer flight = null; + + private String title; + private ArrayList elements; + private HashMap axes; + private HashMap datasets; + private ArrayList datasetAxis; + + public AltosGraphTime(String title) { + this.filename = title.toLowerCase().replaceAll("[^a-z0-9]","_")+".png"; + this.title = title; + this.elements = new ArrayList(); + this.axes = new HashMap(); + this.datasets = new HashMap(); + this.datasetAxis = new ArrayList(); + } + + public AltosGraphTime addElement(Element e) { + e.attachGraph(this); + elements.add(e); + return this; + } + + public void setAxis(Element ds, String axisName, Color color) { + Integer axisNum = axes.get(axisName); + int dsNum = datasetAxis.size(); + if (axisNum == null) { + axisNum = newAxis(axisName, color); + } + datasets.put(ds, dsNum); + datasetAxis.add(axisNum); + } + + public int getAxisNum(Element ds) { + return datasetAxis.get( datasets.get(ds) ).intValue(); + } + public int getDataNum(Element ds) { + return datasets.get(ds).intValue(); + } + + private Integer newAxis(String name, Color color) { + int cnt = axes.size(); + AxisLocation locn = AxisLocation.BOTTOM_OR_LEFT; + if (cnt > 0) { + locn = AxisLocation.TOP_OR_RIGHT; + } + Integer res = new Integer(cnt); + axes.put(name, res); + this.addElement(new TimeAxis(cnt, name, color, locn)); + return res; + } + + public void addData(AltosDataPoint d) { + double time = d.time(); + for (Element e : elements) { + e.gotTimeData(time, d); + } + if (callsign == null) callsign = d.callsign(); + if (serial == null) serial = new Integer(d.serial()); + if (flight == null) flight = new Integer(d.flight()); + } + + public JFreeChart createChart() { + NumberAxis xAxis = new NumberAxis("Time (s)"); + xAxis.setAutoRangeIncludesZero(false); + XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false); + XYPlot plot = new XYPlot(); + plot.setDomainAxis(xAxis); + plot.setRenderer(renderer); + plot.setOrientation(PlotOrientation.VERTICAL); + + if (serial != null && flight != null) { + title = serial + "/" + flight + ": " + title; + } + if (callsign != null) { + title = callsign + " - " + title; + } + + renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); + JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, + plot, true); + ChartUtilities.applyCurrentTheme(chart); + + plot.setDomainPannable(true); + plot.setRangePannable(true); + + for (Element e : elements) { + e.addToPlot(this, plot); + } + + return chart; + } + + public void toPNG() throws java.io.IOException { + if (axes.size() > 1) { + toPNG(800, 500); + } else { + toPNG(300, 500); + } + } +} diff --git a/altosui/AltosGraphUI.java b/altosui/AltosGraphUI.java new file mode 100644 index 00000000..cd158651 --- /dev/null +++ b/altosui/AltosGraphUI.java @@ -0,0 +1,274 @@ + +// Copyright (c) 2010 Anthony Towns +// GPL v2 or later + +package altosui; + +import java.io.*; +import java.util.ArrayList; + +import javax.swing.JFrame; +import java.awt.Color; + +import org.jfree.chart.ChartPanel; +import org.jfree.chart.ChartUtilities; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.AxisLocation; +import org.jfree.ui.ApplicationFrame; +import org.jfree.ui.RefineryUtilities; + +public class AltosGraphUI extends JFrame +{ + static final private Color red = new Color(194,31,31); + static final private Color green = new Color(31,194,31); + static final private Color blue = new Color(31,31,194); + static final private Color black = new Color(31,31,31); + + static private class OverallGraphs { + AltosGraphTime.Element height = + new AltosGraphTime.TimeSeries("Height (m)", "Height (AGL)", red) { + public void gotTimeData(double time, AltosDataPoint d) { + series.add(time, d.height()); + } + }; + + AltosGraphTime.Element speed = + new AltosGraphTime.TimeSeries("Speed (m/s)", "Vertical Speed", green) { + public void gotTimeData(double time, AltosDataPoint d) { + if (d.state() < Altos.ao_flight_drogue) { + series.add(time, d.accel_speed()); + } else { + series.add(time, d.baro_speed()); + } + } + }; + + AltosGraphTime.Element acceleration = + new AltosGraphTime.TimeSeries("Acceleration (m/s\u00B2)", + "Axial Acceleration", blue) + { + public void gotTimeData(double time, AltosDataPoint d) { + series.add(time, d.acceleration()); + } + }; + + AltosGraphTime.Element temperature = + new AltosGraphTime.TimeSeries("Temperature (\u00B0C)", + "Board temperature", red) + { + public void gotTimeData(double time, AltosDataPoint d) { + series.add(time, d.temperature()); + } + }; + + AltosGraphTime.Element drogue_voltage = + new AltosGraphTime.TimeSeries("Voltage (V)", "Drogue Continuity", blue) + { + public void gotTimeData(double time, AltosDataPoint d) { + series.add(time, d.drogue_voltage()); + } + }; + + AltosGraphTime.Element main_voltage = + new AltosGraphTime.TimeSeries("Voltage (V)", "Main Continuity", green) + { + public void gotTimeData(double time, AltosDataPoint d) { + series.add(time, d.main_voltage()); + } + }; + + AltosGraphTime.Element e_pad = new AltosGraphTime.StateMarker(Altos.ao_flight_pad, "Pad"); + AltosGraphTime.Element e_boost = new AltosGraphTime.StateMarker(Altos.ao_flight_boost, "Boost"); + AltosGraphTime.Element e_fast = new AltosGraphTime.StateMarker(Altos.ao_flight_fast, "Fast"); + AltosGraphTime.Element e_coast = new AltosGraphTime.StateMarker(Altos.ao_flight_coast, "Coast"); + AltosGraphTime.Element e_drogue = new AltosGraphTime.StateMarker(Altos.ao_flight_drogue, "Drogue"); + AltosGraphTime.Element e_main = new AltosGraphTime.StateMarker(Altos.ao_flight_main, "Main"); + AltosGraphTime.Element e_landed = new AltosGraphTime.StateMarker(Altos.ao_flight_landed, "Landed"); + + protected AltosGraphTime myAltosGraphTime(String suffix) { + return (new AltosGraphTime("Overall " + suffix)) + .addElement(e_boost) + .addElement(e_drogue) + .addElement(e_main) + .addElement(e_landed); + } + + public ArrayList graphs() { + ArrayList graphs = new ArrayList(); + + graphs.add( myAltosGraphTime("Summary") + .addElement(height) + .addElement(speed) + .addElement(acceleration) ); + + graphs.add( myAltosGraphTime("Altitude") + .addElement(height) ); + + graphs.add( myAltosGraphTime("Speed") + .addElement(speed) ); + + graphs.add( myAltosGraphTime("Acceleration") + .addElement(acceleration) ); + + graphs.add( myAltosGraphTime("Temperature") + .addElement(temperature) ); + + graphs.add( myAltosGraphTime("Continuity") + .addElement(drogue_voltage) + .addElement(main_voltage) ); + + return graphs; + } + } + + static private class AscentGraphs extends OverallGraphs { + protected AltosGraphTime myAltosGraphTime(String suffix) { + return (new AltosGraphTime("Ascent " + suffix) { + public void addData(AltosDataPoint d) { + int state = d.state(); + if (Altos.ao_flight_boost <= state && state <= Altos.ao_flight_coast) { + super.addData(d); + } + } + }).addElement(e_boost) + .addElement(e_fast) + .addElement(e_coast); + } + } + + static private class DescentGraphs extends OverallGraphs { + protected AltosGraphTime myAltosGraphTime(String suffix) { + return (new AltosGraphTime("Descent " + suffix) { + public void addData(AltosDataPoint d) { + int state = d.state(); + if (Altos.ao_flight_drogue <= state && state <= Altos.ao_flight_main) { + super.addData(d); + } + } + }).addElement(e_drogue) + .addElement(e_main); + // ((XYGraph)graph[8]).ymin = new Double(-50); + } + } + + public AltosGraphUI(AltosRecordIterable records) { + super("Altos Graph"); + + Iterable reader = new AltosDataPointReader (records); + if (reader == null) + return; + + init(reader, 0); + } + + public AltosGraphUI(Iterable data, int which) + { + super("Altos Graph"); + init(data, which); + } + + private void init(Iterable data, int which) { + AltosGraph graph = createGraph(data, which); + + JFreeChart chart = graph.createChart(); + ChartPanel chartPanel = new ChartPanel(chart); + chartPanel.setMouseWheelEnabled(true); + chartPanel.setPreferredSize(new java.awt.Dimension(800, 500)); + setContentPane(chartPanel); + + pack(); + + RefineryUtilities.centerFrameOnScreen(this); + + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setVisible(true); + } + + private static AltosGraph createGraph(Iterable data, + int which) + { + return createGraphsWhich(data, which).get(0); + } + + private static ArrayList createGraphs( + Iterable data) + { + return createGraphsWhich(data, -1); + } + + private static ArrayList createGraphsWhich( + Iterable data, int which) + { + ArrayList graph = new ArrayList(); + graph.addAll((new OverallGraphs()).graphs()); + graph.addAll((new AscentGraphs()).graphs()); + graph.addAll((new DescentGraphs()).graphs()); + + if (which > 0) { + if (which >= graph.size()) { + which = 0; + } + AltosGraph g = graph.get(which); + graph = new ArrayList(); + graph.add(g); + } + + for (AltosDataPoint dp : data) { + for (AltosGraph g : graph) { + g.addData(dp); + } + } + + return graph; + } +} + +/* gnuplot bits... + * +300x400 + +-------------------------------------------------------- +TOO HARD! :) + +"ascent-gps-accuracy.png" "Vertical error margin to apogee - GPS v Baro (m)" + 5:($7 < 6 ? $24-$11 : 1/0) +"descent-gps-accuracy.png" "Vertical error margin during descent - GPS v Baro (m)" + 5:($7 < 6 ? 1/0 : $24-$11) + +set output "overall-gps-accuracy.png" +set ylabel "distance above sea level (m)" +plot "telemetry.csv" using 5:11 with lines ti "baro altitude" axis x1y1, \ + "telemetry.csv" using 5:24 with lines ti "gps altitude" axis x1y1 + +set term png tiny size 700,700 enhanced +set xlabel "m" +set ylabel "m" +set polar +set grid polar +set rrange[*:*] +set angles degrees + +set output "overall-gps-path.png" +#:30 with yerrorlines +plot "telemetry.csv" using (90-$33):($7 == 2 ? $31 : 1/0) with lines ti "pad", \ + "telemetry.csv" using (90-$33):($7 == 3 ? $31 : 1/0) with lines ti "boost", \ + "telemetry.csv" using (90-$33):($7 == 4 ? $31 : 1/0) with lines ti "fast", \ + "telemetry.csv" using (90-$33):($7 == 5 ? $31 : 1/0) with lines ti "coast", \ + "telemetry.csv" using (90-$33):($7 == 6 ? $31 : 1/0) with lines ti "drogue", \ + "telemetry.csv" using (90-$33):($7 == 7 ? $31 : 1/0) with lines ti "main", \ + "telemetry.csv" using (90-$33):($7 == 8 ? $31 : 1/0) with lines ti "landed" + +set output "ascent-gps-path.png" +plot "telemetry.csv" using (90-$33):($7 == 2 ? $31 : 1/0):30 with lines ti "pad", \ + "telemetry.csv" using (90-$33):($7 == 3 ? $31 : 1/0):20 with lines ti "boost", \ + "telemetry.csv" using (90-$33):($7 == 4 ? $31 : 1/0):10 with lines ti "fast", \ + "telemetry.csv" using (90-$33):($7 == 5 ? $31 : 1/0):5 with lines ti "coast" + +set output "descent-gps-path.png" +plot "telemetry.csv" using (90-$33):($7 == 6 ? $31 : 1/0) with lines ti "drogue", \ + "telemetry.csv" using (90-$33):($7 == 7 ? $31 : 1/0) with lines ti "main", \ + "telemetry.csv" using (90-$33):($7 == 8 ? $31 : 1/0) with lines ti "landed" + + */ + + diff --git a/altosui/AltosGreatCircle.java b/altosui/AltosGreatCircle.java new file mode 100644 index 00000000..fb1b6ab3 --- /dev/null +++ b/altosui/AltosGreatCircle.java @@ -0,0 +1,100 @@ +/* + * 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.lang.Math; + +public class AltosGreatCircle { + double distance; + double bearing; + + double sqr(double a) { return a * a; } + + static final double rad = Math.PI / 180; + static final double earth_radius = 6371.2 * 1000; /* in meters */ + + static int BEARING_LONG = 0; + static int BEARING_SHORT = 1; + static int BEARING_VOICE = 2; + String bearing_words(int length) { + String [][] bearing_string = { + { + "North", "North North East", "North East", "East North East", + "East", "East South East", "South East", "South South East", + "South", "South South West", "South West", "West South West", + "West", "West North West", "North West", "North North West" + }, { + "N", "NNE", "NE", "ENE", + "E", "ESE", "SE", "SSE", + "S", "SSW", "SW", "WSW", + "W", "WNW", "NW", "NNW" + }, { + "north", "nor nor east", "north east", "east nor east", + "east", "east sow east", "south east", "sow sow east", + "south", "sow sow west", "south west", "west sow west", + "west", "west nor west", "north west", "nor nor west " + } + }; + return bearing_string[length][(int)((bearing / 90 * 8 + 1) / 2)%16]; + } + + public AltosGreatCircle (double start_lat, double start_lon, + double end_lat, double end_lon) + { + double lat1 = rad * start_lat; + double lon1 = rad * -start_lon; + double lat2 = rad * end_lat; + double lon2 = rad * -end_lon; + + double d_lon = lon2 - lon1; + + /* From http://en.wikipedia.org/wiki/Great-circle_distance */ + double vdn = Math.sqrt(sqr(Math.cos(lat2) * Math.sin(d_lon)) + + sqr(Math.cos(lat1) * Math.sin(lat2) - + Math.sin(lat1) * Math.cos(lat2) * Math.cos(d_lon))); + double vdd = Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(d_lon); + double d = Math.atan2(vdn,vdd); + double course; + + if (Math.cos(lat1) < 1e-20) { + if (lat1 > 0) + course = Math.PI; + else + course = -Math.PI; + } else { + if (d < 1e-10) + course = 0; + else + course = Math.acos((Math.sin(lat2)-Math.sin(lat1)*Math.cos(d)) / + (Math.sin(d)*Math.cos(lat1))); + if (Math.sin(lon2-lon1) > 0) + course = 2 * Math.PI-course; + } + distance = d * earth_radius; + bearing = course * 180/Math.PI; + } + + public AltosGreatCircle(AltosGPS start, AltosGPS end) { + this(start.lat, start.lon, end.lat, end.lon); + } + + public AltosGreatCircle() { + distance = 0; + bearing = 0; + } +} diff --git a/altosui/AltosHexfile.java b/altosui/AltosHexfile.java new file mode 100644 index 00000000..19e35ae1 --- /dev/null +++ b/altosui/AltosHexfile.java @@ -0,0 +1,252 @@ +/* + * 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.lang.*; +import java.io.*; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.LinkedList; +import java.util.Iterator; +import java.util.Arrays; + +class HexFileInputStream extends PushbackInputStream { + public int line; + + public HexFileInputStream(FileInputStream o) { + super(new BufferedInputStream(o)); + line = 1; + } + + public int read() throws IOException { + int c = super.read(); + if (c == '\n') + line++; + return c; + } + + public void unread(int c) throws IOException { + if (c == '\n') + line--; + if (c != -1) + super.unread(c); + } +} + +class HexRecord implements Comparable { + public int address; + public int type; + public byte checksum; + public byte[] data; + + static final int NORMAL = 0; + static final int EOF = 1; + static final int EXTENDED_ADDRESS = 2; + + enum read_state { + marker, + length, + address, + type, + data, + checksum, + newline, + white, + done, + } + + boolean ishex(int c) { + if ('0' <= c && c <= '9') + return true; + if ('a' <= c && c <= 'f') + return true; + if ('A' <= c && c <= 'F') + return true; + return false; + } + + boolean isspace(int c) { + switch (c) { + case ' ': + case '\t': + return true; + } + return false; + } + + int fromhex(int c) { + if ('0' <= c && c <= '9') + return c - '0'; + if ('a' <= c && c <= 'f') + return c - 'a' + 10; + if ('A' <= c && c <= 'F') + return c - 'A' + 10; + return -1; + } + + public byte checksum() { + byte got = 0; + + got += data.length; + got += (address >> 8) & 0xff; + got += (address ) & 0xff; + got += type; + for (int i = 0; i < data.length; i++) + got += data[i]; + return (byte) (-got); + } + + public int compareTo(Object other) { + HexRecord o = (HexRecord) other; + return address - o.address; + } + + public String toString() { + return String.format("%04x: %02x (%d)", address, type, data.length); + } + + public HexRecord(HexFileInputStream input) throws IOException { + read_state state = read_state.marker; + int nhexbytes = 0; + int hex = 0; + int ndata = 0; + byte got_checksum; + + while (state != read_state.done) { + int c = input.read(); + if (c < 0 && state != read_state.white) + throw new IOException(String.format("%d: Unexpected EOF", input.line)); + if (c == ' ') + continue; + switch (state) { + case marker: + if (c != ':') + throw new IOException("Missing ':'"); + state = read_state.length; + nhexbytes = 2; + hex = 0; + break; + case length: + case address: + case type: + case data: + case checksum: + if(!ishex(c)) + throw new IOException(String.format("Non-hex char '%c'", c)); + hex = hex << 4 | fromhex(c); + --nhexbytes; + if (nhexbytes != 0) + break; + + switch (state) { + case length: + data = new byte[hex]; + state = read_state.address; + nhexbytes = 4; + break; + case address: + address = hex; + state = read_state.type; + nhexbytes = 2; + break; + case type: + type = hex; + if (data.length > 0) + state = read_state.data; + else + state = read_state.checksum; + nhexbytes = 2; + ndata = 0; + break; + case data: + data[ndata] = (byte) hex; + ndata++; + nhexbytes = 2; + if (ndata == data.length) + state = read_state.checksum; + break; + case checksum: + checksum = (byte) hex; + state = read_state.newline; + break; + default: + break; + } + hex = 0; + break; + case newline: + if (c != '\n' && c != '\r') + throw new IOException("Missing newline"); + state = read_state.white; + break; + case white: + if (!isspace(c)) { + input.unread(c); + state = read_state.done; + } + break; + case done: + break; + } + } + got_checksum = checksum(); + if (got_checksum != checksum) + throw new IOException(String.format("Invalid checksum (read 0x%02x computed 0x%02x)\n", + checksum, got_checksum)); + } +} + +public class AltosHexfile { + public int address; + public byte[] data; + + public byte get_byte(int a) { + return data[a - address]; + } + + public AltosHexfile(FileInputStream file) throws IOException { + HexFileInputStream input = new HexFileInputStream(file); + LinkedList record_list = new LinkedList(); + boolean done = false; + + while (!done) { + HexRecord record = new HexRecord(input); + + if (record.type == HexRecord.EOF) + done = true; + else + record_list.add(record); + } + HexRecord[] records = record_list.toArray(new HexRecord[0]); + Arrays.sort(records); + if (records.length > 0) { + int base = records[0].address; + int bound = records[records.length-1].address + + records[records.length-1].data.length; + + data = new byte[bound - base]; + address = base; + Arrays.fill(data, (byte) 0xff); + + /* Paint the records into the new array */ + for (int i = 0; i < records.length; i++) { + for (int j = 0; j < records[i].data.length; j++) + data[records[i].address - base + j] = records[i].data[j]; + } + } + } +} \ No newline at end of file diff --git a/altosui/AltosIgnite.java b/altosui/AltosIgnite.java new file mode 100644 index 00000000..3cbd8a75 --- /dev/null +++ b/altosui/AltosIgnite.java @@ -0,0 +1,173 @@ +/* + * 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.*; +import java.util.concurrent.*; + +public class AltosIgnite { + AltosDevice device; + AltosSerial serial; + boolean remote; + boolean serial_started; + 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 { + serial_started = true; + if (remote) { + serial.set_radio(); + serial.printf("p\nE 0\n"); + serial.flush_input(); + } + } + + private void stop_serial() throws InterruptedException { + if (!serial_started) + return; + serial_started = false; + 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) throws InterruptedException, TimeoutException { + int status = Unknown; + if (serial == null) + return status; + string_ref status_name = new string_ref(); + start_serial(); + serial.printf("t\n"); + for (;;) { + String line = serial.get_reply(5000); + 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; + 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 void close() { + try { + stop_serial(); + } catch (InterruptedException ie) { + } + serial.close(); + serial = null; + } + + public AltosIgnite(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException { + + device = in_device; + serial = new AltosSerial(device); + remote = false; + + if (!device.matchProduct(AltosDevice.product_telemetrum)) + remote = true; + } +} \ No newline at end of file diff --git a/altosui/AltosIgniteUI.java b/altosui/AltosIgniteUI.java new file mode 100644 index 00000000..d542729c --- /dev/null +++ b/altosui/AltosIgniteUI.java @@ -0,0 +1,317 @@ +/* + * 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.*; +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()) + arm.setText(String.format("%d", time_remaining)); + else + arm.setText("Arm"); + } + + void start_timer() { + time_remaining = 10; + set_arm_text(); + timer_running = true; + } + + void stop_timer() { + time_remaining = 0; + arm.setSelected(false); + arm.setEnabled(false); + fire.setEnabled(false); + timer_running = false; + set_arm_text(); + } + + void cancel () { + apogee.setSelected(false); + main.setSelected(false); + fire.setEnabled(false); + 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.toShortString()), + "Connection Failed", + JOptionPane.ERROR_MESSAGE); + } + + void tick_timer() { + 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() { + 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; + ignite.fire(igniter); + cancel(); + } + } + + public void actionPerformed(ActionEvent e) { + String cmd = e.getActionCommand(); + if (cmd.equals("apogee") || cmd.equals("main")) { + stop_timer(); + } + + if (cmd.equals("apogee") && apogee.isSelected()) { + main.setSelected(false); + arm.setEnabled(true); + } + if (cmd.equals("main") && main.isSelected()) { + apogee.setSelected(false); + arm.setEnabled(true); + } + + 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(); + 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.toShortString()), + "Cannot open target device", + JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(owner, + String.format("Device \"%s\" already in use", + device.toShortString()), + "Device in use", + JOptionPane.ERROR_MESSAGE); + } catch (IOException ee) { + JOptionPane.showMessageDialog(owner, + device.toShortString(), + 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; + + 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; + 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); + apogee.setActionCommand("apogee"); + + 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 = 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); + arm.setActionCommand("arm"); + arm.setEnabled(false); + + c.gridx = 1; + c.gridy = 3; + c.gridwidth = 1; + c.anchor = GridBagConstraints.CENTER; + fire = new JButton ("Fire"); + fire.setEnabled(false); + pane.add(fire, c); + fire.addActionListener(this); + fire.setActionCommand("fire"); + + pack(); + setLocationRelativeTo(owner); + setVisible(true); + + addWindowListener(new ConfigListener(this)); + } +} \ No newline at end of file diff --git a/altosui/AltosInfoTable.java b/altosui/AltosInfoTable.java new file mode 100644 index 00000000..723f8301 --- /dev/null +++ b/altosui/AltosInfoTable.java @@ -0,0 +1,190 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosInfoTable extends JTable { + private AltosFlightInfoTableModel model; + + private Font infoLabelFont = new Font("SansSerif", Font.PLAIN, 14); + private Font infoValueFont = new Font("Monospaced", Font.PLAIN, 14); + + static final int info_columns = 3; + static final int info_rows = 17; + + int desired_row_height() { + FontMetrics infoValueMetrics = getFontMetrics(infoValueFont); + return (infoValueMetrics.getHeight() + infoValueMetrics.getLeading()) * 18 / 10; + } + + public AltosInfoTable() { + super(new AltosFlightInfoTableModel(info_rows, info_columns)); + model = (AltosFlightInfoTableModel) getModel(); + setFont(infoValueFont); + setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS); + setShowGrid(true); + setRowHeight(desired_row_height()); + doLayout(); + } + + public Dimension getPreferredScrollableViewportSize() { + return getPreferredSize(); + } + + void info_reset() { + model.reset(); + } + + void info_add_row(int col, String name, String value) { + model.addRow(col, name, value); + } + + void info_add_row(int col, String name, String format, Object... parameters) { + info_add_row (col, name, String.format(format, parameters)); + } + + void info_add_deg(int col, String name, double v, int pos, int neg) { + int c = pos; + if (v < 0) { + c = neg; + v = -v; + } + double deg = Math.floor(v); + double min = (v - deg) * 60; + + info_add_row(col, name, String.format("%3.0f°%08.5f'", deg, min)); + } + + void info_finish() { + model.finish(); + } + + public void clear() { + model.clear(); + } + + public void show(AltosState state, int crc_errors) { + if (state == null) + return; + info_reset(); + info_add_row(0, "Rocket state", "%s", state.data.state()); + info_add_row(0, "Callsign", "%s", state.data.callsign); + info_add_row(0, "Rocket serial", "%6d", state.data.serial); + info_add_row(0, "Rocket flight", "%6d", state.data.flight); + + info_add_row(0, "RSSI", "%6d dBm", state.data.rssi); + info_add_row(0, "CRC Errors", "%6d", crc_errors); + info_add_row(0, "Height", "%6.0f m", state.height); + info_add_row(0, "Max height", "%6.0f m", state.max_height); + info_add_row(0, "Acceleration", "%8.1f m/s²", state.acceleration); + info_add_row(0, "Max acceleration", "%8.1f m/s²", state.max_acceleration); + info_add_row(0, "Speed", "%8.1f m/s", state.ascent ? state.speed : state.baro_speed); + info_add_row(0, "Max Speed", "%8.1f m/s", state.max_speed); + info_add_row(0, "Temperature", "%9.2f °C", state.temperature); + info_add_row(0, "Battery", "%9.2f V", state.battery); + info_add_row(0, "Drogue", "%9.2f V", state.drogue_sense); + info_add_row(0, "Main", "%9.2f V", state.main_sense); + info_add_row(0, "Pad altitude", "%6.0f m", state.ground_altitude); + if (state.gps == null) { + info_add_row(1, "GPS", "not available"); + } else { + if (state.gps_ready) + info_add_row(1, "GPS state", "%s", "ready"); + else + info_add_row(1, "GPS state", "wait (%d)", + state.gps_waiting); + if (state.data.gps.locked) + info_add_row(1, "GPS", " locked"); + else if (state.data.gps.connected) + info_add_row(1, "GPS", " unlocked"); + else + info_add_row(1, "GPS", " missing"); + info_add_row(1, "Satellites", "%6d", state.data.gps.nsat); + info_add_deg(1, "Latitude", state.gps.lat, 'N', 'S'); + info_add_deg(1, "Longitude", state.gps.lon, 'E', 'W'); + info_add_row(1, "GPS altitude", "%6d", state.gps.alt); + info_add_row(1, "GPS height", "%6.0f", state.gps_height); + + /* The SkyTraq GPS doesn't report these values */ + if (false) { + info_add_row(1, "GPS ground speed", "%8.1f m/s %3d°", + state.gps.ground_speed, + state.gps.course); + info_add_row(1, "GPS climb rate", "%8.1f m/s", + state.gps.climb_rate); + info_add_row(1, "GPS error", "%6d m(h)%3d m(v)", + state.gps.h_error, state.gps.v_error); + } + info_add_row(1, "GPS hdop", "%8.1f", state.gps.hdop); + + if (state.npad > 0) { + if (state.from_pad != null) { + info_add_row(1, "Distance from pad", "%6d m", + (int) (state.from_pad.distance + 0.5)); + info_add_row(1, "Direction from pad", "%6d°", + (int) (state.from_pad.bearing + 0.5)); + info_add_row(1, "Elevation from pad", "%6d°", + (int) (state.elevation + 0.5)); + info_add_row(1, "Range from pad", "%6d m", + (int) (state.range + 0.5)); + } else { + info_add_row(1, "Distance from pad", "unknown"); + info_add_row(1, "Direction from pad", "unknown"); + info_add_row(1, "Elevation from pad", "unknown"); + info_add_row(1, "Range from pad", "unknown"); + } + info_add_deg(1, "Pad latitude", state.pad_lat, 'N', 'S'); + info_add_deg(1, "Pad longitude", state.pad_lon, 'E', 'W'); + info_add_row(1, "Pad GPS alt", "%6.0f m", state.pad_alt); + } + info_add_row(1, "GPS date", "%04d-%02d-%02d", + state.gps.year, + state.gps.month, + state.gps.day); + info_add_row(1, "GPS time", " %02d:%02d:%02d", + state.gps.hour, + state.gps.minute, + state.gps.second); + int nsat_vis = 0; + int c; + + if (state.gps.cc_gps_sat == null) + info_add_row(2, "Satellites Visible", "%4d", 0); + else { + info_add_row(2, "Satellites Visible", "%4d", state.gps.cc_gps_sat.length); + for (c = 0; c < state.gps.cc_gps_sat.length; c++) { + info_add_row(2, "Satellite id,C/N0", + "%4d, %4d", + state.gps.cc_gps_sat[c].svid, + state.gps.cc_gps_sat[c].c_n0); + } + } + } + info_finish(); + } +} diff --git a/altosui/AltosKML.java b/altosui/AltosKML.java new file mode 100644 index 00000000..d586033f --- /dev/null +++ b/altosui/AltosKML.java @@ -0,0 +1,169 @@ +/* + * 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.lang.*; +import java.io.*; +import java.text.*; +import java.util.*; + +public class AltosKML implements AltosWriter { + + File name; + PrintStream out; + int state = -1; + AltosRecord prev = null; + + static final String[] kml_state_colors = { + "FF000000", + "FF000000", + "FF000000", + "FF0000FF", + "FF4080FF", + "FF00FFFF", + "FFFF0000", + "FF00FF00", + "FF000000", + "FFFFFFFF" + }; + + static final String kml_header_start = + "\n" + + "\n" + + "\n" + + " AO Flight#%d S/N: %03d\n" + + " \n"; + static final String kml_header_end = + " \n" + + " 0\n"; + + static final String kml_style_start = + " \n"; + + static final String kml_placemark_start = + " \n" + + " %s\n" + + " #ao-flightstate-%s\n" + + " \n" + + " 1\n" + + " absolute\n" + + " \n"; + + static final String kml_coord_fmt = + " %12.7f, %12.7f, %12.7f \n"; + + static final String kml_placemark_end = + " \n" + + " \n" + + " \n"; + + static final String kml_footer = + "\n" + + "\n"; + + void start (AltosRecord record) { + out.printf(kml_header_start, record.flight, record.serial); + out.printf("Date: %04d-%02d-%02d\n", + record.gps.year, record.gps.month, record.gps.day); + out.printf("Time: %2d:%02d:%02d\n", + record.gps.hour, record.gps.minute, record.gps.second); + out.printf("%s", kml_header_end); + } + + boolean started = false; + + void state_start(AltosRecord record) { + String state_name = Altos.state_name(record.state); + out.printf(kml_style_start, state_name, kml_state_colors[record.state]); + out.printf("\tState: %s\n", state_name); + out.printf("%s", kml_style_end); + out.printf(kml_placemark_start, state_name, state_name); + } + + void state_end(AltosRecord record) { + out.printf("%s", kml_placemark_end); + } + + void coord(AltosRecord record) { + AltosGPS gps = record.gps; + out.printf(kml_coord_fmt, + gps.lon, gps.lat, + record.filtered_altitude(), (double) gps.alt, + record.time, gps.nsat); + } + + void end() { + out.printf("%s", kml_footer); + } + + public void close() { + if (prev != null) { + state_end(prev); + end(); + prev = null; + } + } + + public void write(AltosRecord record) { + AltosGPS gps = record.gps; + + if (gps == null) + return; + if (!started) { + start(record); + started = true; + } + if (prev != null && + prev.gps.second == record.gps.second && + prev.gps.minute == record.gps.minute && + prev.gps.hour == record.gps.hour) + return; + if (record.state != state) { + state = record.state; + if (prev != null) { + coord(record); + state_end(prev); + } + state_start(record); + } + coord(record); + prev = record; + } + + public void write(AltosRecordIterable iterable) { + for (AltosRecord record : iterable) + write(record); + } + + public AltosKML(File in_name) throws FileNotFoundException { + name = in_name; + out = new PrintStream(name); + } + + public AltosKML(String in_string) throws FileNotFoundException { + this(new File(in_string)); + } +} diff --git a/altosui/AltosLanded.java b/altosui/AltosLanded.java new file mode 100644 index 00000000..d34efe6d --- /dev/null +++ b/altosui/AltosLanded.java @@ -0,0 +1,212 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosLanded extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + Font label_font; + Font value_font; + + public class LandedValue { + JLabel label; + JTextField value; + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + } + + void show(String format, double v) { + value.setText(String.format(format, v)); + } + + public LandedValue (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + label = new JLabel(text); + label.setFont(label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 0; c.gridy = y; + c.insets = new Insets(10, 10, 10, 10); + c.anchor = GridBagConstraints.WEST; + c.weightx = 0; + c.fill = GridBagConstraints.VERTICAL; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(Altos.text_width); + value.setFont(value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 1; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.weightx = 1; + c.fill = GridBagConstraints.BOTH; + layout.setConstraints(value, c); + add(value); + } + } + + String pos(double p, String pos, String neg) { + String h = pos; + if (p < 0) { + h = neg; + p = -p; + } + int deg = (int) Math.floor(p); + double min = (p - Math.floor(p)) * 60.0; + return String.format("%s %4d° %9.6f", h, deg, min); + } + + class Lat extends LandedValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + value.setText(pos(state.gps.lat,"N", "S")); + else + value.setText("???"); + } + public Lat (GridBagLayout layout, int y) { + super (layout, y, "Latitude"); + } + } + + Lat lat; + + class Lon extends LandedValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + value.setText(pos(state.gps.lon,"E", "W")); + else + value.setText("???"); + } + public Lon (GridBagLayout layout, int y) { + super (layout, y, "Longitude"); + } + } + + Lon lon; + + class Bearing extends LandedValue { + void show (AltosState state, int crc_errors) { + if (state.from_pad != null) + show("%3.0f°", state.from_pad.bearing); + else + value.setText("???"); + } + public Bearing (GridBagLayout layout, int y) { + super (layout, y, "Bearing"); + } + } + + Bearing bearing; + + class Distance extends LandedValue { + void show (AltosState state, int crc_errors) { + if (state.from_pad != null) + show("%6.0f m", state.from_pad.distance); + else + value.setText("???"); + } + public Distance (GridBagLayout layout, int y) { + super (layout, y, "Distance"); + } + } + + Distance distance; + + class Height extends LandedValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m", state.max_height); + } + public Height (GridBagLayout layout, int y) { + super (layout, y, "Maximum Height"); + } + } + + Height height; + + class Speed extends LandedValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m/s", state.max_speed); + } + public Speed (GridBagLayout layout, int y) { + super (layout, y, "Maximum Speed"); + } + } + + Speed speed; + + class Accel extends LandedValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m/s²", state.max_acceleration); + } + public Accel (GridBagLayout layout, int y) { + super (layout, y, "Maximum Acceleration"); + } + } + + Accel accel; + + public void reset() { + lat.reset(); + lon.reset(); + bearing.reset(); + distance.reset(); + height.reset(); + speed.reset(); + accel.reset(); + } + + public void show(AltosState state, int crc_errors) { + bearing.show(state, crc_errors); + distance.show(state, crc_errors); + lat.show(state, crc_errors); + lon.show(state, crc_errors); + height.show(state, crc_errors); + speed.show(state, crc_errors); + accel.show(state, crc_errors); + } + + public AltosLanded() { + layout = new GridBagLayout(); + + label_font = new Font("Dialog", Font.PLAIN, 22); + value_font = new Font("Monospaced", Font.PLAIN, 22); + setLayout(layout); + + /* Elements in descent display */ + bearing = new Bearing(layout, 0); + distance = new Distance(layout, 1); + lat = new Lat(layout, 2); + lon = new Lon(layout, 3); + height = new Height(layout, 4); + speed = new Speed(layout, 5); + accel = new Accel(layout, 6); + } +} diff --git a/altosui/AltosLed.java b/altosui/AltosLed.java new file mode 100644 index 00000000..e08e9960 --- /dev/null +++ b/altosui/AltosLed.java @@ -0,0 +1,54 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosLed extends JLabel { + ImageIcon on, off; + + ImageIcon create_icon(String path) { + java.net.URL imgURL = AltosUI.class.getResource(path); + if (imgURL != null) + return new ImageIcon(imgURL); + System.err.printf("Cannot find icon \"%s\"\n", path); + return null; + } + + public void set(boolean set) { + if (set) + setIcon(on); + else + setIcon(off); + } + + public AltosLed(String on_path, String off_path) { + on = create_icon(on_path); + off = create_icon(off_path); + setIcon(off); + } +} diff --git a/altosui/AltosLights.java b/altosui/AltosLights.java new file mode 100644 index 00000000..2fa38412 --- /dev/null +++ b/altosui/AltosLights.java @@ -0,0 +1,73 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosLights extends JComponent { + + GridBagLayout gridbag; + + AltosLed red, green; + + ImageIcon create_icon(String path, String description) { + java.net.URL imgURL = AltosUI.class.getResource(path); + if (imgURL != null) + return new ImageIcon(imgURL, description); + System.err.printf("Cannot find icon \"%s\"\n", path); + return null; + } + + public void set (boolean on) { + if (on) { + red.set(false); + green.set(true); + } else { + red.set(true); + green.set(false); + } + } + + public AltosLights() { + GridBagConstraints c; + gridbag = new GridBagLayout(); + setLayout(gridbag); + + c = new GridBagConstraints(); + red = new AltosLed("/redled.png", "/grayled.png"); + c.gridx = 0; c.gridy = 0; + c.insets = new Insets (0, 5, 0, 5); + gridbag.setConstraints(red, c); + add(red); + red.set(true); + green = new AltosLed("/greenled.png", "/grayled.png"); + c.gridx = 1; c.gridy = 0; + gridbag.setConstraints(green, c); + add(green); + green.set(false); + } +} diff --git a/altosui/AltosLine.java b/altosui/AltosLine.java new file mode 100644 index 00000000..86e9d4c6 --- /dev/null +++ b/altosui/AltosLine.java @@ -0,0 +1,30 @@ +/* + * 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; + +public class AltosLine { + public String line; + + public AltosLine() { + line = null; + } + + public AltosLine(String s) { + line = s; + } +} \ No newline at end of file diff --git a/altosui/AltosLog.java b/altosui/AltosLog.java new file mode 100644 index 00000000..dd147d21 --- /dev/null +++ b/altosui/AltosLog.java @@ -0,0 +1,115 @@ +/* + * 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.*; +import java.lang.*; +import java.util.*; +import java.text.ParseException; +import java.util.concurrent.LinkedBlockingQueue; + +/* + * This creates a thread to capture telemetry data and write it to + * a log file + */ +class AltosLog implements Runnable { + + LinkedBlockingQueue input_queue; + LinkedBlockingQueue pending_queue; + int serial; + int flight; + FileWriter log_file; + Thread log_thread; + + private void close_log_file() { + if (log_file != null) { + try { + log_file.close(); + } catch (IOException io) { + } + log_file = null; + } + } + + void close() { + close_log_file(); + if (log_thread != null) { + log_thread.interrupt(); + log_thread = null; + } + } + + boolean open (AltosTelemetry telem) throws IOException { + AltosFile a = new AltosFile(telem); + + log_file = new FileWriter(a, true); + if (log_file != null) { + while (!pending_queue.isEmpty()) { + try { + String s = pending_queue.take(); + log_file.write(s); + log_file.write('\n'); + } catch (InterruptedException ie) { + } + } + log_file.flush(); + } + return log_file != null; + } + + public void run () { + try { + for (;;) { + AltosLine line = input_queue.take(); + if (line.line == null) + continue; + try { + AltosTelemetry telem = new AltosTelemetry(line.line); + if (telem.serial != serial || telem.flight != flight || log_file == null) { + close_log_file(); + serial = telem.serial; + flight = telem.flight; + open(telem); + } + } catch (ParseException pe) { + } catch (AltosCRCException ce) { + } + if (log_file != null) { + log_file.write(line.line); + log_file.write('\n'); + log_file.flush(); + } else + pending_queue.put(line.line); + } + } catch (InterruptedException ie) { + } catch (IOException ie) { + } + close(); + } + + public AltosLog (AltosSerial s) { + pending_queue = new LinkedBlockingQueue (); + input_queue = new LinkedBlockingQueue (); + s.add_monitor(input_queue); + serial = -1; + flight = -1; + log_file = null; + log_thread = new Thread(this); + log_thread.start(); + } +} diff --git a/altosui/AltosPad.java b/altosui/AltosPad.java new file mode 100644 index 00000000..66954347 --- /dev/null +++ b/altosui/AltosPad.java @@ -0,0 +1,269 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosPad extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + + public class LaunchStatus { + JLabel label; + JTextField value; + AltosLights lights; + + void show(AltosState state, int crc_errors) {} + void reset() { + value.setText(""); + lights.set(false); + } + + public LaunchStatus (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + lights = new AltosLights(); + c.gridx = 0; c.gridy = y; + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(lights, c); + add(lights); + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(Altos.text_width); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + + } + } + + public class LaunchValue { + JLabel label; + JTextField value; + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + } + public LaunchValue (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.weighty = 1; + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(Altos.text_width); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + } + } + + class Battery extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.battery)); + lights.set(state.battery > 3.7); + } + public Battery (GridBagLayout layout, int y) { + super(layout, y, "Battery Voltage"); + } + } + + Battery battery; + + class Apogee extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.drogue_sense)); + lights.set(state.drogue_sense > 3.2); + } + public Apogee (GridBagLayout layout, int y) { + super(layout, y, "Apogee Igniter Voltage"); + } + } + + Apogee apogee; + + class Main extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.main_sense)); + lights.set(state.main_sense > 3.2); + } + public Main (GridBagLayout layout, int y) { + super(layout, y, "Main Igniter Voltage"); + } + } + + Main main; + + class GPSLocked extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4d sats", state.gps.nsat)); + lights.set(state.gps.locked); + } + public GPSLocked (GridBagLayout layout, int y) { + super (layout, y, "GPS Locked"); + } + } + + GPSLocked gps_locked; + + class GPSReady extends LaunchStatus { + void show (AltosState state, int crc_errors) { + if (state.gps_ready) + value.setText("Ready"); + else + value.setText(String.format("Waiting %d", state.gps_waiting)); + lights.set(state.gps_ready); + } + public GPSReady (GridBagLayout layout, int y) { + super (layout, y, "GPS Ready"); + } + } + + GPSReady gps_ready; + + String pos(double p, String pos, String neg) { + String h = pos; + if (p < 0) { + h = neg; + p = -p; + } + int deg = (int) Math.floor(p); + double min = (p - Math.floor(p)) * 60.0; + return String.format("%s %4d° %9.6f", h, deg, min); + } + + class PadLat extends LaunchValue { + void show (AltosState state, int crc_errors) { + value.setText(pos(state.pad_lat,"N", "S")); + } + public PadLat (GridBagLayout layout, int y) { + super (layout, y, "Pad Latitude"); + } + } + + PadLat pad_lat; + + class PadLon extends LaunchValue { + void show (AltosState state, int crc_errors) { + value.setText(pos(state.pad_lon,"E", "W")); + } + public PadLon (GridBagLayout layout, int y) { + super (layout, y, "Pad Longitude"); + } + } + + PadLon pad_lon; + + class PadAlt extends LaunchValue { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.0f m", state.pad_alt)); + } + public PadAlt (GridBagLayout layout, int y) { + super (layout, y, "Pad Altitude"); + } + } + + PadAlt pad_alt; + + public void reset() { + battery.reset(); + apogee.reset(); + main.reset(); + gps_locked.reset(); + gps_ready.reset(); + pad_lat.reset(); + pad_lon.reset(); + pad_alt.reset(); + } + + public void show(AltosState state, int crc_errors) { + battery.show(state, crc_errors); + apogee.show(state, crc_errors); + main.show(state, crc_errors); + gps_locked.show(state, crc_errors); + gps_ready.show(state, crc_errors); + pad_lat.show(state, crc_errors); + pad_lon.show(state, crc_errors); + pad_alt.show(state, crc_errors); + } + + public AltosPad() { + layout = new GridBagLayout(); + + setLayout(layout); + + /* Elements in pad display: + * + * Battery voltage + * Igniter continuity + * GPS lock status + * GPS ready status + * GPS location + * Pad altitude + * RSSI + */ + battery = new Battery(layout, 0); + apogee = new Apogee(layout, 1); + main = new Main(layout, 2); + gps_locked = new GPSLocked(layout, 3); + gps_ready = new GPSReady(layout, 4); + pad_lat = new PadLat(layout, 5); + pad_lon = new PadLon(layout, 6); + pad_alt = new PadAlt(layout, 7); + } +} diff --git a/altosui/AltosParse.java b/altosui/AltosParse.java new file mode 100644 index 00000000..fbfcaaee --- /dev/null +++ b/altosui/AltosParse.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.text.*; +import java.lang.*; + +public class AltosParse { + static boolean isdigit(char c) { + return '0' <= c && c <= '9'; + } + + static int parse_int(String v) throws ParseException { + try { + return Altos.fromdec(v); + } catch (NumberFormatException e) { + throw new ParseException("error parsing int " + v, 0); + } + } + + static int parse_hex(String v) throws ParseException { + try { + return Altos.fromhex(v); + } catch (NumberFormatException e) { + throw new ParseException("error parsing hex " + v, 0); + } + } + + static double parse_double(String v) throws ParseException { + try { + return Double.parseDouble(v); + } catch (NumberFormatException e) { + throw new ParseException("error parsing double " + v, 0); + } + } + + static double parse_coord(String coord) throws ParseException { + String[] dsf = coord.split("\\D+"); + + if (dsf.length != 3) { + throw new ParseException("error parsing coord " + coord, 0); + } + int deg = parse_int(dsf[0]); + int min = parse_int(dsf[1]); + int frac = parse_int(dsf[2]); + + double r = deg + (min + frac / 10000.0) / 60.0; + if (coord.endsWith("S") || coord.endsWith("W")) + r = -r; + return r; + } + + static String strip_suffix(String v, String suffix) { + if (v.endsWith(suffix)) + return v.substring(0, v.length() - suffix.length()); + return v; + } + + static void word(String v, String m) throws ParseException { + if (!v.equals(m)) { + throw new ParseException("error matching '" + v + "' '" + m + "'", 0); + } + } +} diff --git a/altosui/AltosPreferences.java b/altosui/AltosPreferences.java new file mode 100644 index 00000000..e2a3df3b --- /dev/null +++ b/altosui/AltosPreferences.java @@ -0,0 +1,205 @@ +/* + * 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.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; +import java.awt.Component; +import javax.swing.*; +import javax.swing.filechooser.FileSystemView; + +class AltosPreferences { + static Preferences preferences; + + /* logdir preference name */ + final static String logdirPreference = "LOGDIR"; + + /* channel preference name */ + final static String channelPreferenceFormat = "CHANNEL-%d"; + + /* voice preference name */ + final static String voicePreference = "VOICE"; + + /* callsign preference name */ + final static String callsignPreference = "CALLSIGN"; + + /* firmware directory preference name */ + final static String firmwaredirPreference = "FIRMWARE"; + + /* Default logdir is ~/TeleMetrum */ + final static String logdirName = "TeleMetrum"; + + /* UI Component to pop dialogs up */ + static Component component; + + /* Log directory */ + static File logdir; + + /* Channel (map serial to channel) */ + static Hashtable channels; + + /* Voice preference */ + static boolean voice; + + /* Callsign preference */ + static String callsign; + + /* Firmware directory */ + static File firmwaredir; + + public static void init(Component ui) { + preferences = Preferences.userRoot().node("/org/altusmetrum/altosui"); + + component = ui; + + /* Initialize logdir from preferences */ + String logdir_string = preferences.get(logdirPreference, null); + if (logdir_string != null) + logdir = new File(logdir_string); + else { + /* Use the file system view default directory */ + logdir = new File(FileSystemView.getFileSystemView().getDefaultDirectory(), logdirName); + if (!logdir.exists()) + logdir.mkdirs(); + } + + channels = new Hashtable(); + + voice = preferences.getBoolean(voicePreference, true); + + callsign = preferences.get(callsignPreference,"N0CALL"); + + String firmwaredir_string = preferences.get(firmwaredirPreference, null); + if (firmwaredir_string != null) + firmwaredir = new File(firmwaredir_string); + else + firmwaredir = null; + } + + static void flush_preferences() { + try { + preferences.flush(); + } catch (BackingStoreException ee) { + JOptionPane.showMessageDialog(component, + preferences.absolutePath(), + "Cannot save prefernces", + JOptionPane.ERROR_MESSAGE); + } + } + + public static void set_logdir(File new_logdir) { + logdir = new_logdir; + synchronized (preferences) { + preferences.put(logdirPreference, logdir.getPath()); + flush_preferences(); + } + } + + private static boolean check_dir(File dir) { + if (!dir.exists()) { + if (!dir.mkdirs()) { + JOptionPane.showMessageDialog(component, + dir.getName(), + "Cannot create directory", + JOptionPane.ERROR_MESSAGE); + return false; + } + } else if (!dir.isDirectory()) { + JOptionPane.showMessageDialog(component, + dir.getName(), + "Is not a directory", + JOptionPane.ERROR_MESSAGE); + return false; + } + return true; + } + + /* Configure the log directory. This is where all telemetry and eeprom files + * will be written to, and where replay will look for telemetry files + */ + public static void ConfigureLog() { + JFileChooser logdir_chooser = new JFileChooser(logdir.getParentFile()); + + logdir_chooser.setDialogTitle("Configure Data Logging Directory"); + logdir_chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + + if (logdir_chooser.showDialog(component, "Select Directory") == JFileChooser.APPROVE_OPTION) { + File dir = logdir_chooser.getSelectedFile(); + if (check_dir(dir)) + set_logdir(dir); + } + } + + public static File logdir() { + return logdir; + } + + public static void set_channel(int serial, int new_channel) { + channels.put(serial, new_channel); + synchronized (preferences) { + preferences.putInt(String.format(channelPreferenceFormat, serial), new_channel); + flush_preferences(); + } + } + + public static int channel(int serial) { + if (channels.containsKey(serial)) + return channels.get(serial); + int channel = preferences.getInt(String.format(channelPreferenceFormat, serial), 0); + channels.put(serial, channel); + return channel; + } + + public static void set_voice(boolean new_voice) { + voice = new_voice; + synchronized (preferences) { + preferences.putBoolean(voicePreference, voice); + flush_preferences(); + } + } + + public static boolean voice() { + return voice; + } + + public static void set_callsign(String new_callsign) { + callsign = new_callsign; + synchronized(preferences) { + preferences.put(callsignPreference, callsign); + flush_preferences(); + } + } + + public static String callsign() { + return callsign; + } + + public static void set_firmwaredir(File new_firmwaredir) { + firmwaredir = new_firmwaredir; + synchronized (preferences) { + preferences.put(firmwaredirPreference, firmwaredir.getPath()); + flush_preferences(); + } + } + + public static File firmwaredir() { + return firmwaredir; + } +} diff --git a/altosui/AltosReader.java b/altosui/AltosReader.java new file mode 100644 index 00000000..b9280a0c --- /dev/null +++ b/altosui/AltosReader.java @@ -0,0 +1,28 @@ +/* + * 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.*; +import java.util.*; +import java.text.*; + +public class AltosReader { + public AltosRecord read() throws IOException, ParseException { return null; } + public void close() { } + public void write_comments(PrintStream out) { } +} diff --git a/altosui/AltosRecord.java b/altosui/AltosRecord.java new file mode 100644 index 00000000..1160a273 --- /dev/null +++ b/altosui/AltosRecord.java @@ -0,0 +1,219 @@ +/* + * 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.lang.*; +import java.text.*; +import java.util.HashMap; +import java.io.*; + +public class AltosRecord { + int version; + String callsign; + int serial; + int flight; + int rssi; + int status; + int state; + int tick; + int accel; + int pres; + int temp; + int batt; + int drogue; + int main; + int flight_accel; + int ground_accel; + int flight_vel; + int flight_pres; + int ground_pres; + int accel_plus_g; + int accel_minus_g; + AltosGPS gps; + + double time; /* seconds since boost */ + + /* + * Values for our MP3H6115A pressure sensor + * + * From the data sheet: + * + * Pressure range: 15-115 kPa + * Voltage at 115kPa: 2.82 + * Output scale: 27mV/kPa + * + * + * 27 mV/kPa * 2047 / 3300 counts/mV = 16.75 counts/kPa + * 2.82V * 2047 / 3.3 counts/V = 1749 counts/115 kPa + */ + + static final double counts_per_kPa = 27 * 2047 / 3300; + static final double counts_at_101_3kPa = 1674.0; + + static double + barometer_to_pressure(double count) + { + return ((count / 16.0) / 2047.0 + 0.095) / 0.009 * 1000.0; + } + + public double raw_pressure() { + return barometer_to_pressure(pres); + } + + public double filtered_pressure() { + return barometer_to_pressure(flight_pres); + } + + public double ground_pressure() { + return barometer_to_pressure(ground_pres); + } + + public double filtered_altitude() { + return AltosConvert.pressure_to_altitude(filtered_pressure()); + } + + public double raw_altitude() { + return AltosConvert.pressure_to_altitude(raw_pressure()); + } + + public double ground_altitude() { + return AltosConvert.pressure_to_altitude(ground_pressure()); + } + + public double filtered_height() { + return filtered_altitude() - ground_altitude(); + } + + public double raw_height() { + return raw_altitude() - ground_altitude(); + } + + public double battery_voltage() { + return AltosConvert.cc_battery_to_voltage(batt); + } + + public double main_voltage() { + return AltosConvert.cc_ignitor_to_voltage(main); + } + + public double drogue_voltage() { + return AltosConvert.cc_ignitor_to_voltage(drogue); + } + + /* Value for the CC1111 built-in temperature sensor + * Output voltage at 0°C = 0.755V + * Coefficient = 0.00247V/°C + * Reference voltage = 1.25V + * + * temp = ((value / 32767) * 1.25 - 0.755) / 0.00247 + * = (value - 19791.268) / 32768 * 1.25 / 0.00247 + */ + + static double + thermometer_to_temperature(double thermo) + { + return (thermo - 19791.268) / 32728.0 * 1.25 / 0.00247; + } + + public double temperature() { + return thermometer_to_temperature(temp); + } + + double accel_counts_per_mss() { + double counts_per_g = Math.abs(accel_minus_g - accel_plus_g) / 2; + + return counts_per_g / 9.80665; + } + public double acceleration() { + return (ground_accel - accel) / accel_counts_per_mss(); + } + + public double accel_speed() { + double speed = flight_vel / (accel_counts_per_mss() * 100.0); + return speed; + } + + public String state() { + return Altos.state_name(state); + } + + public static String gets(FileInputStream s) throws IOException { + int c; + String line = ""; + + while ((c = s.read()) != -1) { + if (c == '\r') + continue; + if (c == '\n') { + return line; + } + line = line + (char) c; + } + return null; + } + + public AltosRecord(AltosRecord old) { + version = old.version; + callsign = old.callsign; + serial = old.serial; + flight = old.flight; + rssi = old.rssi; + status = old.status; + state = old.state; + tick = old.tick; + accel = old.accel; + pres = old.pres; + temp = old.temp; + batt = old.batt; + drogue = old.drogue; + main = old.main; + flight_accel = old.flight_accel; + ground_accel = old.ground_accel; + flight_vel = old.flight_vel; + flight_pres = old.flight_pres; + ground_pres = old.ground_pres; + accel_plus_g = old.accel_plus_g; + accel_minus_g = old.accel_minus_g; + gps = new AltosGPS(old.gps); + } + + public AltosRecord() { + version = 0; + callsign = "N0CALL"; + serial = 0; + flight = 0; + rssi = 0; + status = 0; + state = Altos.ao_flight_startup; + tick = 0; + accel = 0; + pres = 0; + temp = 0; + batt = 0; + drogue = 0; + main = 0; + flight_accel = 0; + ground_accel = 0; + flight_vel = 0; + flight_pres = 0; + ground_pres = 0; + accel_plus_g = 0; + accel_minus_g = 0; + gps = new AltosGPS(); + } +} diff --git a/altosui/AltosRecordIterable.java b/altosui/AltosRecordIterable.java new file mode 100644 index 00000000..a7df92d1 --- /dev/null +++ b/altosui/AltosRecordIterable.java @@ -0,0 +1,34 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +public abstract class AltosRecordIterable implements Iterable { + public abstract Iterator iterator(); + public void write_comments(PrintStream out) { } +} diff --git a/altosui/AltosReplayReader.java b/altosui/AltosReplayReader.java new file mode 100644 index 00000000..4e5e1d93 --- /dev/null +++ b/altosui/AltosReplayReader.java @@ -0,0 +1,57 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +/* + * Open an existing telemetry file and replay it in realtime + */ + +public class AltosReplayReader extends AltosFlightReader { + Iterator iterator; + + public AltosRecord read() { + if (iterator.hasNext()) + return iterator.next(); + return null; + } + + public void close (boolean interrupted) { + } + + void update(AltosState state) throws InterruptedException { + /* Make it run in realtime after the rocket leaves the pad */ + if (state.state > Altos.ao_flight_pad) + Thread.sleep((int) (Math.min(state.time_change,10) * 1000)); + } + + public AltosReplayReader(Iterator in_iterator, String in_name) { + iterator = in_iterator; + name = in_name; + } +} diff --git a/altosui/AltosRomconfig.java b/altosui/AltosRomconfig.java new file mode 100644 index 00000000..55056b5e --- /dev/null +++ b/altosui/AltosRomconfig.java @@ -0,0 +1,147 @@ +/* + * 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 AltosRomconfig { + public boolean valid; + public int version; + public int check; + public int serial_number; + public int radio_calibration; + + static int get_int(byte[] bytes, int start, int len) { + int v = 0; + int o = 0; + while (len > 0) { + v = v | ((((int) bytes[start]) & 0xff) << o); + start++; + len--; + o += 8; + } + return v; + } + + static void put_int(int value, byte[] bytes, int start, int len) { + while (len > 0) { + bytes[start] = (byte) (value & 0xff); + start++; + len--; + value >>= 8; + } + } + + static void put_string(String value, byte[] bytes, int start) { + for (int i = 0; i < value.length(); i++) + bytes[start + i] = (byte) value.charAt(i); + } + + static final int AO_USB_DESC_STRING = 3; + + static void put_usb_serial(int value, byte[] bytes, int start) { + int offset = start + 0xa; + int string_num = 0; + + while (offset < bytes.length && bytes[offset] != 0) { + if (bytes[offset + 1] == AO_USB_DESC_STRING) { + ++string_num; + if (string_num == 4) + break; + } + offset += ((int) bytes[offset]) & 0xff; + } + if (offset >= bytes.length || bytes[offset] == 0) + return; + int len = ((((int) bytes[offset]) & 0xff) - 2) / 2; + String fmt = String.format("%%0%dd", len); + + String s = String.format(fmt, value); + if (s.length() != len) { + System.out.printf("weird usb length issue %s isn't %d\n", + s, len); + return; + } + for (int i = 0; i < len; i++) { + bytes[offset + 2 + i*2] = (byte) s.charAt(i); + bytes[offset + 2 + i*2+1] = 0; + } + } + + public AltosRomconfig(byte[] bytes, int offset) { + version = get_int(bytes, offset + 0, 2); + check = get_int(bytes, offset + 2, 2); + if (check == (~version & 0xffff)) { + switch (version) { + case 2: + case 1: + serial_number = get_int(bytes, offset + 4, 2); + radio_calibration = get_int(bytes, offset + 6, 4); + valid = true; + break; + } + } + } + + public AltosRomconfig(AltosHexfile hexfile) { + this(hexfile.data, 0xa0 - hexfile.address); + } + + public void write(byte[] bytes, int offset) throws IOException { + if (!valid) + throw new IOException("rom configuration invalid"); + + if (offset < 0 || bytes.length < offset + 10) + throw new IOException("image cannot contain rom config"); + + AltosRomconfig existing = new AltosRomconfig(bytes, offset); + if (!existing.valid) + throw new IOException("image does not contain existing rom config"); + + switch (existing.version) { + case 2: + put_usb_serial(serial_number, bytes, offset); + case 1: + put_int(serial_number, bytes, offset + 4, 2); + put_int(radio_calibration, bytes, offset + 6, 4); + break; + } + } + + public void write (AltosHexfile hexfile) throws IOException { + write(hexfile.data, 0xa0 - hexfile.address); + AltosRomconfig check = new AltosRomconfig(hexfile); + if (!check.valid()) + throw new IOException("writing new rom config failed\n"); + } + + public AltosRomconfig(int in_serial_number, int in_radio_calibration) { + valid = true; + version = 1; + check = (~version & 0xffff); + serial_number = in_serial_number; + radio_calibration = in_radio_calibration; + } + + public boolean valid() { + return valid && serial_number != 0; + } + + public AltosRomconfig() { + valid = false; + } +} diff --git a/altosui/AltosRomconfigUI.java b/altosui/AltosRomconfigUI.java new file mode 100644 index 00000000..e1dc974e --- /dev/null +++ b/altosui/AltosRomconfigUI.java @@ -0,0 +1,186 @@ +/* + * 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 AltosRomconfigUI + extends JDialog + implements ActionListener +{ + Container pane; + Box box; + JLabel serial_label; + JLabel radio_calibration_label; + + JFrame owner; + JTextField serial_value; + JTextField radio_calibration_value; + + JButton ok; + JButton cancel; + + /* Build the UI using a grid bag */ + public AltosRomconfigUI(JFrame in_owner) { + super (in_owner, "Configure TeleMetrum Rom Values", true); + + owner = in_owner; + GridBagConstraints c; + + Insets il = new Insets(4,4,4,4); + Insets ir = new Insets(4,4,4,4); + + pane = getContentPane(); + pane.setLayout(new GridBagLayout()); + + /* Serial */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 0; + c.gridwidth = 3; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + serial_label = new JLabel("Serial:"); + pane.add(serial_label, c); + + c = new GridBagConstraints(); + c.gridx = 3; c.gridy = 0; + c.gridwidth = 3; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + serial_value = new JTextField("0"); + pane.add(serial_value, c); + + /* Radio calibration value */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 1; + c.gridwidth = 3; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + c.ipady = 5; + radio_calibration_label = new JLabel("Radio Calibration:"); + pane.add(radio_calibration_label, c); + + c = new GridBagConstraints(); + c.gridx = 3; c.gridy = 1; + c.gridwidth = 3; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + c.ipady = 5; + radio_calibration_value = new JTextField("1186611"); + pane.add(radio_calibration_value, c); + + /* Buttons */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 2; + c.gridwidth = 3; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.insets = il; + ok = new JButton("OK"); + pane.add(ok, c); + ok.addActionListener(this); + ok.setActionCommand("ok"); + + c = new GridBagConstraints(); + c.gridx = 3; c.gridy = 2; + c.gridwidth = 3; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.insets = il; + cancel = new JButton("Cancel"); + pane.add(cancel, c); + cancel.addActionListener(this); + cancel.setActionCommand("cancel"); + + pack(); + setLocationRelativeTo(owner); + } + + boolean selected; + + /* Listen for events from our buttons */ + public void actionPerformed(ActionEvent e) { + String cmd = e.getActionCommand(); + + if (cmd.equals("ok")) { + AltosRomconfig romconfig = romconfig(); + if (romconfig == null || !romconfig.valid()) { + JOptionPane.showMessageDialog(this, + "Invalid serial number or radio calibration value", + "Invalid rom configuration", + JOptionPane.ERROR_MESSAGE); + return; + } + selected = true; + } + setVisible(false); + } + + int serial() { + return Integer.parseInt(serial_value.getText()); + } + + void set_serial(int serial) { + serial_value.setText(String.format("%d", serial)); + } + + int radio_calibration() { + return Integer.parseInt(radio_calibration_value.getText()); + } + + void set_radio_calibration(int calibration) { + radio_calibration_value.setText(String.format("%d", calibration)); + } + + public void set(AltosRomconfig config) { + if (config != null && config.valid()) { + set_serial(config.serial_number); + set_radio_calibration(config.radio_calibration); + } + } + + AltosRomconfig romconfig() { + try { + return new AltosRomconfig(serial(), radio_calibration()); + } catch (NumberFormatException ne) { + return null; + } + } + + public AltosRomconfig showDialog() { + setVisible(true); + if (selected) + return romconfig(); + return null; + } +} diff --git a/altosui/AltosSerial.java b/altosui/AltosSerial.java new file mode 100644 index 00000000..b19143e5 --- /dev/null +++ b/altosui/AltosSerial.java @@ -0,0 +1,253 @@ +/* + * 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. + */ + +/* + * Deal with TeleDongle on a serial port + */ + +package altosui; + +import java.lang.*; +import java.io.*; +import java.util.concurrent.*; +import java.util.*; + +import libaltosJNI.*; + +/* + * This class reads from the serial port and places each received + * line in a queue. Dealing with that queue is left up to other + * threads. + */ + +public class AltosSerial implements Runnable { + + static List devices_opened = Collections.synchronizedList(new LinkedList()); + + AltosDevice device; + SWIGTYPE_p_altos_file altos; + LinkedList> monitors; + LinkedBlockingQueue reply_queue; + Thread input_thread; + String line; + byte[] line_bytes; + int line_count; + boolean monitor_mode; + + public void run () { + int c; + + try { + for (;;) { + c = libaltos.altos_getchar(altos, 0); + if (Thread.interrupted()) + break; + if (c == libaltosConstants.LIBALTOS_ERROR) { + for (int e = 0; e < monitors.size(); e++) { + LinkedBlockingQueue q = monitors.get(e); + q.put(new AltosLine()); + } + reply_queue.put (new AltosLine()); + break; + } + if (c == libaltosConstants.LIBALTOS_TIMEOUT) + continue; + if (c == '\r') + continue; + synchronized(this) { + if (c == '\n') { + if (line_count != 0) { + try { + line = new String(line_bytes, 0, line_count, "UTF-8"); + } catch (UnsupportedEncodingException ue) { + line = ""; + for (int i = 0; i < line_count; i++) + line = line + line_bytes[i]; + } + if (line.startsWith("VERSION") || line.startsWith("CRC")) { + for (int e = 0; e < monitors.size(); e++) { + LinkedBlockingQueue q = monitors.get(e); + q.put(new AltosLine (line)); + } + } else { +// System.out.printf("GOT: %s\n", line); + reply_queue.put(new AltosLine (line)); + } + line_count = 0; + line = ""; + } + } else { + if (line_bytes == null) { + line_bytes = new byte[256]; + } else if (line_count == line_bytes.length) { + byte[] new_line_bytes = new byte[line_count * 2]; + System.arraycopy(line_bytes, 0, new_line_bytes, 0, line_count); + line_bytes = new_line_bytes; + } + line_bytes[line_count] = (byte) c; + line_count++; + } + } + } + } catch (InterruptedException e) { + } + } + + public void flush_output() { + if (altos != null) + libaltos.altos_flush(altos); + } + + public void flush_input() { + flush_output(); + boolean got_some; + do { + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + } + got_some = !reply_queue.isEmpty(); + synchronized(this) { + if (!"VERSION".startsWith(line) && + !line.startsWith("VERSION")) + line = ""; + reply_queue.clear(); + } + } while (got_some); + } + + public String get_reply() throws InterruptedException { + flush_output(); + AltosLine line = reply_queue.take(); + 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); + } + + public void remove_monitor(LinkedBlockingQueue q) { + monitors.remove(q); + if (monitors.isEmpty()) + set_monitor(false); + } + + public void close() { + if (altos != null) { + libaltos.altos_close(altos); + } + if (input_thread != null) { + try { + input_thread.interrupt(); + input_thread.join(); + } catch (InterruptedException e) { + } + input_thread = null; + } + if (altos != null) { + libaltos.altos_free(altos); + altos = null; + } + synchronized (devices_opened) { + devices_opened.remove(device.getPath()); + } + } + + public void putc(char c) { + if (altos != null) + libaltos.altos_putchar(altos, c); + } + + public void print(String data) { +// System.out.printf("\"%s\" ", data); + for (int i = 0; i < data.length(); i++) + putc(data.charAt(i)); + } + + public void printf(String format, Object ... arguments) { + print(String.format(format, arguments)); + } + + private void open() throws FileNotFoundException, AltosSerialInUseException { + synchronized (devices_opened) { + if (devices_opened.contains(device.getPath())) + throw new AltosSerialInUseException(device); + devices_opened.add(device.getPath()); + } + altos = libaltos.altos_open(device); + if (altos == null) { + close(); + throw new FileNotFoundException(device.toShortString()); + } + input_thread = new Thread(this); + input_thread.start(); + print("~\nE 0\n"); + set_monitor(false); + flush_output(); + } + + public void set_radio() { + set_channel(AltosPreferences.channel(device.getSerial())); + set_callsign(AltosPreferences.callsign()); + } + + public void set_channel(int channel) { + if (altos != null) { + if (monitor_mode) + printf("m 0\nc r %d\nm 1\n", channel); + else + printf("c r %d\n", channel); + flush_output(); + } + } + + void set_monitor(boolean monitor) { + monitor_mode = monitor; + if (altos != null) { + if (monitor) + printf("m 1\n"); + else + printf("m 0\n"); + flush_output(); + } + } + + public void set_callsign(String callsign) { + if (altos != null) { + printf ("c c %s\n", callsign); + flush_output(); + } + } + + public AltosSerial(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException { + device = in_device; + line = ""; + monitor_mode = false; + monitors = new LinkedList> (); + reply_queue = new LinkedBlockingQueue (); + open(); + } +} diff --git a/altosui/AltosSerialInUseException.java b/altosui/AltosSerialInUseException.java new file mode 100644 index 00000000..4b108c7c --- /dev/null +++ b/altosui/AltosSerialInUseException.java @@ -0,0 +1,28 @@ +/* + * 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 libaltosJNI.*; + +public class AltosSerialInUseException extends Exception { + public altos_device device; + + public AltosSerialInUseException (altos_device in_device) { + device = in_device; + } +} diff --git a/altosui/AltosSerialMonitor.java b/altosui/AltosSerialMonitor.java new file mode 100644 index 00000000..ad0e9295 --- /dev/null +++ b/altosui/AltosSerialMonitor.java @@ -0,0 +1,22 @@ +/* + * 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; + +public interface AltosSerialMonitor { + void data(String data); +} diff --git a/altosui/AltosSiteMap.java b/altosui/AltosSiteMap.java new file mode 100644 index 00000000..80970605 --- /dev/null +++ b/altosui/AltosSiteMap.java @@ -0,0 +1,388 @@ +/* + * Copyright © 2010 Anthony Towns + * + * 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.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.MouseInputAdapter; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.lang.Math; +import java.awt.geom.Point2D; +import java.awt.geom.Line2D; + +public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { + // preferred vertical step in a tile in naut. miles + // will actually choose a step size between x and 2x, where this + // is 1.5x + static final double tile_size_nmi = 0.75; + + static final int px_size = 512; + + static final int MAX_TILE_DELTA = 100; + + private static Point2D.Double translatePoint(Point2D.Double p, + Point2D.Double d) + { + return new Point2D.Double(p.x + d.x, p.y + d.y); + } + + static class LatLng { + public double lat, lng; + public LatLng(double lat, double lng) { + this.lat = lat; + this.lng = lng; + } + } + + // based on google js + // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js + // search for fromLatLngToPoint and fromPointToLatLng + private static Point2D.Double pt(LatLng latlng, int zoom) { + double scale_x = 256/360.0 * Math.pow(2, zoom); + double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + return pt(latlng, scale_x, scale_y); + } + + private static Point2D.Double pt(LatLng latlng, + double scale_x, double scale_y) + { + Point2D.Double res = new Point2D.Double(); + double e; + + res.x = latlng.lng * scale_x; + + e = Math.sin(Math.toRadians(latlng.lat)); + e = Math.max(e,-(1-1.0E-15)); + e = Math.min(e, 1-1.0E-15 ); + + res.y = 0.5*Math.log((1+e)/(1-e))*-scale_y; + return res; + } + + static private LatLng latlng(Point2D.Double pt, + double scale_x, double scale_y) + { + double lat, lng; + double rads; + + lng = pt.x/scale_x; + rads = 2 * Math.atan(Math.exp(-pt.y/scale_y)); + lat = Math.toDegrees(rads - Math.PI/2); + + return new LatLng(lat,lng); + } + + int zoom; + double scale_x, scale_y; + + private Point2D.Double pt(double lat, double lng) { + return pt(new LatLng(lat, lng), scale_x, scale_y); + } + + private LatLng latlng(double x, double y) { + return latlng(new Point2D.Double(x,y), scale_x, scale_y); + } + private LatLng latlng(Point2D.Double pt) { + return latlng(pt, scale_x, scale_y); + } + + HashMap mapTiles = new HashMap(); + Point2D.Double centre; + + private Point2D.Double tileCoordOffset(Point p) { + return new Point2D.Double(centre.x - p.x*px_size, + centre.y - p.y * px_size); + } + + private Point tileOffset(Point2D.Double p) { + return new Point((int)Math.floor((centre.x+p.x)/px_size), + (int)Math.floor((centre.y+p.y)/px_size)); + } + + private Point2D.Double getBaseLocation(double lat, double lng) { + Point2D.Double locn, north_step; + + zoom = 2; + // stupid loop structure to please Java's control flow analysis + do { + zoom++; + scale_x = 256/360.0 * Math.pow(2, zoom); + scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + locn = pt(lat, lng); + north_step = pt(lat+tile_size_nmi*4/3/60.0, lng); + if (locn.y - north_step.y > px_size) + break; + } while (zoom < 22); + locn.x = -px_size * Math.floor(locn.x/px_size); + locn.y = -px_size * Math.floor(locn.y/px_size); + return locn; + } + + public void reset() { + // nothing + } + + private void bgLoadMap(final AltosSiteMapTile tile, + final File pngfile, final String pngurl) + { + //System.out.printf("Loading/fetching map %s\n", pngfile); + Thread thread = new Thread() { + public void run() { + ImageIcon res; + res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl); + if (res != null) { + tile.loadMap(res); + } else { + System.out.printf("# Failed to fetch file %s\n", pngfile); + System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); + } + } + }; + thread.start(); + } + + public static void prefetchMaps(double lat, double lng, int w, int h) { + AltosPreferences.init(null); + + AltosSiteMap asm = new AltosSiteMap(true); + asm.centre = asm.getBaseLocation(lat, lng); + + Point2D.Double p = new Point2D.Double(); + Point2D.Double p2; + int dx = -w/2, dy = -h/2; + for (int y = dy; y < h+dy; y++) { + for (int x = dx; x < w+dx; x++) { + LatLng map_latlng = asm.latlng( + -asm.centre.x + x*px_size + px_size/2, + -asm.centre.y + y*px_size + px_size/2); + File pngfile = asm.MapFile(map_latlng.lat, map_latlng.lng); + String pngurl = asm.MapURL(map_latlng.lat, map_latlng.lng); + if (pngfile.exists()) { + System.out.printf("Already have %s\n", pngfile); + } else if (AltosSiteMapCache.fetchMap(pngfile, pngurl)) { + System.out.printf("Fetched map %s\n", pngfile); + } else { + System.out.printf("# Failed to fetch file %s\n", pngfile); + System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); + } + } + } + } + + private void initMap(AltosSiteMapTile tile, Point offset) { + Point2D.Double coord = tileCoordOffset(offset); + + LatLng map_latlng = latlng(px_size/2-coord.x, px_size/2-coord.y); + + File pngfile = MapFile(map_latlng.lat, map_latlng.lng); + String pngurl = MapURL(map_latlng.lat, map_latlng.lng); + bgLoadMap(tile, pngfile, pngurl); + } + + private void initMaps(double lat, double lng) { + centre = getBaseLocation(lat, lng); + + for (Point k : mapTiles.keySet()) { + initMap(mapTiles.get(k), k); + } + } + + private File MapFile(double lat, double lng) { + char chlat = lat < 0 ? 'S' : 'N'; + char chlng = lng < 0 ? 'E' : 'W'; + if (lat < 0) lat = -lat; + if (lng < 0) lng = -lng; + return new File(AltosPreferences.logdir(), + String.format("map-%c%.6f,%c%.6f-%d.png", + chlat, lat, chlng, lng, zoom)); + } + + private String MapURL(double lat, double lng) { + return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size); + } + + boolean initialised = false; + Point2D.Double last_pt = null; + int last_state = -1; + public void show(final AltosState state, final int crc_errors) { + // if insufficient gps data, nothing to update + if (state.gps == null) + return; + if (state.pad_lat == 0 && state.pad_lon == 0) + return; + if (!state.gps.locked) { + if (state.gps.nsat < 4) + return; + } + + if (!initialised) { + initMaps(state.pad_lat, state.pad_lon); + initialised = true; + } + + final Point2D.Double pt = pt(state.gps.lat, state.gps.lon); + if (last_pt == pt && last_state == state.state) + return; + + if (last_pt == null) { + last_pt = pt; + } + boolean in_any = false; + for (Point offset : mapTiles.keySet()) { + AltosSiteMapTile tile = mapTiles.get(offset); + Point2D.Double ref, lref; + ref = translatePoint(pt, tileCoordOffset(offset)); + lref = translatePoint(last_pt, tileCoordOffset(offset)); + tile.show(state, crc_errors, lref, ref); + if (0 <= ref.x && ref.x < px_size) + if (0 <= ref.y && ref.y < px_size) + in_any = true; + } + + Point offset = tileOffset(pt); + if (!in_any) { + Point2D.Double ref, lref; + ref = translatePoint(pt, tileCoordOffset(offset)); + lref = translatePoint(last_pt, tileCoordOffset(offset)); + + AltosSiteMapTile tile = createTile(offset); + tile.show(state, crc_errors, lref, ref); + initMap(tile, offset); + finishTileLater(tile, offset); + } + + scrollRocketToVisible(pt); + + if (offset != tileOffset(last_pt)) { + ensureTilesAround(offset); + } + + last_pt = pt; + last_state = state.state; + } + + private AltosSiteMapTile createTile(Point offset) { + AltosSiteMapTile tile = new AltosSiteMapTile(px_size); + mapTiles.put(offset, tile); + return tile; + } + private void finishTileLater(final AltosSiteMapTile tile, + final Point offset) + { + SwingUtilities.invokeLater( new Runnable() { + public void run() { + addTileAt(tile, offset); + } + } ); + } + + private void ensureTilesAround(Point base_offset) { + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + Point offset = new Point(base_offset.x + x, base_offset.y + y); + if (mapTiles.containsKey(offset)) + continue; + AltosSiteMapTile tile = createTile(offset); + initMap(tile, offset); + finishTileLater(tile, offset); + } + } + } + + private Point topleft = new Point(0,0); + private void scrollRocketToVisible(Point2D.Double pt) { + Rectangle r = comp.getVisibleRect(); + Point2D.Double copt = translatePoint(pt, tileCoordOffset(topleft)); + int dx = (int)copt.x - r.width/2 - r.x; + int dy = (int)copt.y - r.height/2 - r.y; + if (Math.abs(dx) > r.width/4 || Math.abs(dy) > r.height/4) { + r.x += dx; + r.y += dy; + comp.scrollRectToVisible(r); + } + } + + private void addTileAt(AltosSiteMapTile tile, Point offset) { + if (Math.abs(offset.x) >= MAX_TILE_DELTA || + Math.abs(offset.y) >= MAX_TILE_DELTA) + { + System.out.printf("Rocket too far away from pad (tile %d,%d)\n", + offset.x, offset.y); + return; + } + + boolean review = false; + Rectangle r = comp.getVisibleRect(); + if (offset.x < topleft.x) { + r.x += (topleft.x - offset.x) * px_size; + topleft.x = offset.x; + review = true; + } + if (offset.y < topleft.y) { + r.y += (topleft.y - offset.y) * px_size; + topleft.y = offset.y; + review = true; + } + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + // put some space between the map tiles, debugging only + // c.insets = new Insets(5, 5, 5, 5); + + c.gridx = offset.x + MAX_TILE_DELTA; + c.gridy = offset.y + MAX_TILE_DELTA; + layout.setConstraints(tile, c); + + comp.add(tile); + if (review) { + comp.scrollRectToVisible(r); + } + } + + private AltosSiteMap(boolean knowWhatYouAreDoing) { + if (!knowWhatYouAreDoing) { + throw new RuntimeException("Arggh."); + } + } + + JComponent comp = new JComponent() { }; + private GridBagLayout layout = new GridBagLayout(); + + public AltosSiteMap() { + GrabNDrag scroller = new GrabNDrag(comp); + + comp.setLayout(layout); + + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + Point offset = new Point(x, y); + AltosSiteMapTile t = createTile(offset); + addTileAt(t, offset); + } + } + setViewportView(comp); + setPreferredSize(new Dimension(500,200)); + } +} diff --git a/altosui/AltosSiteMapCache.java b/altosui/AltosSiteMapCache.java new file mode 100644 index 00000000..2e62cc45 --- /dev/null +++ b/altosui/AltosSiteMapCache.java @@ -0,0 +1,103 @@ +/* + * Copyright © 2010 Anthony Towns + * + * 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.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.net.URL; +import java.net.URLConnection; + +public class AltosSiteMapCache extends JLabel { + public static boolean fetchMap(File file, String url) { + URL u; + + try { + u = new URL(url); + } catch (java.net.MalformedURLException e) { + return false; + } + + byte[] data; + try { + URLConnection uc = u.openConnection(); + int contentLength = uc.getContentLength(); + InputStream in = new BufferedInputStream(uc.getInputStream()); + int bytesRead = 0; + int offset = 0; + data = new byte[contentLength]; + while (offset < contentLength) { + bytesRead = in.read(data, offset, data.length - offset); + if (bytesRead == -1) + break; + offset += bytesRead; + } + in.close(); + + if (offset != contentLength) { + return false; + } + } catch (IOException e) { + return false; + } + + try { + FileOutputStream out = new FileOutputStream(file); + out.write(data); + out.flush(); + out.close(); + } catch (FileNotFoundException e) { + return false; + } catch (IOException e) { + if (file.exists()) { + file.delete(); + } + return false; + } + return true; + } + + public static ImageIcon fetchAndLoadMap(File pngfile, String url) { + if (!pngfile.exists()) { + if (!fetchMap(pngfile, url)) { + return null; + } + } + return loadMap(pngfile, url); + } + + public static ImageIcon loadMap(File pngfile, String url) { + if (!pngfile.exists()) { + return null; + } + + try { + return new ImageIcon(ImageIO.read(pngfile)); + } catch (IOException e) { + System.out.printf("# IO error trying to load %s\n", pngfile); + return null; + } + } +} diff --git a/altosui/AltosSiteMapTile.java b/altosui/AltosSiteMapTile.java new file mode 100644 index 00000000..8301f42b --- /dev/null +++ b/altosui/AltosSiteMapTile.java @@ -0,0 +1,112 @@ +/* + * Copyright © 2010 Anthony Towns + * + * 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.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.lang.Math; +import java.awt.geom.Point2D; +import java.awt.geom.Line2D; + +public class AltosSiteMapTile extends JLayeredPane { + JLabel mapLabel; + JLabel draw; + Graphics2D g2d; + + public void loadMap(ImageIcon icn) { + mapLabel.setIcon(icn); + } + + static Color stateColors[] = { + Color.WHITE, // startup + Color.WHITE, // idle + Color.WHITE, // pad + Color.RED, // boost + Color.PINK, // fast + Color.YELLOW, // coast + Color.CYAN, // drogue + Color.BLUE, // main + Color.BLACK // landed + }; + + private boolean drawn_landed_circle = false; + private boolean drawn_boost_circle = false; + public synchronized void show(AltosState state, int crc_errors, + Point2D.Double last_pt, Point2D.Double pt) + { + if (0 <= state.state && state.state < stateColors.length) { + g2d.setColor(stateColors[state.state]); + } + g2d.draw(new Line2D.Double(last_pt, pt)); + + if (state.state == 3 && !drawn_boost_circle) { + drawn_boost_circle = true; + g2d.setColor(Color.RED); + g2d.drawOval((int)last_pt.x-5, (int)last_pt.y-5, 10, 10); + g2d.drawOval((int)last_pt.x-20, (int)last_pt.y-20, 40, 40); + g2d.drawOval((int)last_pt.x-35, (int)last_pt.y-35, 70, 70); + } + if (state.state == 8 && !drawn_landed_circle) { + drawn_landed_circle = true; + g2d.setColor(Color.BLACK); + g2d.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10); + g2d.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40); + g2d.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); + } + + repaint(); + } + + public static Graphics2D fillLabel(JLabel l, Color c, int px_size) { + BufferedImage img = new BufferedImage(px_size, px_size, + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = img.createGraphics(); + g.setColor(c); + g.fillRect(0, 0, px_size, px_size); + l.setIcon(new ImageIcon(img)); + return g; + } + + public AltosSiteMapTile(int px_size) { + setPreferredSize(new Dimension(px_size, px_size)); + + mapLabel = new JLabel(); + fillLabel(mapLabel, Color.GRAY, px_size); + mapLabel.setOpaque(true); + mapLabel.setBounds(0, 0, px_size, px_size); + add(mapLabel, new Integer(0)); + + draw = new JLabel(); + g2d = fillLabel(draw, new Color(127, 127, 127, 0), px_size); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); + draw.setBounds(0, 0, px_size, px_size); + draw.setOpaque(false); + + add(draw, new Integer(1)); + } +} diff --git a/altosui/AltosState.java b/altosui/AltosState.java new file mode 100644 index 00000000..ec499d5a --- /dev/null +++ b/altosui/AltosState.java @@ -0,0 +1,197 @@ +/* + * 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. + */ + +/* + * Track flight state from telemetry or eeprom data stream + */ + +package altosui; + +public class AltosState { + AltosRecord data; + + /* derived data */ + + long report_time; + + double time_change; + int tick; + + int state; + boolean landed; + boolean ascent; /* going up? */ + + double ground_altitude; + double height; + double speed; + double acceleration; + double battery; + double temperature; + double main_sense; + double drogue_sense; + double baro_speed; + + double max_height; + double max_acceleration; + double max_speed; + + AltosGPS gps; + + double pad_lat; + double pad_lon; + double pad_alt; + + static final int MIN_PAD_SAMPLES = 10; + + int npad; + int ngps; + int gps_waiting; + boolean gps_ready; + + AltosGreatCircle from_pad; + double elevation; /* from pad */ + double range; /* total distance */ + + double gps_height; + + int speak_tick; + double speak_altitude; + + + void init (AltosRecord cur, AltosState prev_state) { + int i; + AltosRecord prev; + + data = cur; + + ground_altitude = data.ground_altitude(); + height = data.filtered_altitude() - ground_altitude; + + report_time = System.currentTimeMillis(); + + acceleration = data.acceleration(); + speed = data.accel_speed(); + temperature = data.temperature(); + drogue_sense = data.drogue_voltage(); + main_sense = data.main_voltage(); + battery = data.battery_voltage(); + tick = data.tick; + state = data.state; + + if (prev_state != null) { + + /* Preserve any existing gps data */ + npad = prev_state.npad; + ngps = prev_state.ngps; + gps = prev_state.gps; + pad_lat = prev_state.pad_lat; + pad_lon = prev_state.pad_lon; + pad_alt = prev_state.pad_alt; + max_height = prev_state.max_height; + max_acceleration = prev_state.max_acceleration; + max_speed = prev_state.max_speed; + + /* make sure the clock is monotonic */ + while (tick < prev_state.tick) + tick += 65536; + + time_change = (tick - prev_state.tick) / 100.0; + + /* compute barometric speed */ + + double height_change = height - prev_state.height; + if (time_change > 0) + baro_speed = (prev_state.baro_speed * 3 + (height_change / time_change)) / 4.0; + else + baro_speed = prev_state.baro_speed; + } else { + npad = 0; + ngps = 0; + gps = null; + baro_speed = 0; + time_change = 0; + } + + if (state == Altos.ao_flight_pad) { + + /* Track consecutive 'good' gps reports, waiting for 10 of them */ + if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) + npad++; + else + npad = 0; + + /* Average GPS data while on the pad */ + if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) { + if (ngps > 1) { + /* filter pad position */ + pad_lat = (pad_lat * 31.0 + data.gps.lat) / 32.0; + pad_lon = (pad_lon * 31.0 + data.gps.lon) / 32.0; + pad_alt = (pad_alt * 31.0 + data.gps.alt) / 32.0; + } else { + pad_lat = data.gps.lat; + pad_lon = data.gps.lon; + pad_alt = data.gps.alt; + } + ngps++; + } + } + + gps_waiting = MIN_PAD_SAMPLES - npad; + if (gps_waiting < 0) + gps_waiting = 0; + + gps_ready = gps_waiting == 0; + + ascent = (Altos.ao_flight_boost <= state && + state <= Altos.ao_flight_coast); + + /* Only look at accelerometer data on the way up */ + if (ascent && acceleration > max_acceleration) + max_acceleration = acceleration; + if (ascent && speed > max_speed) + max_speed = speed; + + if (height > max_height) + max_height = height; + if (data.gps != null) { + if (gps == null || !gps.locked || data.gps.locked) + gps = data.gps; + if (ngps > 0 && gps.locked) { + from_pad = new AltosGreatCircle(pad_lat, pad_lon, gps.lat, gps.lon); + } + } + elevation = 0; + range = -1; + if (ngps > 0) { + gps_height = gps.alt - pad_alt; + if (from_pad != null) { + elevation = Math.atan2(height, from_pad.distance) * 180 / Math.PI; + range = Math.sqrt(height * height + from_pad.distance * from_pad.distance); + } + } else { + gps_height = 0; + } + } + + public AltosState(AltosRecord cur) { + init(cur, null); + } + + public AltosState (AltosRecord cur, AltosState prev) { + init(cur, prev); + } +} diff --git a/altosui/AltosTelemetry.java b/altosui/AltosTelemetry.java new file mode 100644 index 00000000..bdb6466a --- /dev/null +++ b/altosui/AltosTelemetry.java @@ -0,0 +1,143 @@ +/* + * 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.lang.*; +import java.text.*; +import java.util.HashMap; + +/* + * Telemetry data contents + */ + + +/* + * The telemetry data stream is a bit of a mess at present, with no consistent + * formatting. In particular, the GPS data is formatted for viewing instead of parsing. + * However, the key feature is that every telemetry line contains all of the information + * necessary to describe the current rocket state, including the calibration values + * for accelerometer and barometer. + * + * GPS unlocked: + * + * VERSION 2 CALL KB0G SERIAL 51 FLIGHT 2 RSSI -68 STATUS ff STATE pad 1001 \ + * a: 16032 p: 21232 t: 20284 v: 25160 d: 204 m: 204 fa: 16038 ga: 16032 fv: 0 \ + * fp: 21232 gp: 21230 a+: 16049 a-: 16304 GPS 0 sat unlocked SAT 1 15 30 + * + * GPS locked: + * + * VERSION 2 CALL KB0G SERIAL 51 FLIGHT 2 RSSI -71 STATUS ff STATE pad 2504 \ + * a: 16028 p: 21220 t: 20360 v: 25004 d: 208 m: 200 fa: 16031 ga: 16032 fv: 330 \ + * fp: 21231 gp: 21230 a+: 16049 a-: 16304 \ + * GPS 9 sat 2010-02-13 17:16:51 35°20.0803'N 106°45.2235'W 1790m \ + * 0.00m/s(H) 0° 0.00m/s(V) 1.0(hdop) 0(herr) 0(verr) \ + * SAT 10 29 30 24 28 5 25 21 20 15 33 1 23 30 24 18 26 10 29 2 26 + */ + +public class AltosTelemetry extends AltosRecord { + public AltosTelemetry(String line) throws ParseException, AltosCRCException { + String[] words = line.split("\\s+"); + int i = 0; + + if (words[i].equals("CRC") && words[i+1].equals("INVALID")) { + i += 2; + AltosParse.word(words[i++], "RSSI"); + rssi = AltosParse.parse_int(words[i++]); + throw new AltosCRCException(rssi); + } + if (words[i].equals("CALL")) { + version = 0; + } else { + AltosParse.word (words[i++], "VERSION"); + version = AltosParse.parse_int(words[i++]); + } + + AltosParse.word (words[i++], "CALL"); + callsign = words[i++]; + + AltosParse.word (words[i++], "SERIAL"); + serial = AltosParse.parse_int(words[i++]); + + if (version >= 2) { + AltosParse.word (words[i++], "FLIGHT"); + flight = AltosParse.parse_int(words[i++]); + } else + flight = 0; + + AltosParse.word(words[i++], "RSSI"); + rssi = AltosParse.parse_int(words[i++]); + + /* Older telemetry data had mis-computed RSSI value */ + if (version <= 2) + rssi = (rssi + 74) / 2 - 74; + + AltosParse.word(words[i++], "STATUS"); + status = AltosParse.parse_hex(words[i++]); + + AltosParse.word(words[i++], "STATE"); + state = Altos.state(words[i++]); + + tick = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "a:"); + accel = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "p:"); + pres = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "t:"); + temp = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "v:"); + batt = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "d:"); + drogue = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "m:"); + main = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "fa:"); + flight_accel = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "ga:"); + ground_accel = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "fv:"); + flight_vel = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "fp:"); + flight_pres = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "gp:"); + ground_pres = AltosParse.parse_int(words[i++]); + + if (version >= 1) { + AltosParse.word(words[i++], "a+:"); + accel_plus_g = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "a-:"); + accel_minus_g = AltosParse.parse_int(words[i++]); + } else { + accel_plus_g = ground_accel; + accel_minus_g = ground_accel + 530; + } + + gps = new AltosGPS(words, i, version); + } +} diff --git a/altosui/AltosTelemetryIterable.java b/altosui/AltosTelemetryIterable.java new file mode 100644 index 00000000..a71ab872 --- /dev/null +++ b/altosui/AltosTelemetryIterable.java @@ -0,0 +1,82 @@ +/* + * 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.*; +import java.util.*; +import java.text.*; + +public class AltosTelemetryIterable extends AltosRecordIterable { + LinkedList records; + + public Iterator iterator () { + return records.iterator(); + } + + public AltosTelemetryIterable (FileInputStream input) { + boolean saw_boost = false; + int current_tick = 0; + int boost_tick = 0; + + records = new LinkedList (); + + try { + for (;;) { + String line = AltosRecord.gets(input); + if (line == null) { + break; + } + try { + AltosTelemetry record = new AltosTelemetry(line); + if (record == null) + break; + if (records.isEmpty()) { + current_tick = record.tick; + } else { + int tick = record.tick | (current_tick & ~ 0xffff); + if (tick < current_tick - 0x1000) + tick += 0x10000; + current_tick = tick; + record.tick = current_tick; + } + if (!saw_boost && record.state >= Altos.ao_flight_boost) + { + saw_boost = true; + boost_tick = record.tick; + } + records.add(record); + } catch (ParseException pe) { + System.out.printf("parse exception %s\n", pe.getMessage()); + } catch (AltosCRCException ce) { + System.out.printf("crc error\n"); + } + } + } catch (IOException io) { + System.out.printf("io exception\n"); + } + + /* adjust all tick counts to be relative to boost time */ + for (AltosRecord r : this) + r.time = (r.tick - boost_tick) / 100.0; + + try { + input.close(); + } catch (IOException ie) { + } + } +} diff --git a/altosui/AltosTelemetryReader.java b/altosui/AltosTelemetryReader.java new file mode 100644 index 00000000..6c5a9397 --- /dev/null +++ b/altosui/AltosTelemetryReader.java @@ -0,0 +1,61 @@ +/* + * 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.lang.*; +import java.text.*; +import java.io.*; +import java.util.concurrent.*; + +class AltosTelemetryReader extends AltosFlightReader { + AltosDevice device; + AltosSerial serial; + AltosLog log; + + LinkedBlockingQueue telem; + + AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { + AltosLine l = telem.take(); + if (l.line == null) + throw new IOException("IO error"); + return new AltosTelemetry(l.line); + } + + void close(boolean interrupted) { + serial.remove_monitor(telem); + log.close(); + serial.close(); + } + + void set_channel(int channel) { + serial.set_channel(channel); + AltosPreferences.set_channel(device.getSerial(), channel); + } + + public AltosTelemetryReader (AltosDevice in_device) + throws FileNotFoundException, AltosSerialInUseException, IOException { + device = in_device; + serial = new AltosSerial(device); + log = new AltosLog(serial); + name = device.toShortString(); + + telem = new LinkedBlockingQueue(); + serial.set_radio(); + serial.add_monitor(telem); + } +} diff --git a/altosui/AltosUI.app/Contents/Info.plist b/altosui/AltosUI.app/Contents/Info.plist new file mode 100644 index 00000000..97b1b59c --- /dev/null +++ b/altosui/AltosUI.app/Contents/Info.plist @@ -0,0 +1,38 @@ + + + + + CFBundleName + altosui + CFBundleVersion + 100.0 + CFBundleAllowMixedLocalizations + true + CFBundleExecutable + JavaApplicationStub + CFBundleDevelopmentRegion + English + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleGetInfoString + AltOS UI version 0.7 + CFBundleInfoDictionaryVersion + 6.0 + CFBundleIconFile + AltosUIIcon.icns + Java + + MainClass + altosui.AltosUI + JVMVersion + 1.5+ + ClassPath + + $JAVAROOT/altosui.jar + $JAVAROOT/freetts.jar + + + + diff --git a/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub b/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub new file mode 100755 index 00000000..c661d3e1 Binary files /dev/null and b/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub differ diff --git a/altosui/AltosUI.app/Contents/PkgInfo b/altosui/AltosUI.app/Contents/PkgInfo new file mode 100644 index 00000000..8a43480f --- /dev/null +++ b/altosui/AltosUI.app/Contents/PkgInfo @@ -0,0 +1 @@ +APPLAM.O diff --git a/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns b/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns new file mode 100644 index 00000000..fe49f362 Binary files /dev/null and b/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns differ diff --git a/altosui/AltosUI.java b/altosui/AltosUI.java new file mode 100644 index 00000000..94c4dd2a --- /dev/null +++ b/altosui/AltosUI.java @@ -0,0 +1,405 @@ +/* + * 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.*; +import java.util.concurrent.LinkedBlockingQueue; + +import libaltosJNI.*; + +public class AltosUI extends JFrame { + public AltosVoice voice = new AltosVoice(); + + public static boolean load_library(Frame frame) { + if (!AltosDevice.load_library()) { + JOptionPane.showMessageDialog(frame, + String.format("No AltOS library in \"%s\"", + System.getProperty("java.library.path","")), + "Cannot load device access library", + JOptionPane.ERROR_MESSAGE); + return false; + } + return true; + } + + void telemetry_window(AltosDevice device) { + try { + AltosFlightReader reader = new AltosTelemetryReader(device); + if (reader != null) + new AltosFlightUI(voice, reader, device.getSerial()); + } catch (FileNotFoundException ee) { + JOptionPane.showMessageDialog(AltosUI.this, + String.format("Cannot open device \"%s\"", + device.toShortString()), + "Cannot open target device", + JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(AltosUI.this, + String.format("Device \"%s\" already in use", + device.toShortString()), + "Device in use", + JOptionPane.ERROR_MESSAGE); + } catch (IOException ee) { + JOptionPane.showMessageDialog(AltosUI.this, + device.toShortString(), + "Unkonwn I/O error", + JOptionPane.ERROR_MESSAGE); + } + } + + Container pane; + GridBagLayout gridbag; + + JButton addButton(int x, int y, String label) { + GridBagConstraints c; + JButton b; + + c = new GridBagConstraints(); + c.gridx = x; c.gridy = y; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + b = new JButton(label); + + Dimension ps = b.getPreferredSize(); + + gridbag.setConstraints(b, c); + add(b, c); + return b; + } + + public AltosUI() { + + load_library(null); + + java.net.URL imgURL = AltosUI.class.getResource("/altus-metrum-16x16.jpg"); + if (imgURL != null) + setIconImage(new ImageIcon(imgURL).getImage()); + + AltosPreferences.init(this); + + pane = getContentPane(); + gridbag = new GridBagLayout(); + pane.setLayout(gridbag); + + JButton b; + + b = addButton(0, 0, "Monitor Flight"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ConnectToDevice(); + } + }); + b = addButton(1, 0, "Save Flight Data"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + SaveFlightData(); + } + }); + b = addButton(2, 0, "Replay Flight"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Replay(); + } + }); + b = addButton(3, 0, "Graph Data"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + GraphData(); + } + }); + b = addButton(4, 0, "Export Data"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ExportData(); + } + }); + b = addButton(0, 1, "Configure TeleMetrum"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ConfigureTeleMetrum(); + } + }); + + b = addButton(1, 1, "Configure AltosUI"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ConfigureAltosUI(); + } + }); + + b = addButton(2, 1, "Flash Image"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + FlashImage(); + } + }); + + 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); + } + }); + + setTitle("AltOS"); + + pane.doLayout(); + pane.validate(); + + doLayout(); + validate(); + + setVisible(true); + + Insets i = getInsets(); + Dimension ps = rootPane.getPreferredSize(); + ps.width += i.left + i.right; + ps.height += i.top + i.bottom; + setPreferredSize(ps); + setSize(ps); + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + } + + private void ConnectToDevice() { + AltosDevice device = AltosDeviceDialog.show(AltosUI.this, + AltosDevice.product_basestation); + + if (device != null) + telemetry_window(device); + } + + void ConfigureCallsign() { + String result; + result = JOptionPane.showInputDialog(AltosUI.this, + "Configure Callsign", + AltosPreferences.callsign()); + if (result != null) + AltosPreferences.set_callsign(result); + } + + void ConfigureTeleMetrum() { + new AltosConfig(AltosUI.this); + } + + void FlashImage() { + new AltosFlashUI(AltosUI.this); + } + + void FireIgniter() { + new AltosIgniteUI(AltosUI.this); + } + + /* + * Replay a flight from telemetry data + */ + private void Replay() { + AltosDataChooser chooser = new AltosDataChooser( + AltosUI.this); + + AltosRecordIterable iterable = chooser.runDialog(); + if (iterable != null) { + AltosFlightReader reader = new AltosReplayReader(iterable.iterator(), + chooser.filename()); + new AltosFlightUI(voice, reader); + } + } + + /* Connect to TeleMetrum, either directly or through + * a TeleDongle over the packet link + */ + private void SaveFlightData() { + new AltosEepromDownload(AltosUI.this); + } + + /* Load a flight log file and write out a CSV file containing + * all of the data in standard units + */ + + private void ExportData() { + 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() { + AltosDataChooser chooser; + chooser = new AltosDataChooser(this); + AltosRecordIterable record_reader = chooser.runDialog(); + if (record_reader == null) + return; + new AltosGraphUI(record_reader); + } + + private void ConfigureAltosUI() { + new AltosConfigureUI(AltosUI.this, voice); + } + + static AltosRecordIterable open_logfile(String filename) { + File file = new File (filename); + try { + FileInputStream in; + + in = new FileInputStream(file); + if (filename.endsWith("eeprom")) + return new AltosEepromIterable(in); + else + return new AltosTelemetryIterable(in); + } catch (FileNotFoundException fe) { + System.out.printf("Cannot open '%s'\n", filename); + return null; + } + } + + static AltosWriter open_csv(String filename) { + File file = new File (filename); + try { + return new AltosCSV(file); + } catch (FileNotFoundException fe) { + System.out.printf("Cannot open '%s'\n", filename); + return null; + } + } + + static AltosWriter open_kml(String filename) { + File file = new File (filename); + try { + return new AltosKML(file); + } catch (FileNotFoundException fe) { + System.out.printf("Cannot open '%s'\n", filename); + return null; + } + } + + static final int process_csv = 1; + static final int process_kml = 2; + + static void process_file(String input, int process) { + AltosRecordIterable iterable = open_logfile(input); + if (iterable == null) + return; + if (process == 0) + process = process_csv; + if ((process & process_csv) != 0) { + String output = Altos.replace_extension(input,".csv"); + System.out.printf("Processing \"%s\" to \"%s\"\n", input, output); + if (input.equals(output)) { + System.out.printf("Not processing '%s'\n", input); + } else { + AltosWriter writer = open_csv(output); + if (writer != null) { + writer.write(iterable); + writer.close(); + } + } + } + if ((process & process_kml) != 0) { + String output = Altos.replace_extension(input,".kml"); + System.out.printf("Processing \"%s\" to \"%s\"\n", input, output); + if (input.equals(output)) { + System.out.printf("Not processing '%s'\n", input); + } else { + AltosWriter writer = open_kml(output); + if (writer == null) + return; + writer.write(iterable); + writer.close(); + } + } + } + + public static void main(final String[] args) { + int process = 0; + /* Handle batch-mode */ + if (args.length == 1 && args[0].equals("--help")) { + System.out.printf("Usage: altosui [OPTION]... [FILE]...\n"); + System.out.printf(" Options:\n"); + System.out.printf(" --fetchmaps \tpre-fetch maps for site map view\n"); + System.out.printf(" --replay \t\trelive the glory of past flights \n"); + System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n"); + System.out.printf(" --kml\tgenerate KML output for use with Google Earth\n"); + } else if (args.length == 3 && args[0].equals("--fetchmaps")) { + double lat = Double.parseDouble(args[1]); + double lon = Double.parseDouble(args[2]); + AltosSiteMap.prefetchMaps(lat, lon, 5, 5); + } else if (args.length == 2 && args[0].equals("--replay")) { + String filename = args[1]; + FileInputStream in; + try { + in = new FileInputStream(filename); + } catch (Exception e) { + System.out.printf("Failed to open file '%s'\n", filename); + return; + } + AltosRecordIterable recs; + AltosReplayReader reader; + if (filename.endsWith("eeprom")) { + recs = new AltosEepromIterable(in); + } else { + recs = new AltosTelemetryIterable(in); + } + reader = new AltosReplayReader(recs.iterator(), filename); + AltosFlightUI flight_ui = new AltosFlightUI(new AltosVoice(), reader); + flight_ui.set_exit_on_close(); + return; + } else if (args.length > 0) { + for (int i = 0; i < args.length; i++) { + if (args[i].equals("--kml")) + process |= process_kml; + else if (args[i].equals("--csv")) + process |= process_csv; + else + process_file(args[i], process); + } + } else { + AltosUI altosui = new AltosUI(); + altosui.setVisible(true); + + AltosDevice[] devices = AltosDevice.list(AltosDevice.product_basestation); + for (int i = 0; i < devices.length; i++) + altosui.telemetry_window(devices[i]); + } + } +} diff --git a/altosui/AltosVoice.java b/altosui/AltosVoice.java new file mode 100644 index 00000000..ac13ee14 --- /dev/null +++ b/altosui/AltosVoice.java @@ -0,0 +1,95 @@ +/* + * 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 com.sun.speech.freetts.Voice; +import com.sun.speech.freetts.VoiceManager; +import com.sun.speech.freetts.audio.JavaClipAudioPlayer; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosVoice implements Runnable { + VoiceManager voice_manager; + Voice voice; + LinkedBlockingQueue phrases; + Thread thread; + boolean busy; + + final static String voice_name = "kevin16"; + + public void run() { + try { + for (;;) { + String s = phrases.take(); + voice.speak(s); + synchronized(this) { + if (phrases.isEmpty()) { + busy = false; + notifyAll(); + } + } + } + } catch (InterruptedException e) { + } + } + + public synchronized void drain() throws InterruptedException { + while (busy) + wait(); + } + + public void speak_always(String s) { + try { + if (voice != null) { + synchronized(this) { + busy = true; + phrases.put(s); + } + } + } catch (InterruptedException e) { + } + } + + public void speak(String s) { + if (AltosPreferences.voice()) + speak_always(s); + } + + public void speak(String format, Object... parameters) { + speak(String.format(format, parameters)); + } + + public AltosVoice () { + busy = false; + voice_manager = VoiceManager.getInstance(); + voice = voice_manager.getVoice(voice_name); + if (voice != null) { + voice.allocate(); + phrases = new LinkedBlockingQueue (); + thread = new Thread(this); + thread.start(); + } else { + System.out.printf("Voice manager failed to open %s\n", voice_name); + Voice[] voices = voice_manager.getVoices(); + System.out.printf("Available voices:\n"); + for (int i = 0; i < voices.length; i++) { + System.out.println(" " + voices[i].getName() + + " (" + voices[i].getDomain() + " domain)"); + } + } + } +} diff --git a/altosui/AltosWriter.java b/altosui/AltosWriter.java new file mode 100644 index 00000000..a172dff0 --- /dev/null +++ b/altosui/AltosWriter.java @@ -0,0 +1,32 @@ +/* + * 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.lang.*; +import java.io.*; +import java.text.*; +import java.util.*; + +public interface AltosWriter { + + public void write(AltosRecord record); + + public void write(AltosRecordIterable iterable); + + public void close(); +} diff --git a/altosui/GrabNDrag.java b/altosui/GrabNDrag.java new file mode 100644 index 00000000..e6b87b58 --- /dev/null +++ b/altosui/GrabNDrag.java @@ -0,0 +1,54 @@ +/* + * Copyright © 2010 Anthony Towns + * + * 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.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.MouseInputAdapter; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; + +class GrabNDrag extends MouseInputAdapter { + private JComponent scroll; + private Point startPt = new Point(); + + public GrabNDrag(JComponent scroll) { + this.scroll = scroll; + scroll.addMouseMotionListener(this); + scroll.addMouseListener(this); + scroll.setAutoscrolls(true); + } + + public void mousePressed(MouseEvent e) { + startPt.setLocation(e.getPoint()); + } + public void mouseDragged(MouseEvent e) { + int xd = e.getX() - startPt.x; + int yd = e.getY() - startPt.y; + + Rectangle r = scroll.getVisibleRect(); + r.x -= xd; + r.y -= yd; + scroll.scrollRectToVisible(r); + } +} diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi b/altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi new file mode 100644 index 00000000..3ed821eb --- /dev/null +++ b/altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi @@ -0,0 +1,84 @@ +# +# InstDrv Example, (c) 2003 Jan Kiszka (Jan Kiszka@web.de) +# + +Name "InstDrv.dll test" + +OutFile "InstDrv-Test.exe" + +ShowInstDetails show + +ComponentText "InstDrv Plugin Usage Example" + +Page components +Page instfiles + +Section "Install a Driver" InstDriver + InstDrv::InitDriverSetup /NOUNLOAD "{4d36e978-e325-11ce-bfc1-08002be10318}" "IrCOMM2k" + Pop $0 + DetailPrint "InitDriverSetup: $0" + + InstDrv::DeleteOemInfFiles /NOUNLOAD + Pop $0 + DetailPrint "DeleteOemInfFiles: $0" + StrCmp $0 "00000000" PrintInfNames ContInst1 + + PrintInfNames: + Pop $0 + DetailPrint "Deleted $0" + Pop $0 + DetailPrint "Deleted $0" + + ContInst1: + InstDrv::CreateDevice /NOUNLOAD + Pop $0 + DetailPrint "CreateDevice: $0" + + SetOutPath $TEMP + File "ircomm2k.inf" + File "ircomm2k.sys" + + InstDrv::InstallDriver /NOUNLOAD "$TEMP\ircomm2k.inf" + Pop $0 + DetailPrint "InstallDriver: $0" + StrCmp $0 "00000000" PrintReboot ContInst2 + + PrintReboot: + Pop $0 + DetailPrint "Reboot: $0" + + ContInst2: + InstDrv::CountDevices + Pop $0 + DetailPrint "CountDevices: $0" +SectionEnd + +Section "Uninstall the driver again" UninstDriver + InstDrv::InitDriverSetup /NOUNLOAD "{4d36e978-e325-11ce-bfc1-08002be10318}" "IrCOMM2k" + Pop $0 + DetailPrint "InitDriverSetup: $0" + + InstDrv::DeleteOemInfFiles /NOUNLOAD + Pop $0 + DetailPrint "DeleteOemInfFiles: $0" + StrCmp $0 "00000000" PrintInfNames ContUninst1 + + PrintInfNames: + Pop $0 + DetailPrint "Deleted $0" + Pop $0 + DetailPrint "Deleted $0" + + ContUninst1: + InstDrv::RemoveAllDevices + Pop $0 + DetailPrint "RemoveAllDevices: $0" + StrCmp $0 "00000000" PrintReboot ContUninst2 + + PrintReboot: + Pop $0 + DetailPrint "Reboot: $0" + + ContUninst2: + Delete "$SYSDIR\system32\ircomm2k.sys" +SectionEnd \ No newline at end of file diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe new file mode 100644 index 00000000..615bae15 Binary files /dev/null and b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe differ diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c new file mode 100644 index 00000000..efe866e9 --- /dev/null +++ b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c @@ -0,0 +1,704 @@ +/* + +InstDrv.dll - Installs or Removes Device Drivers + +Copyright © 2003 Jan Kiszka (Jan.Kiszka@web.de) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute +it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; + you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment in the + product documentation would be appreciated but is not required. +2. Altered versions must be plainly marked as such, + and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any distribution. + +*/ + + +#include +#include +#include +#include "../exdll/exdll.h" + + +char paramBuf[1024]; +GUID devClass; +char hwIdBuf[1024]; +int initialized = 0; + + + +void* memset(void* dst, int val, unsigned int len) +{ + while (len-- > 0) + *((char *)dst)++ = val; + + return NULL; +} + + + +void* memcpy(void* dst, const void* src, unsigned int len) +{ + while (len-- > 0) + *((char *)dst)++ = *((char *)src)++; + + return NULL; +} + + + +int HexCharToInt(char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + else if ((c >= 'a') && (c <= 'f')) + return c - 'a' + 10; + else if ((c >= 'A') && (c <= 'F')) + return c - 'A' + 10; + else + return -1; +} + + + +BOOLEAN HexStringToUInt(char* str, int width, void* valBuf) +{ + int i, val; + + + for (i = width - 4; i >= 0; i -= 4) + { + val = HexCharToInt(*str++); + if (val < 0) + return FALSE; + *(unsigned int *)valBuf += val << i; + } + + return TRUE; +} + + + +BOOLEAN StringToGUID(char* guidStr, GUID* pGuid) +{ + int i; + + + memset(pGuid, 0, sizeof(GUID)); + + if (*guidStr++ != '{') + return FALSE; + + if (!HexStringToUInt(guidStr, 32, &pGuid->Data1)) + return FALSE; + guidStr += 8; + + if (*guidStr++ != '-') + return FALSE; + + if (!HexStringToUInt(guidStr, 16, &pGuid->Data2)) + return FALSE; + guidStr += 4; + + if (*guidStr++ != '-') + return FALSE; + + if (!HexStringToUInt(guidStr, 16, &pGuid->Data3)) + return FALSE; + guidStr += 4; + + if (*guidStr++ != '-') + return FALSE; + + for (i = 0; i < 2; i++) + { + if (!HexStringToUInt(guidStr, 8, &pGuid->Data4[i])) + return FALSE; + guidStr += 2; + } + + if (*guidStr++ != '-') + return FALSE; + + for (i = 2; i < 8; i++) + { + if (!HexStringToUInt(guidStr, 8, &pGuid->Data4[i])) + return FALSE; + guidStr += 2; + } + + if (*guidStr++ != '}') + return FALSE; + + return TRUE; +} + + + +DWORD FindNextDevice(HDEVINFO devInfoSet, SP_DEVINFO_DATA* pDevInfoData, DWORD* pIndex) +{ + DWORD buffersize = 0; + LPTSTR buffer = NULL; + DWORD dataType; + DWORD result; + + + while (1) + { + if (!SetupDiEnumDeviceInfo(devInfoSet, (*pIndex)++, pDevInfoData)) + { + result = GetLastError(); + break; + } + + GetDeviceRegistryProperty: + if (!SetupDiGetDeviceRegistryProperty(devInfoSet, pDevInfoData, SPDRP_HARDWAREID, + &dataType, (PBYTE)buffer, buffersize, + &buffersize)) + { + result = GetLastError(); + + if (result == ERROR_INSUFFICIENT_BUFFER) + { + if (buffer != NULL) + LocalFree(buffer); + + buffer = (LPTSTR)LocalAlloc(LPTR, buffersize); + + if (buffer == NULL) + break; + + goto GetDeviceRegistryProperty; + } + else if (result == ERROR_INVALID_DATA) + continue; // ignore invalid entries + else + break; // break on other errors + } + + if (lstrcmpi(buffer, hwIdBuf) == 0) + { + result = 0; + break; + } + } + + if (buffer != NULL) + LocalFree(buffer); + + return result; +} + + + +DWORD FindFirstDevice(HWND hwndParent, const GUID* pDevClass, const LPTSTR hwId, + HDEVINFO* pDevInfoSet, SP_DEVINFO_DATA* pDevInfoData, + DWORD *pIndex, DWORD flags) +{ + DWORD result; + + + *pDevInfoSet = SetupDiGetClassDevs((GUID*)pDevClass, NULL, hwndParent, flags); + if (*pDevInfoSet == INVALID_HANDLE_VALUE) + return GetLastError(); + + pDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA); + *pIndex = 0; + + result = FindNextDevice(*pDevInfoSet, pDevInfoData, pIndex); + + if (result != 0) + SetupDiDestroyDeviceInfoList(*pDevInfoSet); + + return result; +} + + + +/* + * InstDrv::InitDriverSetup devClass drvHWID + * + * devClass - GUID of the driver's device setup class + * drvHWID - Hardware ID of the supported device + * + * Return: + * result - error message, empty on success + */ +void __declspec(dllexport) InitDriverSetup(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) +{ + EXDLL_INIT(); + + /* convert class GUID */ + popstring(paramBuf); + + if (!StringToGUID(paramBuf, &devClass)) + { + popstring(paramBuf); + pushstring("Invalid GUID!"); + return; + } + + /* get hardware ID */ + memset(hwIdBuf, 0, sizeof(hwIdBuf)); + popstring(hwIdBuf); + + initialized = 1; + pushstring(""); +} + + + +/* + * InstDrv::CountDevices + * + * Return: + * result - Number of installed devices the driver supports + */ +void __declspec(dllexport) CountDevices(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) +{ + HDEVINFO devInfoSet; + SP_DEVINFO_DATA devInfoData; + int count = 0; + char countBuf[16]; + DWORD index; + DWORD result; + + + EXDLL_INIT(); + + if (!initialized) + { + pushstring("Fatal error!"); + return; + } + + result = FindFirstDevice(hwndParent, &devClass, hwIdBuf, &devInfoSet, &devInfoData, + &index, DIGCF_PRESENT); + if (result != 0) + { + pushstring("0"); + return; + } + + do + { + count++; + } while (FindNextDevice(devInfoSet, &devInfoData, &index) == 0); + + SetupDiDestroyDeviceInfoList(devInfoSet); + + wsprintf(countBuf, "%d", count); + pushstring(countBuf); +} + + + +/* + * InstDrv::CreateDevice + * + * Return: + * result - Windows error code + */ +void __declspec(dllexport) CreateDevice(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) +{ + HDEVINFO devInfoSet; + SP_DEVINFO_DATA devInfoData; + DWORD result = 0; + char resultBuf[16]; + + + EXDLL_INIT(); + + if (!initialized) + { + pushstring("Fatal error!"); + return; + } + + devInfoSet = SetupDiCreateDeviceInfoList(&devClass, hwndParent); + if (devInfoSet == INVALID_HANDLE_VALUE) + { + wsprintf(resultBuf, "%08X", GetLastError()); + pushstring(resultBuf); + return; + } + + devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + if (!SetupDiCreateDeviceInfo(devInfoSet, hwIdBuf, &devClass, NULL, + hwndParent, DICD_GENERATE_ID, &devInfoData)) + { + result = GetLastError(); + goto InstallCleanup; + } + + if (!SetupDiSetDeviceRegistryProperty(devInfoSet, &devInfoData, SPDRP_HARDWAREID, + hwIdBuf, (lstrlen(hwIdBuf)+2)*sizeof(TCHAR))) + { + result = GetLastError(); + goto InstallCleanup; + } + + if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, devInfoSet, &devInfoData)) + result = GetLastError(); + + InstallCleanup: + SetupDiDestroyDeviceInfoList(devInfoSet); + + wsprintf(resultBuf, "%08X", result); + pushstring(resultBuf); +} + + + +/* + * InstDrv::InstallDriver infPath + * + * Return: + * result - Windows error code + * reboot - non-zero if reboot is required + */ +void __declspec(dllexport) InstallDriver(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) +{ + char resultBuf[16]; + BOOL reboot; + + + EXDLL_INIT(); + popstring(paramBuf); + + if (!initialized) + { + pushstring("Fatal error!"); + return; + } + + if (!UpdateDriverForPlugAndPlayDevices(hwndParent, hwIdBuf, paramBuf, + INSTALLFLAG_FORCE, &reboot)) + { + wsprintf(resultBuf, "%08X", GetLastError()); + pushstring(resultBuf); + } + else + { + wsprintf(resultBuf, "%d", reboot); + pushstring(resultBuf); + pushstring("00000000"); + } +} + + + +/* + * InstDrv::DeleteOemInfFiles + * + * Return: + * result - Windows error code + * oeminf - Path of the deleted devices setup file (oemXX.inf) + * oempnf - Path of the deleted devices setup file (oemXX.pnf) + */ +void __declspec(dllexport) DeleteOemInfFiles(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) +{ + HDEVINFO devInfo; + SP_DEVINFO_DATA devInfoData; + SP_DRVINFO_DATA drvInfoData; + SP_DRVINFO_DETAIL_DATA drvInfoDetail; + DWORD index; + DWORD result; + char resultBuf[16]; + + + if (!initialized) + { + pushstring("Fatal error!"); + return; + } + + result = FindFirstDevice(NULL, &devClass, hwIdBuf, &devInfo, &devInfoData, &index, 0); + if (result != 0) + goto Cleanup1; + + if (!SetupDiBuildDriverInfoList(devInfo, &devInfoData, SPDIT_COMPATDRIVER)) + { + result = GetLastError(); + goto Cleanup2; + } + + drvInfoData.cbSize = sizeof(SP_DRVINFO_DATA); + drvInfoDetail.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA); + + if (!SetupDiEnumDriverInfo(devInfo, &devInfoData, SPDIT_COMPATDRIVER, 0, &drvInfoData)) + { + result = GetLastError(); + goto Cleanup3; + } + + if (!SetupDiGetDriverInfoDetail(devInfo, &devInfoData, &drvInfoData, + &drvInfoDetail, sizeof(drvInfoDetail), NULL)) + { + result = GetLastError(); + + if (result != ERROR_INSUFFICIENT_BUFFER) + goto Cleanup3; + + result = 0; + } + + pushstring(drvInfoDetail.InfFileName); + if (!DeleteFile(drvInfoDetail.InfFileName)) + result = GetLastError(); + else + { + index = lstrlen(drvInfoDetail.InfFileName); + if (index > 3) + { + lstrcpy(drvInfoDetail.InfFileName+index-3, "pnf"); + pushstring(drvInfoDetail.InfFileName); + if (!DeleteFile(drvInfoDetail.InfFileName)) + result = GetLastError(); + } + } + + Cleanup3: + SetupDiDestroyDriverInfoList(devInfo, &devInfoData, SPDIT_COMPATDRIVER); + + Cleanup2: + SetupDiDestroyDeviceInfoList(devInfo); + + Cleanup1: + wsprintf(resultBuf, "%08X", result); + pushstring(resultBuf); +} + + + +/* + * InstDrv::RemoveAllDevices + * + * Return: + * result - Windows error code + * reboot - non-zero if reboot is required + */ +void __declspec(dllexport) RemoveAllDevices(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) +{ + HDEVINFO devInfo; + SP_DEVINFO_DATA devInfoData; + DWORD index; + DWORD result; + char resultBuf[16]; + BOOL reboot = FALSE; + SP_DEVINSTALL_PARAMS instParams; + + + EXDLL_INIT(); + + if (!initialized) + { + pushstring("Fatal error!"); + return; + } + + result = FindFirstDevice(NULL, &devClass, hwIdBuf, &devInfo, &devInfoData, &index, 0); + if (result != 0) + goto Cleanup1; + + do + { + if (!SetupDiCallClassInstaller(DIF_REMOVE, devInfo, &devInfoData)) + { + result = GetLastError(); + break; + } + + instParams.cbSize = sizeof(instParams); + if (!reboot && + SetupDiGetDeviceInstallParams(devInfo, &devInfoData, &instParams) && + ((instParams.Flags & (DI_NEEDRESTART|DI_NEEDREBOOT)) != 0)) + { + reboot = TRUE; + } + + result = FindNextDevice(devInfo, &devInfoData, &index); + } while (result == 0); + + SetupDiDestroyDeviceInfoList(devInfo); + + Cleanup1: + if ((result == 0) || (result == ERROR_NO_MORE_ITEMS)) + { + wsprintf(resultBuf, "%d", reboot); + pushstring(resultBuf); + pushstring("00000000"); + } + else + { + wsprintf(resultBuf, "%08X", result); + pushstring(resultBuf); + } +} + + + +/* + * InstDrv::StartSystemService serviceName + * + * Return: + * result - Windows error code + */ +void __declspec(dllexport) StartSystemService(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) +{ + SC_HANDLE managerHndl; + SC_HANDLE svcHndl; + SERVICE_STATUS svcStatus; + DWORD oldCheckPoint; + DWORD result; + char resultBuf[16]; + + + EXDLL_INIT(); + popstring(paramBuf); + + managerHndl = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (managerHndl == NULL) + { + result = GetLastError(); + goto Cleanup1; + } + + svcHndl = OpenService(managerHndl, paramBuf, SERVICE_START | SERVICE_QUERY_STATUS); + if (svcHndl == NULL) + { + result = GetLastError(); + goto Cleanup2; + } + + if (!StartService(svcHndl, 0, NULL) || !QueryServiceStatus(svcHndl, &svcStatus)) + { + result = GetLastError(); + goto Cleanup3; + } + + while (svcStatus.dwCurrentState == SERVICE_START_PENDING) + { + oldCheckPoint = svcStatus.dwCheckPoint; + + Sleep(svcStatus.dwWaitHint); + + if (!QueryServiceStatus(svcHndl, &svcStatus)) + { + result = GetLastError(); + break; + } + + if (oldCheckPoint >= svcStatus.dwCheckPoint) + { + if ((svcStatus.dwCurrentState == SERVICE_STOPPED) && + (svcStatus.dwWin32ExitCode != 0)) + result = svcStatus.dwWin32ExitCode; + else + result = ERROR_SERVICE_REQUEST_TIMEOUT; + } + } + + if (svcStatus.dwCurrentState == SERVICE_RUNNING) + result = 0; + + Cleanup3: + CloseServiceHandle(svcHndl); + + Cleanup2: + CloseServiceHandle(managerHndl); + + Cleanup1: + wsprintf(resultBuf, "%08X", result); + pushstring(resultBuf); +} + + + +/* + * InstDrv::StopSystemService serviceName + * + * Return: + * result - Windows error code + */ +void __declspec(dllexport) StopSystemService(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) +{ + SC_HANDLE managerHndl; + SC_HANDLE svcHndl; + SERVICE_STATUS svcStatus; + DWORD oldCheckPoint; + DWORD result; + char resultBuf[16]; + + + EXDLL_INIT(); + popstring(paramBuf); + + managerHndl = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (managerHndl == NULL) + { + result = GetLastError(); + goto Cleanup1; + } + + svcHndl = OpenService(managerHndl, paramBuf, SERVICE_STOP | SERVICE_QUERY_STATUS); + if (svcHndl == NULL) + { + result = GetLastError(); + goto Cleanup2; + } + + if (!ControlService(svcHndl, SERVICE_CONTROL_STOP, &svcStatus)) + { + result = GetLastError(); + goto Cleanup3; + } + + while (svcStatus.dwCurrentState == SERVICE_STOP_PENDING) + { + oldCheckPoint = svcStatus.dwCheckPoint; + + Sleep(svcStatus.dwWaitHint); + + if (!QueryServiceStatus(svcHndl, &svcStatus)) + { + result = GetLastError(); + break; + } + + if (oldCheckPoint >= svcStatus.dwCheckPoint) + { + result = ERROR_SERVICE_REQUEST_TIMEOUT; + break; + } + } + + if (svcStatus.dwCurrentState == SERVICE_STOPPED) + result = 0; + + Cleanup3: + CloseServiceHandle(svcHndl); + + Cleanup2: + CloseServiceHandle(managerHndl); + + Cleanup1: + wsprintf(resultBuf, "%08X", result); + pushstring(resultBuf); +} + + + +BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) +{ + return TRUE; +} diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp new file mode 100644 index 00000000..874e66c7 --- /dev/null +++ b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp @@ -0,0 +1,110 @@ +# Microsoft Developer Studio Project File - Name="InstDrv" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** NICHT BEARBEITEN ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=InstDrv - Win32 Debug +!MESSAGE Dies ist kein gültiges Makefile. Zum Erstellen dieses Projekts mit NMAKE +!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und führen Sie den Befehl +!MESSAGE +!MESSAGE NMAKE /f "InstDrv.mak". +!MESSAGE +!MESSAGE Sie können beim Ausführen von NMAKE eine Konfiguration angeben +!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel: +!MESSAGE +!MESSAGE NMAKE /f "InstDrv.mak" CFG="InstDrv - Win32 Debug" +!MESSAGE +!MESSAGE Für die Konfiguration stehen zur Auswahl: +!MESSAGE +!MESSAGE "InstDrv - Win32 Release" (basierend auf "Win32 (x86) Dynamic-Link Library") +!MESSAGE "InstDrv - Win32 Debug" (basierend auf "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "InstDrv - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INSTDRV_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O1 /I "C:\Programme\WINDDK\3790\inc\ddk\w2k" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INSTDRV_EXPORTS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x407 /d "NDEBUG" +# ADD RSC /l 0x407 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib setupapi.lib newdev.lib /nologo /entry:"_DllMainCRTStartup" /dll /machine:I386 /nodefaultlib /out:"../../Plugins/InstDrv.dll" /libpath:"C:\Programme\WINDDK\3790\lib\w2k\i386" /opt:nowin98 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "InstDrv - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INSTDRV_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INSTDRV_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x407 /d "_DEBUG" +# ADD RSC /l 0x407 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"_DllMainCRTStartup" /dll /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /nodefaultlib + +!ENDIF + +# Begin Target + +# Name "InstDrv - Win32 Release" +# Name "InstDrv - Win32 Debug" +# Begin Group "Quellcodedateien" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\InstDrv.c +# End Source File +# End Group +# Begin Group "Header-Dateien" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Ressourcendateien" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw new file mode 100644 index 00000000..b3d02f0e --- /dev/null +++ b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELÖSCHT WERDEN! + +############################################################################### + +Project: "InstDrv"=.\InstDrv.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt b/altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt new file mode 100644 index 00000000..e5877aa6 --- /dev/null +++ b/altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt @@ -0,0 +1,141 @@ +InstDrv.dll version 0.2 - Installs or Removes Device Drivers +------------------------------------------------------------ + + +The plugin helps you to create NSIS scripts for installing device drivers or +removing them again. It can count installed device instances, create new ones +or delete all supported device. InstDrv works on Windows 2000 or later. + + + +InstDrv::InitDriverSetup devClass drvHWID +Return: result + +To start processing a driver, first call this function. devClass is the GUID +of the device class the driver supports, drvHWID is the device hardware ID. If +you don't know what these terms mean, you may want to take a look at the +Windows DDK. This function returns an empty string on success, otherwise an +error message. + +InitDriverSetup has to be called every time after the plugin dll has been +(re-)loaded, or if you want to switch to a different driver. + + + +InstDrv::CountDevices +Return: number + +This call returns the number of installed and supported devices of the driver. + + + +InstDrv::CreateDevice +Return: result + +To create a new deviced node which the driver has to support, use this +function. You may even call it multiple times for more than one instance. The +return value is the Windows error code (in hex). Use CreateDevice before +installing or updating the driver itself. + + + +InstDrv::InstallDriver infPath +Return: result + reboot + +InstallDriver installs or updates a device driver as specified in the .inf +setup script. It returns a Windows error code (in hex) and, on success, a flag +signalling if a system reboot is required. + + + +InstDrv::DeleteOemInfFiles +Return: result + oeminf + oempnf + +DeleteOemInfFiles tries to clean up the Windows inf directory by deleting the +oemXX.inf and oemXX.pnf files associated with the drivers. It returns a +Windows error code (in hex) and, on success, the names of the deleted files. +This functions requires that at least one device instance is still present. +So, call it before you remove the devices itself. You should also call it +before updating a driver. This avoids that the inf directory gets slowly +messed up with useless old setup scripts (which does NOT really accelerate +Windows). The error code which comes up when no device is installed is +"00000103". + + + +InstDrv::RemoveAllDevices +Return: result + reboot + +This functions deletes all devices instances the driver supported. It returns +a Windows error code (in hex) and, on success, a flag signalling if the system +needs to be rebooted. You additionally have to remove the driver binaries from +the system paths. + + + +InstDrv::StartSystemService serviceName +Return: result + +Call this function to start the provided system service. The function blocks +until the service is started or the system reported a timeout. The return value +is the Windows error code (in hex). + + + +InstDrv::StopSystemService serviceName +Return: result + +This function tries to stop the provided system service. It blocks until the +service has been shut down or the system reported a timeout. The return value +is the Windows error code (in hex). + + + +Example.nsi + +The example script installs or removes the virtual COM port driver of IrCOMM2k +(2.0.0-alpha8, see www.ircomm2k.de/english). The driver and its setup script +are only included for demonstration purposes, they do not work without the +rest of IrCOMM2k (but they also do not cause any harm). + + + +Building the Source Code + +To build the plugin from the source code, some include files and libraries +which come with the Windows DDK are required. + + + +History + + 0.2 - fixed bug when calling InitDriverSetup the second time + - added StartSystemService and StopSystemService + + 0.1 - first release + + + +License + +Copyright © 2003 Jan Kiszka (Jan.Kiszka@web.de) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute +it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; + you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment in the + product documentation would be appreciated but is not required. +2. Altered versions must be plainly marked as such, + and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any distribution. diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf b/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf new file mode 100644 index 00000000..ccda1d87 --- /dev/null +++ b/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf @@ -0,0 +1,137 @@ +; IrCOMM2k.inf +; +; Installation file for the Virtual Infrared-COM-Port +; +; (c) Copyright 2001, 2002 Jan Kiszka +; + +[Version] +Signature="$Windows NT$" +Provider=%JK% +Class=Ports +ClassGUID={4d36e978-e325-11ce-bfc1-08002be10318} +;DriverVer=03/26/2002,1.2.1.0 + +[DestinationDirs] +IrCOMM2k.Copy2Drivers = 12 +IrCOMM2k.Copy2Winnt = 10 +IrCOMM2k.Copy2System32 = 11 +IrCOMM2k.Copy2Help = 18 + + +; +; Driver information +; + +[Manufacturer] +%JK% = JK.Mfg + +[JK.Mfg] +%JK.DeviceDescIrCOMM% = IrCOMM2k_inst,IrCOMM2k + + +; +; General installation section +; + +[IrCOMM2k_inst] +CopyFiles = IrCOMM2k.Copy2Drivers ;,IrCOMM2k.Copy2System32,IrCOMM2k.Copy2Help,IrCOMM2k.Copy2Winnt +;AddReg = IrCOMM2k_inst_AddReg + + +; +; File sections +; + +[IrCOMM2k.Copy2Drivers] +ircomm2k.sys,,,2 + +;[IrCOMM2k.Copy2System32] +;ircomm2k.exe,,,2 +;ircomm2k.dll,,,2 + +;[IrCOMM2k.Copy2Help] +;ircomm2k.hlp,,,2 + +;[IrCOMM2k.Copy2Winnt] +;IrCOMM2k-Setup.exe,Setup.exe,,2 + + +; +; Service Installation +; + +[IrCOMM2k_inst.Services] +AddService = IrCOMM2k,0x00000002,IrCOMM2k_DriverService_Inst,IrCOMM2k_DriverEventLog_Inst +;AddService = IrCOMM2kSvc,,IrCOMM2k_Service_Inst + +[IrCOMM2k_DriverService_Inst] +DisplayName = %IrCOMM2k.DrvName% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 0 ; SERVICE_ERROR_IGNORE +ServiceBinary = %12%\ircomm2k.sys + +;[IrCOMM2k_Service_Inst] +;DisplayName = %IrCOMM2k.SvcName% +;Description = %IrCOMM2k.SvcDesc% +;ServiceType = 0x00000120 ; SERVICE_WIN32_SHARE_PROCESS, SERVICE_INTERACTIVE_PROCESS +;StartType = 2 ; SERVICE_AUTO_START +;ErrorControl = 0 ; SERVICE_ERROR_IGNORE +;ServiceBinary = %11%\ircomm2k.exe +;Dependencies = IrCOMM2k +;AddReg = IrCOMM2kSvcAddReg + + +[IrCOMM2k_inst.nt.HW] +AddReg=IrCOMM2kHwAddReg + +[IrCOMM2kHwAddReg] +HKR,,PortSubClass,REG_BINARY,0x00000001 +;HKR,,TimeoutScaling,REG_DWORD,0x00000001 +;HKR,,StatusLines,REG_DWORD,0x00000000 + +;[IrCOMM2k_inst_AddReg] +;HKR,,EnumPropPages32,,"ircomm2k.dll,IrCOMM2kPropPageProvider" +;HKLM,%UNINSTALL_KEY%,DisplayIcon,0x00020000,"%windir%\IrCOMM2k-Setup.exe" +;HKLM,%UNINSTALL_KEY%,DisplayName,,"IrCOMM2k 1.2.1 " +;HKLM,%UNINSTALL_KEY%,DisplayVersion,,"1.2.1" +;HKLM,%UNINSTALL_KEY%,HelpLink,,"http://www.ircomm2k.de" +;HKLM,%UNINSTALL_KEY%,Publisher,,%JK% +;HKLM,%UNINSTALL_KEY%,UninstallString,0x00020000,"%windir%\IrCOMM2k-Setup.exe" + +;[IrCOMM2kSvcAddReg] +;HKR,Parameters,ActiveConnectOnly,REG_DWORD,0x00000000 + + +[IrCOMM2k_DriverEventLog_Inst] +AddReg = IrCOMM2k_DriverEventLog_AddReg + +[IrCOMM2k_DriverEventLog_AddReg] +HKR,,EventMessageFile,REG_EXPAND_SZ,"%SystemRoot%\System32\IoLogMsg.dll;%SystemRoot%\System32\drivers\ircomm2k.sys" +HKR,,TypesSupported,REG_DWORD,7 + + +[Strings] + +; +; Non-Localizable Strings +; + +REG_SZ = 0x00000000 +REG_MULTI_SZ = 0x00010000 +REG_EXPAND_SZ = 0x00020000 +REG_BINARY = 0x00000001 +REG_DWORD = 0x00010001 +SERVICEROOT = "System\CurrentControlSet\Services" +UNINSTALL_KEY = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\IrCOMM2k" + +; +; Localizable Strings +; + +JK = "Jan Kiszka" +JK.DeviceDescIrCOMM = "Virtueller Infrarot-Kommunikationsanschluss" +IrCOMM2k.DrvName = "Virtueller Infrarot-Kommunikationsanschluss" +;IrCOMM2k.SvcName = "Virtueller Infrarot-Kommunikationsanschluß, Dienstprogramm" +;IrCOMM2k.SvcDesc = "Bildet über Infarot einen Kommunikationsanschluß nach." diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys b/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys new file mode 100644 index 00000000..7882583b Binary files /dev/null and b/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys differ diff --git a/altosui/Instdrv/NSIS/Plugins/InstDrv.dll b/altosui/Instdrv/NSIS/Plugins/InstDrv.dll new file mode 100644 index 00000000..482e955e Binary files /dev/null and b/altosui/Instdrv/NSIS/Plugins/InstDrv.dll differ diff --git a/altosui/Makefile-standalone b/altosui/Makefile-standalone new file mode 100644 index 00000000..a95a5aa8 --- /dev/null +++ b/altosui/Makefile-standalone @@ -0,0 +1,184 @@ +.SUFFIXES: .java .class + +CLASSPATH=classes:./*:/usr/share/java/* +CLASSFILES=\ + Altos.class \ + AltosChannelMenu.class \ + AltosConfig.class \ + AltosConfigUI.class \ + AltosConvert.class \ + AltosCRCException.class \ + AltosCSV.class \ + AltosCSVUI.class \ + AltosDebug.class \ + AltosEepromDownload.class \ + AltosEepromMonitor.class \ + AltosEepromReader.class \ + AltosEepromRecord.class \ + AltosFile.class \ + AltosFlash.class \ + AltosFlashUI.class \ + AltosFlightInfoTableModel.class \ + AltosFlightStatusTableModel.class \ + AltosGPS.class \ + AltosGreatCircle.class \ + AltosHexfile.class \ + AltosLine.class \ + AltosInfoTable.class \ + AltosLog.class \ + AltosLogfileChooser.class \ + AltosParse.class \ + AltosPreferences.class \ + AltosReader.class \ + AltosRecord.class \ + AltosSerialMonitor.class \ + AltosSerial.class \ + AltosState.class \ + AltosStatusTable.class \ + AltosTelemetry.class \ + AltosTelemetryReader.class \ + AltosUI.class \ + AltosDevice.class \ + AltosDeviceDialog.class \ + AltosRomconfig.class \ + AltosRomconfigUI.class \ + AltosVoice.class + +JAVA_ICON=../../icon/altus-metrum-16x16.jpg +WINDOWS_ICON=../../icon/altus-metrum.ico + +# where altosui.jar gets installed +ALTOSLIB=/usr/share/java + +# where freetts.jar is to be found +FREETTSLIB=/usr/share/java + +# all of the freetts files +FREETTSJAR= \ + $(FREETTSLIB)/cmudict04.jar \ + $(FREETTSLIB)/cmulex.jar \ + $(FREETTSLIB)/cmu_time_awb.jar \ + $(FREETTSLIB)/cmutimelex.jar \ + $(FREETTSLIB)/cmu_us_kal.jar \ + $(FREETTSLIB)/en_us.jar \ + $(FREETTSLIB)/freetts.jar + +# The current hex files +HEXLIB=../../src +HEXFILES = \ + $(HEXLIB)/telemetrum-v1.0.ihx \ + $(HEXLIB)/teledongle-v0.2.ihx + +JAVAFLAGS=-Xlint:unchecked -Xlint:deprecation + +ALTOSUIJAR = altosui.jar +FATJAR = fat/altosui.jar + +OS:=$(shell uname) + +LINUX_APP=altosui + +DARWIN_ZIP=Altos-Mac.zip + +WINDOWS_EXE=Altos-Windows.exe + +LINUX_TGZ=Altos-Linux.tgz + +all: altosui.jar $(LINUX_APP) +fat: altosui.jar $(LINUX_APP) $(DARWIN_ZIP) $(WINDOWS_EXE) $(LINUX_TGZ) + +$(CLASSFILES): + +.java.class: + javac -encoding UTF8 -classpath "$(CLASSPATH)" $(JAVAFLAGS) $*.java + +altosui.jar: classes/images classes/altosui classes/libaltosJNI $(CLASSFILES) Manifest.txt + cd ./classes && jar cfm ../$@ altosui/Manifest.txt images/* altosui/*.class libaltosJNI/*.class + +Manifest.txt: Makefile $(CLASSFILES) + echo 'Main-Class: altosui.AltosUI' > $@ + echo "Class-Path: $(FREETTSLIB)/freetts.jar" >> $@ + +classes/altosui: + mkdir -p classes + ln -sf .. classes/altosui + +classes/libaltosJNI: + mkdir -p classes + ln -sf ../../libaltos/libaltosJNI classes/libaltosJNI + +classes/images: + mkdir -p classes/images + ln -sf ../../$(JAVA_ICON) classes/images + +altosui: + echo "#!/bin/sh" > $@ + echo "exec java -Djava.library.path=/usr/lib/altos -jar /usr/share/java/altosui.jar" >> $@ + chmod +x ./altosui + +fat/altosui: + echo "#!/bin/sh" > $@ + echo 'ME=`which "$0"`' >> $@ + echo 'DIR=`dirname "$ME"`' >> $@ + echo 'exec java -Djava.library.path="$$DIR" -jar "$$DIR"/altosui.jar' >> $@ + chmod +x $@ + +fat/altosui.jar: $(CLASSFILES) $(JAVA_ICON) fat/classes/Manifest.txt + mkdir -p fat/classes + test -L fat/classes/altosui || ln -sf ../.. fat/classes/altosui + mkdir -p fat/classes/images + cp $(JAVA_ICON) fat/classes/images + test -L fat/classes/libaltosJNI || ln -sf ../../../libaltos/libaltosJNI fat/classes/libaltosJNI + cd ./fat/classes && jar cfm ../../$@ Manifest.txt images/* altosui/*.class libaltosJNI/*.class + +fat/classes/Manifest.txt: $(CLASSFILES) Makefile + mkdir -p fat/classes + echo 'Main-Class: altosui.AltosUI' > $@ + echo "Class-Path: freetts.jar" >> $@ + +install: altosui.jar altosui + install -m 0644 altosui.jar $(DESTDIR)/usr/share/java/altosui.jar + install -m 0644 altosui.1 $(DESTDIR)/usr/share/man/man1/altosui.1 + install altosui $(DESTDIR)/usr/bin/altosui + +clean: + rm -f *.class altosui.jar + rm -f AltosUI.app/Contents/Resources/Java/* + rm -rf classes + rm -rf windows linux + +distclean: clean + rm -f $(DARWIN_ZIP) $(WINDOWS_EXE) $(LINUX_TGZ) + rm -rf darwin fat + +FAT_FILES=$(FATJAR) $(FREETTSJAR) $(HEXFILES) + +LINUX_FILES=$(FAT_FILES) ../libaltos/libaltos.so fat/altosui +$(LINUX_TGZ): $(LINUX_FILES) + rm -f $@ + mkdir -p linux/AltOS + rm -f linux/AltOS/* + cp $(LINUX_FILES) linux/AltOS + cd linux && tar czf ../$@ AltOS + +DARWIN_RESOURCES=$(FATJAR) $(FREETTSJAR) ../libaltos/libaltos.dylib +DARWIN_EXTRA=$(HEXFILES) +DARWIN_FILES=$(DARWIN_RESOURCES) $(DARWIN_EXTRA) + +$(DARWIN_ZIP): $(DARWIN_FILES) + rm -f $@ + cp -a AltosUI.app darwin/ + mkdir -p darwin/AltosUI.app/Contents/Resources/Java + cp $(DARWIN_RESOURCES) darwin/AltosUI.app/Contents/Resources/Java + mkdir -p darwin/AltOS + cp $(DARWIN_EXTRA) darwin/AltOS + cd darwin && zip -r ../$@ AltosUI.app AltOS + +WINDOWS_FILES = $(FAT_FILES) ../libaltos/altos.dll ../../telemetrum.inf $(WINDOWS_ICON) + +$(WINDOWS_EXE): $(WINDOWS_FILES) altos-windows.nsi + rm -f $@ + mkdir -p windows/AltOS + rm -f windows/AltOS/* + cp $(WINDOWS_FILES) windows/AltOS + makensis altos-windows.nsi diff --git a/altosui/Makefile.am b/altosui/Makefile.am new file mode 100644 index 00000000..e2ff55af --- /dev/null +++ b/altosui/Makefile.am @@ -0,0 +1,272 @@ +SUBDIRS=libaltos +JAVAROOT=classes +AM_JAVACFLAGS=-encoding UTF-8 + +man_MANS=altosui.1 + +altoslibdir=$(libdir)/altos + +CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:libaltos:$(FREETTS)/*:/usr/share/java/*" + +bin_SCRIPTS=altosui + +altosui_JAVA = \ + GrabNDrag.java \ + AltosAscent.java \ + AltosChannelMenu.java \ + AltosConfig.java \ + AltosConfigUI.java \ + AltosConfigureUI.java \ + AltosConvert.java \ + AltosCRCException.java \ + AltosCSV.java \ + AltosCSVUI.java \ + AltosDebug.java \ + AltosDescent.java \ + AltosDeviceDialog.java \ + AltosDevice.java \ + AltosDisplayThread.java \ + AltosEepromDownload.java \ + AltosEepromMonitor.java \ + AltosEepromIterable.java \ + AltosEepromRecord.java \ + AltosFile.java \ + AltosFlash.java \ + AltosFlashUI.java \ + AltosFlightDisplay.java \ + AltosFlightInfoTableModel.java \ + AltosFlightReader.java \ + AltosFlightStatus.java \ + AltosFlightUI.java \ + AltosGPS.java \ + AltosGreatCircle.java \ + AltosHexfile.java \ + Altos.java \ + AltosIgnite.java \ + AltosIgniteUI.java \ + AltosInfoTable.java \ + AltosKML.java \ + AltosLanded.java \ + AltosLed.java \ + AltosLights.java \ + AltosLine.java \ + AltosLog.java \ + AltosPad.java \ + AltosParse.java \ + AltosPreferences.java \ + AltosReader.java \ + AltosRecord.java \ + AltosRecordIterable.java \ + AltosTelemetryReader.java \ + AltosReplayReader.java \ + AltosRomconfig.java \ + AltosRomconfigUI.java \ + AltosSerial.java \ + AltosSerialInUseException.java \ + AltosSerialMonitor.java \ + AltosSiteMap.java \ + AltosSiteMapCache.java \ + AltosSiteMapTile.java \ + AltosState.java \ + AltosTelemetry.java \ + AltosTelemetryIterable.java \ + AltosUI.java \ + AltosWriter.java \ + AltosDataPointReader.java \ + AltosDataPoint.java \ + AltosGraph.java \ + AltosGraphTime.java \ + AltosGraphUI.java \ + AltosDataChooser.java \ + AltosVoice.java + +JFREECHART_CLASS= \ + jfreechart.jar + +JCOMMON_CLASS=\ + jcommon.jar + +FREETTS_CLASS= \ + cmudict04.jar \ + cmulex.jar \ + cmu_time_awb.jar \ + cmutimelex.jar \ + cmu_us_kal.jar \ + en_us.jar \ + freetts.jar + +LIBALTOS= \ + libaltos.so \ + libaltos.dylib \ + altos.dll + +JAR=altosui.jar + +FATJAR=altosui-fat.jar + +# Icons +ICONDIR=$(top_srcdir)/icon + +JAVA_ICON=$(ICONDIR)/altus-metrum-16x16.jpg + +ICONS= $(ICONDIR)/redled.png $(ICONDIR)/redoff.png \ + $(ICONDIR)/greenled.png $(ICONDIR)/greenoff.png \ + $(ICONDIR)/grayled.png $(ICONDIR)/grayoff.png + +# icon base names for jar +ICONJAR= -C $(ICONDIR) altus-metrum-16x16.jpg \ + -C $(ICONDIR) redled.png -C $(ICONDIR) redoff.png \ + -C $(ICONDIR) greenled.png -C $(ICONDIR) greenoff.png \ + -C $(ICONDIR) grayon.png -C $(ICONDIR) grayled.png + +WINDOWS_ICON=$(ICONDIR)/altus-metrum.ico + +# Firmware +FIRMWARE_TD=$(top_srcdir)/src/teledongle-v0.2-$(VERSION).ihx +FIRMWARE_TM=$(top_srcdir)/src/telemetrum-v1.0-$(VERSION).ihx +FIRMWARE=$(FIRMWARE_TM) $(FIRMWARE_TD) + +# Distribution targets +LINUX_DIST=Altos-Linux-$(VERSION).tar.bz2 +MACOSX_DIST=Altos-Mac-$(VERSION).zip +WINDOWS_DIST=Altos-Windows-$(VERSION_DASH).exe + +FAT_FILES=$(FATJAR) $(FREETTS_CLASS) $(JFREECHART_CLASS) $(JCOMMON_CLASS) + +LINUX_FILES=$(FAT_FILES) libaltos.so $(FIRMWARE) +LINUX_EXTRA=altosui-fat + +MACOSX_FILES=$(FAT_FILES) libaltos.dylib +MACOSX_EXTRA=$(FIRMWARE) + +WINDOWS_FILES=$(FAT_FILES) altos.dll $(top_srcdir)/telemetrum.inf $(WINDOWS_ICON) + +all-local: classes/altosui $(JAR) altosui altosui-test altosui-jdb + +clean-local: + -rm -rf classes $(JAR) $(FATJAR) \ + $(LINUX_DIST) $(MACOSX_DIST) windows $(WINDOWS_DIST) $(FREETTS_CLASS) \ + $(JFREECHART_CLASS) $(JCOMMON_CLASS) $(LIBALTOS) Manifest.txt Manifest-fat.txt altos-windows.log \ + altosui altosui-test altosui-jdb macosx linux + +if FATINSTALL + +FATTARGET=$(FATDIR)/$(VERSION) + +LINUX_DIST_TARGET=$(FATTARGET)/$(LINUX_DIST) +MACOSX_DIST_TARGET=$(FATTARGET)/$(MACOSX_DIST) +WINDOWS_DIST_TARGET=$(FATTARGET)/$(WINDOWS_DIST) + +fat: $(LINUX_DIST_TARGET) $(MACOSX_DIST_TARGET) $(WINDOWS_DIST_TARGET) + +$(LINUX_DIST_TARGET): $(LINUX_DIST) + mkdir -p $(FATTARGET) + cp -p $< $@ + +$(MACOSX_DIST_TARGET): $(MACOSX_DIST) + mkdir -p $(FATTARGET) + cp -p $< $@ + +$(WINDOWS_DIST_TARGET): $(WINDOWS_DIST) + mkdir -p $(FATTARGET) + cp -p $< $@ + +else +fat: $(LINUX_DIST) $(MACOSX_DIST) $(WINDOWS_DIST) +endif + + +altosuidir=$(datadir)/java + +install-altosuiJAVA: altosui.jar + @$(NORMAL_INSTALL) + test -z "$(altosuidir)" || $(MKDIR_P) "$(DESTDIR)$(altosuidir)" + echo " $(INSTALL_DATA)" "$<" "'$(DESTDIR)$(altosuidir)/altosui.jar'"; \ + $(INSTALL_DATA) "$<" "$(DESTDIR)$(altosuidir)" + +classes/altosui: + mkdir -p classes/altosui + +$(JAR): classaltosui.stamp Manifest.txt $(JAVA_ICON) + jar cfm $@ Manifest.txt \ + $(ICONJAR) \ + -C classes altosui \ + -C libaltos libaltosJNI + +$(FATJAR): classaltosui.stamp Manifest-fat.txt $(FREETTS_CLASS) $(JFREECHART_CLASS) $(JCOMMON_CLASS) $(LIBALTOS) $(JAVA_ICON) + jar cfm $@ Manifest-fat.txt \ + $(ICONJAR) \ + -C classes altosui \ + -C libaltos libaltosJNI + +Manifest.txt: Makefile + echo 'Main-Class: altosui.AltosUI' > $@ + echo "Class-Path: $(FREETTS)/freetts.jar $(JFREECHART)/jfreechart.jar $(JCOMMON)/jcommon.jar" >> $@ + +Manifest-fat.txt: + echo 'Main-Class: altosui.AltosUI' > $@ + echo "Class-Path: freetts.jar jfreechart.jar jcommon.jar" >> $@ + +altosui: Makefile + echo "#!/bin/sh" > $@ + echo 'exec java -cp "$(FREETTS)/*:$(JFREECHART)/*:$(JCOMMON)/*" -Djava.library.path="$(altoslibdir)" -jar "$(altosuidir)/altosui.jar" "$$@"' >> $@ + chmod +x $@ + +altosui-test: Makefile + echo "#!/bin/sh" > $@ + echo 'exec java -cp "$(FREETTS)/*:$(JFREECHART)/*:$(JCOMMON)/*" -Djava.library.path="libaltos/.libs" -jar altosui.jar "$$@"' >> $@ + chmod +x $@ + +altosui-jdb: Makefile + echo "#!/bin/sh" > $@ + echo 'exec jdb -classpath "classes:libaltos:$(FREETTS)/*:$(JFREECHART)/*:$(JCOMMON)/*" -Djava.library.path="libaltos/.libs" altosui/AltosUI "$$@"' >> $@ + chmod +x $@ + +libaltos.so: + -rm -f "$@" + $(LN_S) libaltos/.libs/"$@" . + +libaltos.dylib: + -rm -f "$@" + $(LN_S) libaltos/"$@" . + +altos.dll: + -rm -f "$@" + $(LN_S) libaltos/"$@" . + +$(FREETTS_CLASS): + -rm -f "$@" + $(LN_S) "$(FREETTS)"/"$@" . + +$(JFREECHART_CLASS): + -rm -f "$@" + $(LN_S) "$(JFREECHART)"/"$@" . + +$(JCOMMON_CLASS): + -rm -f "$@" + $(LN_S) "$(JCOMMON)"/"$@" . + +$(LINUX_DIST): $(LINUX_FILES) $(LINUX_EXTRA) + -rm -f $@ + -rm -rf linux + mkdir -p linux/AltOS + cp -p $(LINUX_FILES) linux/AltOS + cp -p altosui-fat linux/AltOS/altosui + chmod +x linux/AltOS/altosui + tar cjf $@ -C linux AltOS + +$(MACOSX_DIST): $(MACOSX_FILES) $(MACOSX_EXTRA) + -rm -f $@ + -rm -rf macosx + mkdir macosx + cp -a AltosUI.app macosx/ + mkdir -p macosx/AltOS macosx/AltosUI.app/Contents/Resources/Java + cp -p $(FATJAR) macosx/AltosUI.app/Contents/Resources/Java/altosui.jar + cp -p $(FREETTS_CLASS) libaltos.dylib macosx/AltosUI.app/Contents/Resources/Java + cp -p $(JFREECHART_CLASS) libaltos.dylib macosx/AltosUI.app/Contents/Resources/Java + cp -p $(MACOSX_EXTRA) macosx/AltOS + cd macosx && zip -r ../$@ AltosUI.app AltOS + +$(WINDOWS_DIST): $(WINDOWS_FILES) altos-windows.nsi + -rm -f $@ + makensis -Oaltos-windows.log "-XOutFile $@" "-DVERSION=$(VERSION)" altos-windows.nsi diff --git a/altosui/altos-windows.nsi b/altosui/altos-windows.nsi new file mode 100644 index 00000000..37777fd6 --- /dev/null +++ b/altosui/altos-windows.nsi @@ -0,0 +1,113 @@ +!addplugindir Instdrv/NSIS/Plugins + +Name "Altus Metrum Installer" + +; Default install directory +InstallDir "$PROGRAMFILES\AltusMetrum" + +; Tell the installer where to re-install a new version +InstallDirRegKey HKLM "Software\AltusMetrum" "Install_Dir" + +LicenseText "GNU General Public License Version 2" +LicenseData "../../COPYING" + +; Need admin privs for Vista or Win7 +RequestExecutionLevel admin + +ShowInstDetails Show + +ComponentText "Altus Metrum Software and Driver Installer" + +; Pages to present + +Page license +Page components +Page directory +Page instfiles + +UninstPage uninstConfirm +UninstPage instfiles + +; And the stuff to install + +Section "Install Driver" InstDriver + InstDrv::InitDriverSetup /NOUNLOAD {4D36E96D-E325-11CE-BFC1-08002BE10318} "Altus Metrum" + Pop $0 + DetailPrint "InitDriverSetup: $0" + + InstDrv::DeleteOemInfFiles /NOUNLOAD + InstDrv::CreateDevice /NOUNLOAD + SetOutPath $TEMP + File "../../telemetrum.inf" + InstDrv::InstallDriver /NOUNLOAD "$TEMP\telemetrum.inf" + + SetOutPath $INSTDIR + File "../../telemetrum.inf" +SectionEnd + +Section "AltosUI Application" + SetOutPath $INSTDIR + + File "altosui-fat.jar" + File "cmudict04.jar" + File "cmulex.jar" + File "cmu_time_awb.jar" + File "cmutimelex.jar" + File "cmu_us_kal.jar" + File "en_us.jar" + File "freetts.jar" + + File "*.dll" + + File "../../icon/*.ico" + + CreateShortCut "$SMPROGRAMS\AltusMetrum.lnk" "$INSTDIR\altosui-fat.jar" "" "$INSTDIR\altus-metrum.ico" +SectionEnd + +Section "AltosUI Desktop Shortcut" + CreateShortCut "$DESKTOP\AltusMetrum.lnk" "$INSTDIR\altosui-fat.jar" "" "$INSTDIR\altus-metrum.ico" +SectionEnd + +Section "TeleMetrum and TeleDongle Firmware" + + SetOutPath $INSTDIR + + File "../../src/telemetrum-v1.0/telemetrum-v1.0-${VERSION}.ihx" + File "../../src/teledongle-v0.2/teledongle-v0.2-${VERSION}.ihx" + +SectionEnd + +Section "Uninstaller" + + ; Deal with the uninstaller + + SetOutPath $INSTDIR + + ; Write the install path to the registry + WriteRegStr HKLM SOFTWARE\AltusMetrum "Install_Dir" "$INSTDIR" + + ; Write the uninstall keys for windows + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum" "DisplayName" "Altus Metrum" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum" "NoModify" "1" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum" "NoRepair" "1" + + WriteUninstaller "uninstall.exe" +SectionEnd + +Section "Uninstall" + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum" + DeleteRegKey HKLM "Software\AltusMetrum" + + Delete "$INSTDIR\*.*" + RMDir "$INSTDIR" + + ; Remove devices + InstDrv::InitDriverSetup /NOUNLOAD {4D36E96D-E325-11CE-BFC1-08002BE10318} "Altus Metrum" + InstDrv::DeleteOemInfFiles /NOUNLOAD + InstDrv::RemoveAllDevices + + ; Remove shortcuts, if any + Delete "$SMPROGRAMS\AltusMetrum.lnk" + Delete "$DESKTOP\AltusMetrum.lnk" +SectionEnd diff --git a/altosui/altosui-fat b/altosui/altosui-fat new file mode 100755 index 00000000..95b1c051 --- /dev/null +++ b/altosui/altosui-fat @@ -0,0 +1,4 @@ +#!/bin/sh +me=`which "$0"` +dir=`dirname "$me"` +exec java -cp "$dir/*" -Djava.library.path="$dir" -jar "$dir"/altosui-fat.jar "$@" diff --git a/altosui/altosui.1 b/altosui/altosui.1 new file mode 100644 index 00000000..57fa4489 --- /dev/null +++ b/altosui/altosui.1 @@ -0,0 +1,46 @@ +.\" +.\" Copyright © 2010 Bdale Garbee +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, but +.\" WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +.\" General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License along +.\" with this program; if not, write to the Free Software Foundation, Inc., +.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +.\" +.\" +.TH ALTOSUI 1 "altosui" "" +.SH NAME +altosui \- Rocket flight monitor +.SH SYNOPSIS +.B "altosui" +.SH DESCRIPTION +.I altosui +connects to a TeleDongle or TeleMetrum device through a USB serial device. +It provides a menu-oriented +user interface to monitor, record and review rocket flight data. +.SH USAGE +When connected to a TeleDongle device, altosui turns on the radio +receiver and listens for telemetry packets. It displays the received +telemetry data, and reports flight status via voice synthesis. All +received telemetry information is recorded to a file. +.P +When connected to a TeleMetrum device, altosui can be used to configure the +TeleMetrum, and to downloads the eeprom data and store it in a file. +.P +A number of other menu options exist, including the ability to export flight +data in different formats. +.SH FILES +All data log files are recorded into a user-specified directory +(default ~/TeleMetrum). Files are named using the current date, the serial +number of the reporting device, the flight number recorded in the data +and either '.telem' for telemetry data or '.eeprom' for eeprom data. +.SH AUTHOR +Keith Packard diff --git a/altosui/altusmetrum.jpg b/altosui/altusmetrum.jpg new file mode 100644 index 00000000..04027921 Binary files /dev/null and b/altosui/altusmetrum.jpg differ diff --git a/altosui/libaltos/.gitignore b/altosui/libaltos/.gitignore new file mode 100644 index 00000000..c490e6f8 --- /dev/null +++ b/altosui/libaltos/.gitignore @@ -0,0 +1,12 @@ +*.so +*.lo +*.la +*.java +*.class +.libs/ +classlibaltos.stamp +libaltos_wrap.c +libaltosJNI +cjnitest +libaltos.swig +swig_bindings/ diff --git a/altosui/libaltos/Makefile-standalone b/altosui/libaltos/Makefile-standalone new file mode 100644 index 00000000..4e438050 --- /dev/null +++ b/altosui/libaltos/Makefile-standalone @@ -0,0 +1,126 @@ +OS:=$(shell uname) + +# +# Linux +# +ifeq ($(OS),Linux) + +JAVA_CFLAGS=-I/usr/lib/jvm/java-6-openjdk/include + +OS_LIB_CFLAGS=-DLINUX -DPOSIX_TTY $(JAVA_CFLAGS) + +OS_APP_CFLAGS=$(OS_LIB_CFLAGS) + +OS_LDFLAGS= + +LIBNAME=libaltos.so +EXEEXT= +endif + +# +# Darwin (Mac OS X) +# +ifeq ($(OS),Darwin) + +OS_LIB_CFLAGS=\ + -DDARWIN -DPOSIX_TTY -arch i386 -arch x86_64 \ + --sysroot=/Developer/SDKs/MacOSX10.5.sdk -mmacosx-version-min=10.5 \ + -iwithsysroot /System/Library/Frameworks/JavaVM.framework/Headers \ + -iwithsysroot /System/Library/Frameworks/IOKit.framework/Headers \ + -iwithsysroot /System/Library/Frameworks/CoreFoundation.framework/Headers +OS_APP_CFLAGS=$(OS_LIB_CFLAGS) + +OS_LDFLAGS =\ + -framework IOKit -framework CoreFoundation + +LIBNAME=libaltos.dylib +EXEEXT= + +endif + +# +# Windows +# +ifneq (,$(findstring MINGW,$(OS))) + +CC=gcc + +OS_LIB_CFLAGS = -DWINDOWS -mconsole -DBUILD_DLL +OS_APP_CFLAGS = -DWINDOWS -mconsole + +OS_LDFLAGS = -lgdi32 -luser32 -lcfgmgr32 -lsetupapi -lole32 \ + -ladvapi32 -lcomctl32 -mconsole -Wl,--add-stdcall-alias + +LIBNAME=altos.dll + +EXEEXT=.exe + +endif + +.SUFFIXES: .java .class + +CLASSPATH=".:jnitest/*:libaltosJNI:/usr/share/java/*" + +SWIG_DIR=swig_bindings/java +SWIG_FILE=$(SWIG_DIR)/libaltos.swig +SWIG_WRAP=$(SWIG_DIR)/libaltos_wrap.c + +JNI_DIR=libaltosJNI +JNI_FILE=$(JNI_DIR)/libaltosJNI.java +JNI_SRCS=$(JNI_FILE) \ + $(JNI_DIR)/SWIGTYPE_p_altos_file.java \ + $(JNI_DIR)/SWIGTYPE_p_altos_list.java \ + $(JNI_DIR)/altos_device.java \ + $(JNI_DIR)/libaltos.java + +JAVAFILES=\ + $(JNI_SRCS) + +CLASSFILES = $(JAVAFILES:%.java=%.class) + +JAVAFLAGS=-Xlint:unchecked + +CJNITEST=cjnitest$(EXEEXT) + +all: $(LIBNAME) $(CJNITEST) $(CLASSFILES) + +.java.class: + javac -encoding UTF8 -classpath "$(CLASSPATH)" $(JAVAFLAGS) $*.java + +CFLAGS=$(OS_LIB_CFLAGS) -O -I. + +LDFLAGS=$(OS_LDFLAGS) + +HEADERS=libaltos.h +SRCS = libaltos.c $(SWIG_WRAP) +OBJS = $(SRCS:%.c=%.o) +LIBS = $(DARWIN_LIBS) + +$(CJNITEST): cjnitest.c $(LIBNAME) + $(CC) -o $@ $(OS_APP_CFLAGS) cjnitest.c $(LIBNAME) $(LIBS) $(LDFLAGS) + +$(LIBNAME): $(OBJS) + $(CC) -shared $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(LDFLAGS) + +clean: + rm -f $(CLASSFILES) $(OBJS) $(LIBNAME) $(CJNITEST) cjnitest.o + rm -rf swig_bindings libaltosJNI + +distclean: clean + +$(JNI_FILE): libaltos.i0 $(HEADERS) + mkdir -p $(SWIG_DIR) + mkdir -p libaltosJNI + sed 's;//%;%;' libaltos.i0 $(HEADERS) > $(SWIG_FILE) + swig -java -package libaltosJNI $(SWIG_FILE) + cp swig_bindings/java/*.java libaltosJNI + +$(SWIG_WRAP): $(JNI_FILE) + +ifeq ($(OS),Linux) +install: $(LIBNAME) + install -c $(LIBNAME) $(DESTDIR)/usr/lib/altos/$(LIBNAME) + +endif + +.NOTPARALLEL: diff --git a/altosui/libaltos/Makefile.am b/altosui/libaltos/Makefile.am new file mode 100644 index 00000000..388d2104 --- /dev/null +++ b/altosui/libaltos/Makefile.am @@ -0,0 +1,41 @@ +JAVAC=javac +AM_CFLAGS=-DLINUX -DPOSIX_TTY -I$(JVM_INCLUDE) +AM_JAVACFLAGS=-encoding UTF-8 + +altoslibdir=$(libdir)/altos + +altoslib_LTLIBRARIES=libaltos.la + +libaltos_la_LDFLAGS = -version-info 1:0:1 + +libaltos_la_SOURCES=\ + libaltos.c \ + libaltos_wrap.c + +noinst_PROGRAMS=cjnitest + +cjnitest_LDADD=libaltos.la + +LIBS= + +HFILES=libaltos.h + +SWIG_FILE=libaltos.swig + +CLASSDIR=libaltosJNI + +$(SWIG_FILE): libaltos.i0 $(HFILES) + sed 's;//%;%;' libaltos.i0 $(HFILES) > $(SWIG_FILE) + +all-local: classlibaltos.stamp + +libaltos_wrap.c: classlibaltos.stamp + +classlibaltos.stamp: $(SWIG_FILE) + swig -java -package libaltosJNI $(SWIG_FILE) + mkdir -p libaltosJNI + $(JAVAC) -d . $(AM_JAVACFLAGS) $(JAVACFLAGS) *.java && \ + touch classlibaltos.stamp + +clean-local: + -rm -rf libaltosJNI *.class *.java classlibaltos.stamp $(SWIG_FILE) libaltos_wrap.c diff --git a/altosui/libaltos/altos.dll b/altosui/libaltos/altos.dll new file mode 100755 index 00000000..28e9b4ad Binary files /dev/null and b/altosui/libaltos/altos.dll differ diff --git a/altosui/libaltos/cjnitest.c b/altosui/libaltos/cjnitest.c new file mode 100644 index 00000000..c6d6e069 --- /dev/null +++ b/altosui/libaltos/cjnitest.c @@ -0,0 +1,43 @@ +#include +#include "libaltos.h" + +static void +altos_puts(struct altos_file *file, char *string) +{ + char c; + + while ((c = *string++)) + altos_putchar(file, c); +} + +main () +{ + struct altos_device device; + struct altos_list *list; + + altos_init(); + list = altos_list_start(); + while (altos_list_next(list, &device)) { + struct altos_file *file; + int c; + + printf ("%04x:%04x %-20s %4d %s\n", device.vendor, device.product, + device.name, device.serial, device.path); + + file = altos_open(&device); + if (!file) { + printf("altos_open failed\n"); + continue; + } + altos_puts(file,"v\nc s\n"); + altos_flush(file); + while ((c = altos_getchar(file, 100)) >= 0) { + putchar (c); + } + if (c != LIBALTOS_TIMEOUT) + printf ("getchar returns %d\n", c); + altos_close(file); + } + altos_list_finish(list); + altos_fini(); +} diff --git a/altosui/libaltos/libaltos.c b/altosui/libaltos/libaltos.c new file mode 100644 index 00000000..465f0ac8 --- /dev/null +++ b/altosui/libaltos/libaltos.c @@ -0,0 +1,1028 @@ +/* + * 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. + */ + +#include "libaltos.h" +#include +#include +#include + +#define USE_POLL + +PUBLIC int +altos_init(void) +{ + return LIBALTOS_SUCCESS; +} + +PUBLIC void +altos_fini(void) +{ +} + +#ifdef DARWIN + +#undef USE_POLL + +/* Mac OS X don't have strndup even if _GNU_SOURCE is defined */ +static char * +altos_strndup (const char *s, size_t n) +{ + size_t len = strlen (s); + char *ret; + + if (len <= n) + return strdup (s); + ret = malloc(n + 1); + strncpy(ret, s, n); + ret[n] = '\0'; + return ret; +} + +#else +#define altos_strndup strndup +#endif + +/* + * Scan for Altus Metrum devices by looking through /sys + */ + +#ifdef LINUX + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +static char * +cc_fullname (char *dir, char *file) +{ + char *new; + int dlen = strlen (dir); + int flen = strlen (file); + int slen = 0; + + if (dir[dlen-1] != '/') + slen = 1; + new = malloc (dlen + slen + flen + 1); + if (!new) + return 0; + strcpy(new, dir); + if (slen) + strcat (new, "/"); + strcat(new, file); + return new; +} + +static char * +cc_basename(char *file) +{ + char *b; + + b = strrchr(file, '/'); + if (!b) + return file; + return b + 1; +} + +static char * +load_string(char *dir, char *file) +{ + char *full = cc_fullname(dir, file); + char line[4096]; + char *r; + FILE *f; + int rlen; + + f = fopen(full, "r"); + free(full); + if (!f) + return NULL; + r = fgets(line, sizeof (line), f); + fclose(f); + if (!r) + return NULL; + rlen = strlen(r); + if (r[rlen-1] == '\n') + r[rlen-1] = '\0'; + return strdup(r); +} + +static int +load_hex(char *dir, char *file) +{ + char *line; + char *end; + long i; + + line = load_string(dir, file); + if (!line) + return -1; + i = strtol(line, &end, 16); + free(line); + if (end == line) + return -1; + return i; +} + +static int +load_dec(char *dir, char *file) +{ + char *line; + char *end; + long i; + + line = load_string(dir, file); + if (!line) + return -1; + i = strtol(line, &end, 10); + free(line); + if (end == line) + return -1; + return i; +} + +static int +dir_filter_tty_colon(const struct dirent *d) +{ + return strncmp(d->d_name, "tty:", 4) == 0; +} + +static int +dir_filter_tty(const struct dirent *d) +{ + return strncmp(d->d_name, "tty", 3) == 0; +} + +struct altos_usbdev { + char *sys; + char *tty; + char *manufacturer; + char *product_name; + int serial; /* AltOS always uses simple integer serial numbers */ + int idProduct; + int idVendor; +}; + +static char * +usb_tty(char *sys) +{ + char *base; + int num_configs; + int config; + struct dirent **namelist; + int interface; + int num_interfaces; + char endpoint_base[20]; + char *endpoint_full; + char *tty_dir; + int ntty; + char *tty; + + base = cc_basename(sys); + num_configs = load_hex(sys, "bNumConfigurations"); + num_interfaces = load_hex(sys, "bNumInterfaces"); + for (config = 1; config <= num_configs; config++) { + for (interface = 0; interface < num_interfaces; interface++) { + sprintf(endpoint_base, "%s:%d.%d", + base, config, interface); + endpoint_full = cc_fullname(sys, endpoint_base); + + /* Check for tty:ttyACMx style names + */ + ntty = scandir(endpoint_full, &namelist, + dir_filter_tty_colon, + alphasort); + if (ntty > 0) { + free(endpoint_full); + tty = cc_fullname("/dev", namelist[0]->d_name + 4); + free(namelist); + return tty; + } + + /* Check for tty/ttyACMx style names + */ + tty_dir = cc_fullname(endpoint_full, "tty"); + free(endpoint_full); + ntty = scandir(tty_dir, &namelist, + dir_filter_tty, + alphasort); + free (tty_dir); + if (ntty > 0) { + tty = cc_fullname("/dev", namelist[0]->d_name); + free(namelist); + return tty; + } + } + } + return NULL; +} + +static struct altos_usbdev * +usb_scan_device(char *sys) +{ + struct altos_usbdev *usbdev; + + usbdev = calloc(1, sizeof (struct altos_usbdev)); + if (!usbdev) + return NULL; + usbdev->sys = strdup(sys); + usbdev->manufacturer = load_string(sys, "manufacturer"); + usbdev->product_name = load_string(sys, "product"); + usbdev->serial = load_dec(sys, "serial"); + usbdev->idProduct = load_hex(sys, "idProduct"); + usbdev->idVendor = load_hex(sys, "idVendor"); + usbdev->tty = usb_tty(sys); + return usbdev; +} + +static void +usbdev_free(struct altos_usbdev *usbdev) +{ + free(usbdev->sys); + free(usbdev->manufacturer); + free(usbdev->product_name); + /* this can get used as a return value */ + if (usbdev->tty) + free(usbdev->tty); + free(usbdev); +} + +#define USB_DEVICES "/sys/bus/usb/devices" + +static int +dir_filter_dev(const struct dirent *d) +{ + const char *n = d->d_name; + char c; + + while ((c = *n++)) { + if (isdigit(c)) + continue; + if (c == '-') + continue; + if (c == '.' && n != d->d_name + 1) + continue; + return 0; + } + return 1; +} + +struct altos_list { + struct altos_usbdev **dev; + int current; + int ndev; +}; + +struct altos_list * +altos_list_start(void) +{ + int e; + struct dirent **ents; + char *dir; + struct altos_usbdev *dev; + struct altos_list *devs; + int n; + + devs = calloc(1, sizeof (struct altos_list)); + if (!devs) + return NULL; + + n = scandir (USB_DEVICES, &ents, + dir_filter_dev, + alphasort); + if (!n) + return 0; + for (e = 0; e < n; e++) { + dir = cc_fullname(USB_DEVICES, ents[e]->d_name); + dev = usb_scan_device(dir); + free(dir); + if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) { + if (devs->dev) + devs->dev = realloc(devs->dev, + devs->ndev + 1 * sizeof (struct usbdev *)); + else + devs->dev = malloc (sizeof (struct usbdev *)); + devs->dev[devs->ndev++] = dev; + } + } + free(ents); + devs->current = 0; + return devs; +} + +int +altos_list_next(struct altos_list *list, struct altos_device *device) +{ + struct altos_usbdev *dev; + if (list->current >= list->ndev) + return 0; + dev = list->dev[list->current]; + strcpy(device->name, dev->product_name); + device->vendor = dev->idVendor; + device->product = dev->idProduct; + strcpy(device->path, dev->tty); + device->serial = dev->serial; + list->current++; + return 1; +} + +void +altos_list_finish(struct altos_list *usbdevs) +{ + int i; + + if (!usbdevs) + return; + for (i = 0; i < usbdevs->ndev; i++) + usbdev_free(usbdevs->dev[i]); + free(usbdevs); +} + +#endif + +#ifdef DARWIN + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct altos_list { + io_iterator_t iterator; +}; + +static int +get_string(io_object_t object, CFStringRef entry, char *result, int result_len) +{ + CFTypeRef entry_as_string; + Boolean got_string; + + entry_as_string = IORegistryEntrySearchCFProperty (object, + kIOServicePlane, + entry, + kCFAllocatorDefault, + kIORegistryIterateRecursively); + if (entry_as_string) { + got_string = CFStringGetCString(entry_as_string, + result, result_len, + kCFStringEncodingASCII); + + CFRelease(entry_as_string); + if (got_string) + return 1; + } + return 0; +} + +static int +get_number(io_object_t object, CFStringRef entry, int *result) +{ + CFTypeRef entry_as_number; + Boolean got_number; + + entry_as_number = IORegistryEntrySearchCFProperty (object, + kIOServicePlane, + entry, + kCFAllocatorDefault, + kIORegistryIterateRecursively); + if (entry_as_number) { + got_number = CFNumberGetValue(entry_as_number, + kCFNumberIntType, + result); + if (got_number) + return 1; + } + return 0; +} + +struct altos_list * +altos_list_start(void) +{ + struct altos_list *list = calloc (sizeof (struct altos_list), 1); + CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice"); + io_iterator_t tdIterator; + io_object_t tdObject; + kern_return_t ret; + int i; + + ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator); + if (ret != kIOReturnSuccess) + return NULL; + return list; +} + +int +altos_list_next(struct altos_list *list, struct altos_device *device) +{ + io_object_t object; + char serial_string[128]; + + for (;;) { + object = IOIteratorNext(list->iterator); + if (!object) + return 0; + + if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) || + !get_number (object, CFSTR(kUSBProductID), &device->product)) + continue; + if (device->vendor != 0xfffe) + continue; + if (device->product < 0x000a || 0x0013 < device->product) + continue; + if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) && + get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) && + get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) { + device->serial = atoi(serial_string); + return 1; + } + } +} + +void +altos_list_finish(struct altos_list *list) +{ + IOObjectRelease (list->iterator); + free(list); +} + +#endif + +#ifdef POSIX_TTY + +#include +#include +#include +#include +#include + +#define USB_BUF_SIZE 64 + +struct altos_file { + int fd; +#ifdef USE_POLL + int pipe[2]; +#else + int out_fd; +#endif + unsigned char out_data[USB_BUF_SIZE]; + int out_used; + unsigned char in_data[USB_BUF_SIZE]; + int in_used; + int in_read; +}; + +PUBLIC struct altos_file * +altos_open(struct altos_device *device) +{ + struct altos_file *file = calloc (sizeof (struct altos_file), 1); + int ret; + struct termios term; + + if (!file) + return NULL; + + file->fd = open(device->path, O_RDWR | O_NOCTTY); + if (file->fd < 0) { + perror(device->path); + free(file); + return NULL; + } +#ifdef USE_POLL + pipe(file->pipe); +#else + file->out_fd = open(device->path, O_RDWR | O_NOCTTY); + if (file->out_fd < 0) { + perror(device->path); + close(file->fd); + free(file); + return NULL; + } +#endif + ret = tcgetattr(file->fd, &term); + if (ret < 0) { + perror("tcgetattr"); + close(file->fd); +#ifndef USE_POLL + close(file->out_fd); +#endif + free(file); + return NULL; + } + cfmakeraw(&term); +#ifdef USE_POLL + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 0; +#else + term.c_cc[VMIN] = 0; + term.c_cc[VTIME] = 1; +#endif + ret = tcsetattr(file->fd, TCSAFLUSH, &term); + if (ret < 0) { + perror("tcsetattr"); + close(file->fd); +#ifndef USE_POLL + close(file->out_fd); +#endif + free(file); + return NULL; + } + return file; +} + +PUBLIC void +altos_close(struct altos_file *file) +{ + if (file->fd != -1) { + int fd = file->fd; + file->fd = -1; +#ifdef USE_POLL + write(file->pipe[1], "\r", 1); +#else + close(file->out_fd); + file->out_fd = -1; +#endif + close(fd); + } +} + +PUBLIC void +altos_free(struct altos_file *file) +{ + altos_close(file); + free(file); +} + +PUBLIC int +altos_flush(struct altos_file *file) +{ + if (file->out_used && 0) { + printf ("flush \""); + fwrite(file->out_data, 1, file->out_used, stdout); + printf ("\"\n"); + } + while (file->out_used) { + int ret; + + if (file->fd < 0) + return -EBADF; +#ifdef USE_POLL + ret = write (file->fd, file->out_data, file->out_used); +#else + ret = write (file->out_fd, file->out_data, file->out_used); +#endif + if (ret < 0) + return -errno; + if (ret) { + memmove(file->out_data, file->out_data + ret, + file->out_used - ret); + file->out_used -= ret; + } + } + return 0; +} + +PUBLIC int +altos_putchar(struct altos_file *file, char c) +{ + int ret; + + if (file->out_used == USB_BUF_SIZE) { + ret = altos_flush(file); + if (ret) { + return ret; + } + } + file->out_data[file->out_used++] = c; + ret = 0; + if (file->out_used == USB_BUF_SIZE) + ret = altos_flush(file); + return 0; +} + +#ifdef USE_POLL +#include +#endif + +static int +altos_fill(struct altos_file *file, int timeout) +{ + int ret; +#ifdef USE_POLL + struct pollfd fd[2]; +#endif + + if (timeout == 0) + timeout = -1; + while (file->in_read == file->in_used) { + if (file->fd < 0) + return LIBALTOS_ERROR; +#ifdef USE_POLL + fd[0].fd = file->fd; + fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; + fd[1].fd = file->pipe[0]; + fd[1].events = POLLIN; + ret = poll(fd, 2, timeout); + if (ret < 0) { + perror("altos_getchar"); + return LIBALTOS_ERROR; + } + if (ret == 0) + return LIBALTOS_TIMEOUT; + + if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL)) + return LIBALTOS_ERROR; + if (fd[0].revents & POLLIN) +#endif + { + ret = read(file->fd, file->in_data, USB_BUF_SIZE); + if (ret < 0) { + perror("altos_getchar"); + return LIBALTOS_ERROR; + } + file->in_read = 0; + file->in_used = ret; +#ifndef USE_POLL + if (ret == 0 && timeout > 0) + return LIBALTOS_TIMEOUT; +#endif + } + } + if (file->in_used && 0) { + printf ("fill \""); + fwrite(file->in_data, 1, file->in_used, stdout); + printf ("\"\n"); + } + return 0; +} + +PUBLIC int +altos_getchar(struct altos_file *file, int timeout) +{ + int ret; + while (file->in_read == file->in_used) { + if (file->fd < 0) + return LIBALTOS_ERROR; + ret = altos_fill(file, timeout); + if (ret) + return ret; + } + return file->in_data[file->in_read++]; +} + +#endif /* POSIX_TTY */ + +#ifdef WINDOWS + +#include +#include +#include + +struct altos_list { + HDEVINFO dev_info; + int index; +}; + +#define USB_BUF_SIZE 64 + +struct altos_file { + HANDLE handle; + unsigned char out_data[USB_BUF_SIZE]; + int out_used; + unsigned char in_data[USB_BUF_SIZE]; + int in_used; + int in_read; + OVERLAPPED ov_read; + BOOL pend_read; + OVERLAPPED ov_write; +}; + +PUBLIC struct altos_list * +altos_list_start(void) +{ + struct altos_list *list = calloc(1, sizeof (struct altos_list)); + + if (!list) + return NULL; + list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL, + DIGCF_ALLCLASSES|DIGCF_PRESENT); + if (list->dev_info == INVALID_HANDLE_VALUE) { + printf("SetupDiGetClassDevs failed %d\n", GetLastError()); + free(list); + return NULL; + } + list->index = 0; + return list; +} + +PUBLIC int +altos_list_next(struct altos_list *list, struct altos_device *device) +{ + SP_DEVINFO_DATA dev_info_data; + char port[128]; + DWORD port_len; + char friendlyname[256]; + char symbolic[256]; + DWORD symbolic_len; + HKEY dev_key; + int vid, pid; + int serial; + HRESULT result; + DWORD friendlyname_type; + DWORD friendlyname_len; + + dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA); + while(SetupDiEnumDeviceInfo(list->dev_info, list->index, + &dev_info_data)) + { + list->index++; + + dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data, + DICS_FLAG_GLOBAL, 0, DIREG_DEV, + KEY_READ); + if (dev_key == INVALID_HANDLE_VALUE) { + printf("cannot open device registry key\n"); + continue; + } + + /* Fetch symbolic name for this device and parse out + * the vid/pid/serial info */ + symbolic_len = sizeof(symbolic); + result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL, + symbolic, &symbolic_len); + if (result != 0) { + printf("cannot find SymbolicName value\n"); + RegCloseKey(dev_key); + continue; + } + vid = pid = serial = 0; + sscanf(symbolic + sizeof("\\??\\USB#VID_") - 1, + "%04X", &vid); + sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1, + "%04X", &pid); + sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1, + "%d", &serial); + if (!USB_IS_ALTUSMETRUM(vid, pid)) { + RegCloseKey(dev_key); + continue; + } + + /* Fetch the com port name */ + port_len = sizeof (port); + result = RegQueryValueEx(dev_key, "PortName", NULL, NULL, + port, &port_len); + RegCloseKey(dev_key); + if (result != 0) { + printf("failed to get PortName\n"); + continue; + } + + /* Fetch the device description which is the device name, + * with firmware that has unique USB ids */ + friendlyname_len = sizeof (friendlyname); + if(!SetupDiGetDeviceRegistryProperty(list->dev_info, + &dev_info_data, + SPDRP_FRIENDLYNAME, + &friendlyname_type, + (BYTE *)friendlyname, + sizeof(friendlyname), + &friendlyname_len)) + { + printf("Failed to get friendlyname\n"); + continue; + } + device->vendor = vid; + device->product = pid; + device->serial = serial; + strcpy(device->name, friendlyname); + + strcpy(device->path, port); + return 1; + } + result = GetLastError(); + if (result != ERROR_NO_MORE_ITEMS) + printf ("SetupDiEnumDeviceInfo failed error %d\n", result); + return 0; +} + +PUBLIC void +altos_list_finish(struct altos_list *list) +{ + SetupDiDestroyDeviceInfoList(list->dev_info); + free(list); +} + +static int +altos_queue_read(struct altos_file *file) +{ + DWORD got; + if (file->pend_read) + return LIBALTOS_SUCCESS; + + if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) { + if (GetLastError() != ERROR_IO_PENDING) + return LIBALTOS_ERROR; + file->pend_read = TRUE; + } else { + file->pend_read = FALSE; + file->in_read = 0; + file->in_used = got; + } + return LIBALTOS_SUCCESS; +} + +static int +altos_wait_read(struct altos_file *file, int timeout) +{ + DWORD ret; + DWORD got; + + if (!file->pend_read) + return LIBALTOS_SUCCESS; + + if (!timeout) + timeout = INFINITE; + + ret = WaitForSingleObject(file->ov_read.hEvent, timeout); + switch (ret) { + case WAIT_OBJECT_0: + if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) + return LIBALTOS_ERROR; + file->pend_read = FALSE; + file->in_read = 0; + file->in_used = got; + break; + case WAIT_TIMEOUT: + return LIBALTOS_TIMEOUT; + break; + default: + return LIBALTOS_ERROR; + } + return LIBALTOS_SUCCESS; +} + +static int +altos_fill(struct altos_file *file, int timeout) +{ + int ret; + + if (file->in_read < file->in_used) + return LIBALTOS_SUCCESS; + + file->in_read = file->in_used = 0; + + ret = altos_queue_read(file); + if (ret) + return ret; + ret = altos_wait_read(file, timeout); + if (ret) + return ret; + + return LIBALTOS_SUCCESS; +} + +PUBLIC int +altos_flush(struct altos_file *file) +{ + DWORD put; + char *data = file->out_data; + char used = file->out_used; + DWORD ret; + + while (used) { + if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) { + if (GetLastError() != ERROR_IO_PENDING) + return LIBALTOS_ERROR; + ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE); + switch (ret) { + case WAIT_OBJECT_0: + if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) + return LIBALTOS_ERROR; + break; + default: + return LIBALTOS_ERROR; + } + } + data += put; + used -= put; + } + file->out_used = 0; + return LIBALTOS_SUCCESS; +} + +PUBLIC struct altos_file * +altos_open(struct altos_device *device) +{ + struct altos_file *file = calloc (1, sizeof (struct altos_file)); + char full_name[64]; + DCB dcbSerialParams = {0}; + COMMTIMEOUTS timeouts; + + if (!file) + return NULL; + + strcpy(full_name, "\\\\.\\"); + strcat(full_name, device->path); + file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + if (file->handle == INVALID_HANDLE_VALUE) { + free(file); + return NULL; + } + file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; + timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */ + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + SetCommTimeouts(file->handle, &timeouts); + + dcbSerialParams.DCBlength = sizeof(dcbSerialParams); + if (!GetCommState(file->handle, &dcbSerialParams)) { + CloseHandle(file->handle); + free(file); + return NULL; + } + dcbSerialParams.BaudRate = CBR_9600; + dcbSerialParams.ByteSize = 8; + dcbSerialParams.StopBits = ONESTOPBIT; + dcbSerialParams.Parity = NOPARITY; + if (!SetCommState(file->handle, &dcbSerialParams)) { + CloseHandle(file->handle); + free(file); + return NULL; + } + + return file; +} + +PUBLIC void +altos_close(struct altos_file *file) +{ + if (file->handle != INVALID_HANDLE_VALUE) { + CloseHandle(file->handle); + file->handle = INVALID_HANDLE_VALUE; + } +} + +PUBLIC void +altos_free(struct altos_file *file) +{ + altos_close(file); + free(file); +} + +int +altos_putchar(struct altos_file *file, char c) +{ + int ret; + + if (file->out_used == USB_BUF_SIZE) { + ret = altos_flush(file); + if (ret) + return ret; + } + file->out_data[file->out_used++] = c; + if (file->out_used == USB_BUF_SIZE) + return altos_flush(file); + return LIBALTOS_SUCCESS; +} + +int +altos_getchar(struct altos_file *file, int timeout) +{ + int ret; + while (file->in_read == file->in_used) { + if (file->handle == INVALID_HANDLE_VALUE) + return LIBALTOS_ERROR; + ret = altos_fill(file, timeout); + if (ret) + return ret; + } + return file->in_data[file->in_read++]; +} + +#endif diff --git a/altosui/libaltos/libaltos.dylib b/altosui/libaltos/libaltos.dylib new file mode 100755 index 00000000..89aa12e7 Binary files /dev/null and b/altosui/libaltos/libaltos.dylib differ diff --git a/altosui/libaltos/libaltos.h b/altosui/libaltos/libaltos.h new file mode 100644 index 00000000..6e94899e --- /dev/null +++ b/altosui/libaltos/libaltos.h @@ -0,0 +1,102 @@ +/* + * 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. + */ + +#ifndef _LIBALTOS_H_ +#define _LIBALTOS_H_ + +#include + +#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# ifndef BUILD_STATIC +# ifdef BUILD_DLL +# define PUBLIC __declspec(dllexport) +# else +# define PUBLIC __declspec(dllimport) +# endif +# endif /* BUILD_STATIC */ +#endif + +#ifndef PUBLIC +# define PUBLIC +#endif + +#define USB_VENDOR_FSF 0xfffe +#define USB_VENDOR_ALTUSMETRUM USB_VENDOR_FSF +#define USB_PRODUCT_ALTUSMETRUM 0x000a +#define USB_PRODUCT_TELEMETRUM 0x000b +#define USB_PRODUCT_TELEDONGLE 0x000c +#define USB_PRODUCT_TELETERRA 0x000d +#define USB_PRODUCT_ALTUSMETRUM_MIN 0x000a +#define USB_PRODUCT_ALTUSMETRUM_MAX 0x0013 + +#define USB_IS_ALTUSMETRUM(v,p) ((v) == USB_VENDOR_ALTUSMETRUM && \ + (USB_PRODUCT_ALTUSMETRUM_MIN <= (p) && \ + (p) <= USB_PRODUCT_ALTUSMETRUM_MAX)) + +struct altos_device { + //%immutable; + int vendor; + int product; + int serial; + char name[256]; + char path[256]; + //%mutable; +}; + +#define LIBALTOS_SUCCESS 0 +#define LIBALTOS_ERROR -1 +#define LIBALTOS_TIMEOUT -2 + +/* Returns 0 for success, < 0 on error */ +PUBLIC int +altos_init(void); + +PUBLIC void +altos_fini(void); + +PUBLIC struct altos_list * +altos_list_start(void); + +/* Returns 1 for success, zero on end of list */ +PUBLIC int +altos_list_next(struct altos_list *list, struct altos_device *device); + +PUBLIC void +altos_list_finish(struct altos_list *list); + +PUBLIC struct altos_file * +altos_open(struct altos_device *device); + +PUBLIC void +altos_close(struct altos_file *file); + +PUBLIC void +altos_free(struct altos_file *file); + +/* Returns < 0 for error */ +PUBLIC int +altos_putchar(struct altos_file *file, char c); + +/* Returns < 0 for error */ +PUBLIC int +altos_flush(struct altos_file *file); + +/* Returns < 0 for error or timeout. timeout of 0 == wait forever */ +PUBLIC int +altos_getchar(struct altos_file *file, int timeout); + +#endif /* _LIBALTOS_H_ */ diff --git a/altosui/libaltos/libaltos.i0 b/altosui/libaltos/libaltos.i0 new file mode 100644 index 00000000..d06468f5 --- /dev/null +++ b/altosui/libaltos/libaltos.i0 @@ -0,0 +1,5 @@ +%module libaltos +%{ +#include "libaltos.h" +%} + diff --git a/ao-tools/Makefile.am b/ao-tools/Makefile.am index 54dc777a..2850e909 100644 --- a/ao-tools/Makefile.am +++ b/ao-tools/Makefile.am @@ -1 +1 @@ -SUBDIRS=lib ao-rawload ao-dbg ao-dumplog ao-bitbang ao-eeprom ao-list ao-load ao-postflight ao-view libaltos altosui +SUBDIRS=lib ao-rawload ao-dbg ao-dumplog ao-bitbang ao-eeprom ao-list ao-load ao-postflight ao-view diff --git a/ao-tools/altosui/.gitignore b/ao-tools/altosui/.gitignore deleted file mode 100644 index 89be1d53..00000000 --- a/ao-tools/altosui/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -windows/ -linux/ -macosx/ -fat/ -Manifest.txt -Manifest-fat.txt -libaltosJNI -classes -altosui -altosui-test -classaltosui.stamp -Altos-Linux-*.tar.bz2 -Altos-Mac-*.zip -Altos-Windows-*.exe -*.dll -*.dylib -*.so -*.jar -*.class diff --git a/ao-tools/altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml b/ao-tools/altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml deleted file mode 100644 index 18e00fe4..00000000 --- a/ao-tools/altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ao-tools/altosui/AltOS Package Configuration.pmdoc/01altosui.xml b/ao-tools/altosui/AltOS Package Configuration.pmdoc/01altosui.xml deleted file mode 100644 index 6170931b..00000000 --- a/ao-tools/altosui/AltOS Package Configuration.pmdoc/01altosui.xml +++ /dev/null @@ -1 +0,0 @@ -org.altusmetrum.altosUi.AltosUI.pkg0.7AltosUI.app/Applications/AltosUI.appinstallTo.pathinstallFrom.isRelativeTypeversionparentrequireAuthorizationinstallTo01altosui-contents.xml/CVS$/\.svn$/\.cvsignore$/\.cvspass$/\.DS_Store$ \ No newline at end of file diff --git a/ao-tools/altosui/AltOS Package Configuration.pmdoc/index.xml b/ao-tools/altosui/AltOS Package Configuration.pmdoc/index.xml deleted file mode 100644 index fabe54a6..00000000 --- a/ao-tools/altosui/AltOS Package Configuration.pmdoc/index.xml +++ /dev/null @@ -1 +0,0 @@ -AltOS UI/Users/keithp/altos/ao-tools/altosui/AltosUI.pkgorg.altusmetrumInstall AltOS User Interfacealtusmetrum.jpg01altosui.xmlproperties.anywhereDomainproperties.titleproperties.customizeOptiondescriptionproperties.userDomainproperties.systemDomain \ No newline at end of file diff --git a/ao-tools/altosui/Altos.java b/ao-tools/altosui/Altos.java deleted file mode 100644 index 8ee94e04..00000000 --- a/ao-tools/altosui/Altos.java +++ /dev/null @@ -1,218 +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.util.*; -import java.text.*; - -public class Altos { - /* EEProm command letters */ - static final int AO_LOG_FLIGHT = 'F'; - static final int AO_LOG_SENSOR = 'A'; - static final int AO_LOG_TEMP_VOLT = 'T'; - static final int AO_LOG_DEPLOY = 'D'; - static final int AO_LOG_STATE = 'S'; - static final int AO_LOG_GPS_TIME = 'G'; - static final int AO_LOG_GPS_LAT = 'N'; - static final int AO_LOG_GPS_LON = 'W'; - static final int AO_LOG_GPS_ALT = 'H'; - static final int AO_LOG_GPS_SAT = 'V'; - static final int AO_LOG_GPS_DATE = 'Y'; - - /* Added for header fields in eeprom files */ - static final int AO_LOG_CONFIG_VERSION = 1000; - static final int AO_LOG_MAIN_DEPLOY = 1001; - static final int AO_LOG_APOGEE_DELAY = 1002; - static final int AO_LOG_RADIO_CHANNEL = 1003; - static final int AO_LOG_CALLSIGN = 1004; - static final int AO_LOG_ACCEL_CAL = 1005; - static final int AO_LOG_RADIO_CAL = 1006; - static final int AO_LOG_MANUFACTURER = 1007; - static final int AO_LOG_PRODUCT = 1008; - static final int AO_LOG_SERIAL_NUMBER = 1009; - static final int AO_LOG_SOFTWARE_VERSION = 1010; - - /* Added to flag invalid records */ - static final int AO_LOG_INVALID = -1; - - /* Flight state numbers and names */ - static final int ao_flight_startup = 0; - static final int ao_flight_idle = 1; - static final int ao_flight_pad = 2; - static final int ao_flight_boost = 3; - static final int ao_flight_fast = 4; - static final int ao_flight_coast = 5; - static final int ao_flight_drogue = 6; - static final int ao_flight_main = 7; - static final int ao_flight_landed = 8; - static final int ao_flight_invalid = 9; - - static HashMap string_to_state = new HashMap(); - - static boolean map_initialized = false; - - static final int tab_elt_pad = 5; - - static final Font label_font = new Font("Dialog", Font.PLAIN, 22); - static final Font value_font = new Font("Monospaced", Font.PLAIN, 22); - static final Font status_font = new Font("SansSerif", Font.BOLD, 24); - - static final int text_width = 16; - - static void initialize_map() - { - string_to_state.put("startup", ao_flight_startup); - string_to_state.put("idle", ao_flight_idle); - string_to_state.put("pad", ao_flight_pad); - string_to_state.put("boost", ao_flight_boost); - string_to_state.put("fast", ao_flight_fast); - string_to_state.put("coast", ao_flight_coast); - string_to_state.put("drogue", ao_flight_drogue); - string_to_state.put("main", ao_flight_main); - string_to_state.put("landed", ao_flight_landed); - string_to_state.put("invalid", ao_flight_invalid); - map_initialized = true; - } - - static String[] state_to_string = { - "startup", - "idle", - "pad", - "boost", - "fast", - "coast", - "drogue", - "main", - "landed", - "invalid", - }; - - static public int state(String state) { - if (!map_initialized) - initialize_map(); - if (string_to_state.containsKey(state)) - return string_to_state.get(state); - return ao_flight_invalid; - } - - static public String state_name(int state) { - if (state < 0 || state_to_string.length <= state) - return "invalid"; - return state_to_string[state]; - } - - static final int AO_GPS_VALID = (1 << 4); - static final int AO_GPS_RUNNING = (1 << 5); - static final int AO_GPS_DATE_VALID = (1 << 6); - static final int AO_GPS_NUM_SAT_SHIFT = 0; - static final int AO_GPS_NUM_SAT_MASK = 0xf; - - static boolean isspace(int c) { - switch (c) { - case ' ': - case '\t': - return true; - } - return false; - } - - static boolean ishex(int c) { - if ('0' <= c && c <= '9') - return true; - if ('a' <= c && c <= 'f') - return true; - if ('A' <= c && c <= 'F') - return true; - return false; - } - - static boolean ishex(String s) { - for (int i = 0; i < s.length(); i++) - if (!ishex(s.charAt(i))) - return false; - return true; - } - - static int fromhex(int c) { - if ('0' <= c && c <= '9') - return c - '0'; - if ('a' <= c && c <= 'f') - return c - 'a' + 10; - if ('A' <= c && c <= 'F') - return c - 'A' + 10; - return -1; - } - - static int fromhex(String s) throws NumberFormatException { - int c, v = 0; - for (int i = 0; i < s.length(); i++) { - c = s.charAt(i); - if (!ishex(c)) { - if (i == 0) - throw new NumberFormatException(String.format("invalid hex \"%s\"", s)); - return v; - } - v = v * 16 + fromhex(c); - } - return v; - } - - static boolean isdec(int c) { - if ('0' <= c && c <= '9') - return true; - return false; - } - - static boolean isdec(String s) { - for (int i = 0; i < s.length(); i++) - if (!isdec(s.charAt(i))) - return false; - return true; - } - - static int fromdec(int c) { - if ('0' <= c && c <= '9') - return c - '0'; - return -1; - } - - static int fromdec(String s) throws NumberFormatException { - int c, v = 0; - int sign = 1; - for (int i = 0; i < s.length(); i++) { - c = s.charAt(i); - if (i == 0 && c == '-') { - sign = -1; - } else if (!isdec(c)) { - if (i == 0) - throw new NumberFormatException(String.format("invalid number \"%s\"", s)); - return v; - } else - v = v * 10 + fromdec(c); - } - return v * sign; - } - - static String replace_extension(String input, String extension) { - int dot = input.lastIndexOf("."); - if (dot > 0) - input = input.substring(0,dot); - return input.concat(extension); - } -} diff --git a/ao-tools/altosui/AltosAscent.java b/ao-tools/altosui/AltosAscent.java deleted file mode 100644 index 64bdcf30..00000000 --- a/ao-tools/altosui/AltosAscent.java +++ /dev/null @@ -1,335 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosAscent extends JComponent implements AltosFlightDisplay { - GridBagLayout layout; - - public class AscentStatus { - JLabel label; - JTextField value; - AltosLights lights; - - void show(AltosState state, int crc_errors) {} - void reset() { - value.setText(""); - lights.set(false); - } - - public AscentStatus (GridBagLayout layout, int y, String text) { - GridBagConstraints c = new GridBagConstraints(); - c.weighty = 1; - - lights = new AltosLights(); - c.gridx = 0; c.gridy = y; - c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.VERTICAL; - c.weightx = 0; - layout.setConstraints(lights, c); - add(lights); - - label = new JLabel(text); - label.setFont(Altos.label_font); - label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = 1; c.gridy = y; - c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.VERTICAL; - c.weightx = 0; - layout.setConstraints(label, c); - add(label); - - value = new JTextField(Altos.text_width); - value.setFont(Altos.value_font); - value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 2; c.gridy = y; - c.gridwidth = 2; - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - layout.setConstraints(value, c); - add(value); - - } - } - - public class AscentValue { - JLabel label; - JTextField value; - void show(AltosState state, int crc_errors) {} - - void reset() { - value.setText(""); - } - public AscentValue (GridBagLayout layout, int y, String text) { - GridBagConstraints c = new GridBagConstraints(); - c.weighty = 1; - - label = new JLabel(text); - label.setFont(Altos.label_font); - label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = 1; c.gridy = y; - c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.VERTICAL; - c.weightx = 0; - layout.setConstraints(label, c); - add(label); - - value = new JTextField(Altos.text_width); - value.setFont(Altos.value_font); - value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 2; c.gridy = y; - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.BOTH; - c.gridwidth = 2; - c.weightx = 1; - layout.setConstraints(value, c); - add(value); - } - } - - public class AscentValueHold { - JLabel label; - JTextField value; - JTextField max_value; - double max; - - void show(AltosState state, int crc_errors) {} - - void reset() { - value.setText(""); - max_value.setText(""); - max = 0; - } - - void show(String format, double v) { - value.setText(String.format(format, v)); - if (v > max) { - max_value.setText(String.format(format, v)); - max = v; - } - } - public AscentValueHold (GridBagLayout layout, int y, String text) { - GridBagConstraints c = new GridBagConstraints(); - c.weighty = 1; - - label = new JLabel(text); - label.setFont(Altos.label_font); - label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = 1; c.gridy = y; - c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.VERTICAL; - c.weightx = 0; - layout.setConstraints(label, c); - add(label); - - value = new JTextField(Altos.text_width); - value.setFont(Altos.value_font); - value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 2; c.gridy = y; - c.anchor = GridBagConstraints.EAST; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - layout.setConstraints(value, c); - add(value); - - max_value = new JTextField(Altos.text_width); - max_value.setFont(Altos.value_font); - max_value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 3; c.gridy = y; - c.anchor = GridBagConstraints.EAST; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - layout.setConstraints(max_value, c); - add(max_value); - } - } - - - class Height extends AscentValueHold { - void show (AltosState state, int crc_errors) { - show("%6.0f m", state.height); - } - public Height (GridBagLayout layout, int y) { - super (layout, y, "Height"); - } - } - - Height height; - - class Speed extends AscentValueHold { - void show (AltosState state, int crc_errors) { - double speed = state.speed; - if (!state.ascent) - speed = state.baro_speed; - show("%6.0f m/s", speed); - } - public Speed (GridBagLayout layout, int y) { - super (layout, y, "Speed"); - } - } - - Speed speed; - - class Accel extends AscentValueHold { - void show (AltosState state, int crc_errors) { - show("%6.0f m/s²", state.acceleration); - } - public Accel (GridBagLayout layout, int y) { - super (layout, y, "Acceleration"); - } - } - - Accel accel; - - String pos(double p, String pos, String neg) { - String h = pos; - if (p < 0) { - h = neg; - p = -p; - } - int deg = (int) Math.floor(p); - double min = (p - Math.floor(p)) * 60.0; - return String.format("%s %4d° %9.6f", h, deg, min); - } - - class Apogee extends AscentStatus { - void show (AltosState state, int crc_errors) { - value.setText(String.format("%4.2f V", state.drogue_sense)); - lights.set(state.drogue_sense > 3.2); - } - public Apogee (GridBagLayout layout, int y) { - super(layout, y, "Apogee Igniter Voltage"); - } - } - - Apogee apogee; - - class Main extends AscentStatus { - void show (AltosState state, int crc_errors) { - value.setText(String.format("%4.2f V", state.main_sense)); - lights.set(state.main_sense > 3.2); - } - public Main (GridBagLayout layout, int y) { - super(layout, y, "Main Igniter Voltage"); - } - } - - Main main; - - class Lat extends AscentValue { - void show (AltosState state, int crc_errors) { - if (state.gps != null) - value.setText(pos(state.gps.lat,"N", "S")); - else - value.setText("???"); - } - public Lat (GridBagLayout layout, int y) { - super (layout, y, "Latitude"); - } - } - - Lat lat; - - class Lon extends AscentValue { - void show (AltosState state, int crc_errors) { - if (state.gps != null) - value.setText(pos(state.gps.lon,"E", "W")); - else - value.setText("???"); - } - public Lon (GridBagLayout layout, int y) { - super (layout, y, "Longitude"); - } - } - - Lon lon; - - public void reset() { - lat.reset(); - lon.reset(); - main.reset(); - apogee.reset(); - height.reset(); - speed.reset(); - accel.reset(); - } - - public void show(AltosState state, int crc_errors) { - lat.show(state, crc_errors); - lon.show(state, crc_errors); - height.show(state, crc_errors); - main.show(state, crc_errors); - apogee.show(state, crc_errors); - speed.show(state, crc_errors); - accel.show(state, crc_errors); - } - - public void labels(GridBagLayout layout, int y) { - GridBagConstraints c; - JLabel cur, max; - - cur = new JLabel("Current"); - cur.setFont(Altos.label_font); - c = new GridBagConstraints(); - c.gridx = 2; c.gridy = y; - c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); - layout.setConstraints(cur, c); - add(cur); - - max = new JLabel("Maximum"); - max.setFont(Altos.label_font); - c.gridx = 3; c.gridy = y; - layout.setConstraints(max, c); - add(max); - } - - public AltosAscent() { - layout = new GridBagLayout(); - - setLayout(layout); - - /* Elements in ascent display: - * - * lat - * lon - * height - */ - labels(layout, 0); - height = new Height(layout, 1); - speed = new Speed(layout, 2); - accel = new Accel(layout, 3); - lat = new Lat(layout, 4); - lon = new Lon(layout, 5); - apogee = new Apogee(layout, 6); - main = new Main(layout, 7); - } -} diff --git a/ao-tools/altosui/AltosCRCException.java b/ao-tools/altosui/AltosCRCException.java deleted file mode 100644 index 4a529bcf..00000000 --- a/ao-tools/altosui/AltosCRCException.java +++ /dev/null @@ -1,26 +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; - -public class AltosCRCException extends Exception { - public int rssi; - - public AltosCRCException (int in_rssi) { - rssi = in_rssi; - } -} diff --git a/ao-tools/altosui/AltosCSV.java b/ao-tools/altosui/AltosCSV.java deleted file mode 100644 index df98b2b4..00000000 --- a/ao-tools/altosui/AltosCSV.java +++ /dev/null @@ -1,252 +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.lang.*; -import java.io.*; -import java.text.*; -import java.util.*; - -public class AltosCSV implements AltosWriter { - File name; - PrintStream out; - boolean header_written; - boolean seen_boost; - int boost_tick; - LinkedList pad_records; - AltosState state; - - static final int ALTOS_CSV_VERSION = 2; - - /* Version 2 format: - * - * General info - * version number - * serial number - * flight number - * callsign - * time (seconds since boost) - * rssi - * link quality - * - * Flight status - * state - * state name - * - * Basic sensors - * acceleration (m/s²) - * pressure (mBar) - * altitude (m) - * height (m) - * accelerometer speed (m/s) - * barometer speed (m/s) - * temp (°C) - * battery (V) - * drogue (V) - * main (V) - * - * GPS data - * connected (1/0) - * locked (1/0) - * nsat (used for solution) - * latitude (°) - * longitude (°) - * altitude (m) - * year (e.g. 2010) - * month (1-12) - * day (1-31) - * hour (0-23) - * minute (0-59) - * second (0-59) - * from_pad_dist (m) - * from_pad_azimuth (deg true) - * from_pad_range (m) - * from_pad_elevation (deg from horizon) - * hdop - * - * GPS Sat data - * C/N0 data for all 32 valid SDIDs - */ - - void write_general_header() { - out.printf("version,serial,flight,call,time,rssi,lqi"); - } - - void write_general(AltosRecord record) { - out.printf("%s, %d, %d, %s, %8.2f, %4d, %3d", - ALTOS_CSV_VERSION, record.serial, record.flight, record.callsign, - (double) record.time, - record.rssi, - record.status & 0x7f); - } - - void write_flight_header() { - out.printf("state,state_name"); - } - - void write_flight(AltosRecord record) { - out.printf("%d,%8s", record.state, record.state()); - } - - void write_basic_header() { - out.printf("acceleration,pressure,altitude,height,accel_speed,baro_speed,temperature,battery_voltage,drogue_voltage,main_voltage"); - } - - void write_basic(AltosRecord record) { - out.printf("%8.2f,%10.2f,%8.2f,%8.2f,%8.2f,%8.2f,%5.1f,%5.2f,%5.2f,%5.2f", - record.acceleration(), - record.raw_pressure(), - record.raw_altitude(), - record.raw_height(), - record.accel_speed(), - state.baro_speed, - record.temperature(), - record.battery_voltage(), - record.drogue_voltage(), - record.main_voltage()); - } - - void write_gps_header() { - out.printf("connected,locked,nsat,latitude,longitude,altitude,year,month,day,hour,minute,second,pad_dist,pad_range,pad_az,pad_el,hdop"); - } - - void write_gps(AltosRecord record) { - AltosGPS gps = record.gps; - if (gps == null) - gps = new AltosGPS(); - - AltosGreatCircle from_pad = state.from_pad; - if (from_pad == null) - from_pad = new AltosGreatCircle(); - - out.printf("%2d,%2d,%3d,%12.7f,%12.7f,%6d,%5d,%3d,%3d,%3d,%3d,%3d,%9.0f,%9.0f,%4.0f,%4.0f,%6.1f", - gps.connected?1:0, - gps.locked?1:0, - gps.nsat, - gps.lat, - gps.lon, - gps.alt, - gps.year, - gps.month, - gps.day, - gps.hour, - gps.minute, - gps.second, - from_pad.distance, - state.range, - from_pad.bearing, - state.elevation, - gps.hdop); - } - - void write_gps_sat_header() { - for(int i = 1; i <= 32; i++) { - out.printf("sat%02d", i); - if (i != 32) - out.printf(","); - } - } - - void write_gps_sat(AltosRecord record) { - AltosGPS gps = record.gps; - for(int i = 1; i <= 32; i++) { - int c_n0 = 0; - if (gps != null && gps.cc_gps_sat != null) { - for(int j = 0; j < gps.cc_gps_sat.length; j++) - if (gps.cc_gps_sat[j].svid == i) { - c_n0 = gps.cc_gps_sat[j].c_n0; - break; - } - } - out.printf ("%3d", c_n0); - if (i != 32) - out.printf(","); - } - } - - void write_header() { - out.printf("#"); write_general_header(); - out.printf(","); write_flight_header(); - out.printf(","); write_basic_header(); - out.printf(","); write_gps_header(); - out.printf(","); write_gps_sat_header(); - out.printf ("\n"); - } - - void write_one(AltosRecord record) { - state = new AltosState(record, state); - write_general(record); out.printf(","); - write_flight(record); out.printf(","); - write_basic(record); out.printf(","); - write_gps(record); out.printf(","); - write_gps_sat(record); - out.printf ("\n"); - } - - void flush_pad() { - while (!pad_records.isEmpty()) { - write_one (pad_records.remove()); - } - } - - public void write(AltosRecord record) { - if (!header_written) { - write_header(); - header_written = true; - } - if (!seen_boost) { - if (record.state >= Altos.ao_flight_boost) { - seen_boost = true; - boost_tick = record.tick; - flush_pad(); - } - } - if (seen_boost) - write_one(record); - else - pad_records.add(record); - } - - public PrintStream out() { - return out; - } - - public void close() { - if (!pad_records.isEmpty()) { - boost_tick = pad_records.element().tick; - flush_pad(); - } - out.close(); - } - - public void write(AltosRecordIterable iterable) { - iterable.write_comments(out()); - for (AltosRecord r : iterable) - write(r); - } - - public AltosCSV(File in_name) throws FileNotFoundException { - name = in_name; - out = new PrintStream(name); - pad_records = new LinkedList(); - } - - public AltosCSV(String in_string) throws FileNotFoundException { - this(new File(in_string)); - } -} diff --git a/ao-tools/altosui/AltosCSVUI.java b/ao-tools/altosui/AltosCSVUI.java deleted file mode 100644 index e1b6002d..00000000 --- a/ao-tools/altosui/AltosCSVUI.java +++ /dev/null @@ -1,108 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosCSVUI - extends JDialog - implements ActionListener -{ - JFileChooser csv_chooser; - JPanel accessory; - JComboBox combo_box; - AltosRecordIterable iterable; - AltosWriter writer; - - static String[] combo_box_items = { "Comma Separated Values (.CSV)", "Googleearth Data (.KML)" }; - - void set_default_file() { - File current = csv_chooser.getSelectedFile(); - String current_name = current.getName(); - String new_name = null; - String selected = (String) combo_box.getSelectedItem(); - - if (selected.contains("CSV")) - new_name = Altos.replace_extension(current_name, ".csv"); - 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 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()); - - 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); - - combo_box = new JComboBox(combo_box_items); - combo_box.addActionListener(this); - 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(); - try { - 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); - } - } - } -} diff --git a/ao-tools/altosui/AltosChannelMenu.java b/ao-tools/altosui/AltosChannelMenu.java deleted file mode 100644 index abbb86f4..00000000 --- a/ao-tools/altosui/AltosChannelMenu.java +++ /dev/null @@ -1,44 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosChannelMenu extends JComboBox implements ActionListener { - int channel; - - public AltosChannelMenu(int current_channel) { - - channel = current_channel; - - 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 deleted file mode 100644 index 1c42870f..00000000 --- a/ao-tools/altosui/AltosConfig.java +++ /dev/null @@ -1,295 +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.*; -import java.util.concurrent.*; - -import libaltosJNI.*; - -public class AltosConfig implements Runnable, ActionListener { - - class int_ref { - int value; - - public int get() { - return value; - } - public void set(int i) { - value = i; - } - public int_ref(int i) { - value = i; - } - } - - class string_ref { - String value; - - public String get() { - return value; - } - public void set(String i) { - value = i; - } - public string_ref(String i) { - value = i; - } - } - - JFrame owner; - AltosDevice device; - AltosSerial serial_line; - boolean remote; - Thread config_thread; - int_ref serial; - int_ref main_deploy; - int_ref apogee_delay; - int_ref radio_channel; - int_ref radio_calibration; - string_ref version; - string_ref product; - string_ref callsign; - AltosConfigUI config_ui; - boolean serial_started; - - boolean get_int(String line, String label, int_ref x) { - if (line.startsWith(label)) { - try { - String tail = line.substring(label.length()).trim(); - String[] tokens = tail.split("\\s+"); - if (tokens.length > 0) { - int i = Integer.parseInt(tokens[0]); - x.set(i); - return true; - } - } catch (NumberFormatException ne) { - } - } - return false; - } - - 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; - } - } - - void start_serial() throws InterruptedException { - serial_started = true; - if (remote) { - serial_line.set_radio(); - serial_line.printf("p\nE 0\n"); - serial_line.flush_input(); - } - } - - void stop_serial() throws InterruptedException { - if (!serial_started) - return; - serial_started = false; - if (remote) { - serial_line.printf("~"); - serial_line.flush_output(); - } - } - - void get_data() throws InterruptedException, TimeoutException { - try { - start_serial(); - serial_line.printf("c s\nv\n"); - for (;;) { - String line = serial_line.get_reply(5000); - 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); - get_int(line, "Radio channel:", radio_channel); - get_int(line, "Radio cal:", radio_calibration); - get_string(line, "Callsign:", callsign); - get_string(line,"software-version", version); - get_string(line,"product", product); - - /* signals the end of the version info */ - if (line.startsWith("software-version")) - break; - } - } finally { - stop_serial(); - } - } - - void init_ui () throws InterruptedException, TimeoutException { - config_ui = new AltosConfigUI(owner, remote); - config_ui.addActionListener(this); - set_ui(); - } - - void abort() { - JOptionPane.showMessageDialog(owner, - String.format("Connection to \"%s\" failed", - device.toShortString()), - "Connection Failed", - JOptionPane.ERROR_MESSAGE); - try { - stop_serial(); - } catch (InterruptedException ie) { - } - 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() { - } - - void save_data() { - main_deploy.set(config_ui.main_deploy()); - apogee_delay.set(config_ui.apogee_delay()); - radio_channel.set(config_ui.radio_channel()); - radio_calibration.set(config_ui.radio_calibration()); - callsign.set(config_ui.callsign()); - try { - start_serial(); - serial_line.printf("c m %d\n", main_deploy.get()); - serial_line.printf("c d %d\n", apogee_delay.get()); - if (!remote) { - serial_line.printf("c r %d\n", radio_channel.get()); - serial_line.printf("c f %d\n", radio_calibration.get()); - } - serial_line.printf("c c %s\n", callsign.get()); - serial_line.printf("c w\n"); - } catch (InterruptedException ie) { - } finally { - try { - stop_serial(); - } catch (InterruptedException ie) { - } - } - } - - public void actionPerformed(ActionEvent e) { - String cmd = e.getActionCommand(); - 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"); - serial_line.flush_output(); - stop_serial(); - serial_line.close(); - } - } else if (cmd.equals("Close")) { - if (serial_line != null) - serial_line.close(); - } - } catch (InterruptedException ie) { - abort(); - } catch (TimeoutException te) { - abort(); - } - } - - public void run () { - try { - init_ui(); - config_ui.make_visible(); - } catch (InterruptedException ie) { - abort(); - } catch (TimeoutException te) { - abort(); - } - } - - public AltosConfig(JFrame given_owner) { - owner = given_owner; - - serial = new int_ref(0); - main_deploy = new int_ref(250); - apogee_delay = new int_ref(0); - radio_channel = new int_ref(0); - radio_calibration = new int_ref(1186611); - callsign = new string_ref("N0CALL"); - version = new string_ref("unknown"); - product = new string_ref("unknown"); - - device = AltosDeviceDialog.show(owner, AltosDevice.product_any); - if (device != null) { - try { - serial_line = new AltosSerial(device); - if (!device.matchProduct(AltosDevice.product_telemetrum)) - remote = true; - config_thread = new Thread(this); - config_thread.start(); - } catch (FileNotFoundException ee) { - JOptionPane.showMessageDialog(owner, - String.format("Cannot open device \"%s\"", - device.toShortString()), - "Cannot open target device", - JOptionPane.ERROR_MESSAGE); - } catch (AltosSerialInUseException si) { - JOptionPane.showMessageDialog(owner, - String.format("Device \"%s\" already in use", - device.toShortString()), - "Device in use", - JOptionPane.ERROR_MESSAGE); - } catch (IOException ee) { - JOptionPane.showMessageDialog(owner, - device.toShortString(), - ee.getLocalizedMessage(), - JOptionPane.ERROR_MESSAGE); - } - } - } -} \ No newline at end of file diff --git a/ao-tools/altosui/AltosConfigUI.java b/ao-tools/altosui/AltosConfigUI.java deleted file mode 100644 index cfa5d7b9..00000000 --- a/ao-tools/altosui/AltosConfigUI.java +++ /dev/null @@ -1,466 +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 javax.swing.event.*; -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.util.concurrent.LinkedBlockingQueue; - -import libaltosJNI.*; - -public class AltosConfigUI - extends JDialog - implements ActionListener, ItemListener, DocumentListener -{ - - Container pane; - Box box; - JLabel product_label; - JLabel version_label; - JLabel serial_label; - JLabel main_deploy_label; - JLabel apogee_delay_label; - JLabel radio_channel_label; - JLabel radio_calibration_label; - JLabel callsign_label; - - public boolean dirty; - - JFrame owner; - JLabel product_value; - JLabel version_value; - JLabel serial_value; - JComboBox main_deploy_value; - JComboBox apogee_delay_value; - JComboBox radio_channel_value; - JTextField radio_calibration_value; - JTextField callsign_value; - - JButton save; - JButton reset; - JButton reboot; - JButton close; - - ActionListener listener; - - static String[] main_deploy_values = { - "100", "150", "200", "250", "300", "350", - "400", "450", "500" - }; - - static String[] apogee_delay_values = { - "0", "1", "2", "3", "4", "5" - }; - - static String[] radio_channel_values = new String[10]; - { - for (int i = 0; i <= 9; i++) - radio_channel_values[i] = String.format("Channel %1d (%7.3fMHz)", - i, 434.550 + i * 0.1); - } - - /* A window listener to catch closing events and tell the config code */ - class ConfigListener extends WindowAdapter { - AltosConfigUI ui; - - public ConfigListener(AltosConfigUI this_ui) { - ui = this_ui; - } - - public void windowClosing(WindowEvent e) { - ui.actionPerformed(new ActionEvent(e.getSource(), - ActionEvent.ACTION_PERFORMED, - "Close")); - } - } - - /* Build the UI using a grid bag */ - public AltosConfigUI(JFrame in_owner, boolean remote) { - super (in_owner, "Configure TeleMetrum", false); - - owner = in_owner; - GridBagConstraints c; - - Insets il = new Insets(4,4,4,4); - Insets ir = new Insets(4,4,4,4); - - pane = getContentPane(); - pane.setLayout(new GridBagLayout()); - - /* Product */ - c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 0; - c.gridwidth = 4; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - product_label = new JLabel("Product:"); - pane.add(product_label, c); - - c = new GridBagConstraints(); - c.gridx = 4; c.gridy = 0; - c.gridwidth = 4; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - product_value = new JLabel(""); - pane.add(product_value, c); - - /* Version */ - c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 1; - c.gridwidth = 4; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - c.ipady = 5; - version_label = new JLabel("Software version:"); - pane.add(version_label, c); - - c = new GridBagConstraints(); - c.gridx = 4; c.gridy = 1; - c.gridwidth = 4; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - c.ipady = 5; - version_value = new JLabel(""); - pane.add(version_value, c); - - /* Serial */ - c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 2; - c.gridwidth = 4; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - c.ipady = 5; - serial_label = new JLabel("Serial:"); - pane.add(serial_label, c); - - c = new GridBagConstraints(); - c.gridx = 4; c.gridy = 2; - c.gridwidth = 4; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - c.ipady = 5; - serial_value = new JLabel(""); - pane.add(serial_value, c); - - /* Main deploy */ - c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 3; - c.gridwidth = 4; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - c.ipady = 5; - main_deploy_label = new JLabel("Main Deploy Altitude(m):"); - pane.add(main_deploy_label, c); - - c = new GridBagConstraints(); - c.gridx = 4; c.gridy = 3; - c.gridwidth = 4; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - c.ipady = 5; - main_deploy_value = new JComboBox(main_deploy_values); - main_deploy_value.setEditable(true); - main_deploy_value.addItemListener(this); - pane.add(main_deploy_value, c); - - /* Apogee delay */ - c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 4; - c.gridwidth = 4; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - c.ipady = 5; - apogee_delay_label = new JLabel("Apogee Delay(s):"); - pane.add(apogee_delay_label, c); - - c = new GridBagConstraints(); - c.gridx = 4; c.gridy = 4; - c.gridwidth = 4; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - c.ipady = 5; - apogee_delay_value = new JComboBox(apogee_delay_values); - apogee_delay_value.setEditable(true); - apogee_delay_value.addItemListener(this); - pane.add(apogee_delay_value, c); - - /* Radio channel */ - c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 5; - c.gridwidth = 4; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - c.ipady = 5; - radio_channel_label = new JLabel("Radio Channel:"); - pane.add(radio_channel_label, c); - - c = new GridBagConstraints(); - c.gridx = 4; c.gridy = 5; - c.gridwidth = 4; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - c.ipady = 5; - radio_channel_value = new JComboBox(radio_channel_values); - radio_channel_value.setEditable(false); - radio_channel_value.addItemListener(this); - if (remote) - radio_channel_value.setEnabled(false); - pane.add(radio_channel_value, c); - - /* Radio Calibration */ - c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 6; - c.gridwidth = 4; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - c.ipady = 5; - radio_calibration_label = new JLabel("RF Calibration:"); - pane.add(radio_calibration_label, c); - - c = new GridBagConstraints(); - c.gridx = 4; c.gridy = 6; - c.gridwidth = 4; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - c.ipady = 5; - radio_calibration_value = new JTextField(String.format("%d", 1186611)); - radio_calibration_value.getDocument().addDocumentListener(this); - if (remote) - radio_calibration_value.setEnabled(false); - pane.add(radio_calibration_value, c); - - /* Callsign */ - c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 7; - c.gridwidth = 4; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - c.ipady = 5; - callsign_label = new JLabel("Callsign:"); - pane.add(callsign_label, c); - - c = new GridBagConstraints(); - c.gridx = 4; c.gridy = 7; - c.gridwidth = 4; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - c.ipady = 5; - callsign_value = new JTextField(AltosPreferences.callsign()); - callsign_value.getDocument().addDocumentListener(this); - pane.add(callsign_value, c); - - /* Buttons */ - c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 8; - c.gridwidth = 2; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - save = new JButton("Save"); - pane.add(save, c); - save.addActionListener(this); - save.setActionCommand("Save"); - - c = new GridBagConstraints(); - c.gridx = 2; c.gridy = 8; - c.gridwidth = 2; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.CENTER; - c.insets = il; - reset = new JButton("Reset"); - pane.add(reset, c); - reset.addActionListener(this); - reset.setActionCommand("Reset"); - - c = new GridBagConstraints(); - c.gridx = 4; c.gridy = 8; - c.gridwidth = 2; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.CENTER; - c.insets = il; - reboot = new JButton("Reboot"); - pane.add(reboot, c); - reboot.addActionListener(this); - reboot.setActionCommand("Reboot"); - - c = new GridBagConstraints(); - c.gridx = 6; c.gridy = 8; - c.gridwidth = 2; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.LINE_END; - c.insets = il; - close = new JButton("Close"); - pane.add(close, c); - close.addActionListener(this); - close.setActionCommand("Close"); - - addWindowListener(new ConfigListener(this)); - } - - /* Once the initial values are set, the config code will show the dialog */ - public void make_visible() { - pack(); - setLocationRelativeTo(owner); - setVisible(true); - } - - /* If any values have been changed, confirm before closing */ - public boolean check_dirty(String operation) { - if (dirty) { - Object[] options = { String.format("%s anyway", operation), "Keep editing" }; - int i; - i = JOptionPane.showOptionDialog(this, - String.format("Configuration modified. %s anyway?", operation), - "Configuration Modified", - JOptionPane.DEFAULT_OPTION, - JOptionPane.WARNING_MESSAGE, - null, options, options[1]); - if (i != 0) - return false; - } - return true; - } - - /* Listen for events from our buttons */ - public void actionPerformed(ActionEvent e) { - String cmd = e.getActionCommand(); - - if (cmd.equals("Close") || cmd.equals("Reboot")) - if (!check_dirty(cmd)) - return; - listener.actionPerformed(e); - if (cmd.equals("Close") || cmd.equals("Reboot")) { - setVisible(false); - dispose(); - } - dirty = false; - } - - /* ItemListener interface method */ - public void itemStateChanged(ItemEvent e) { - dirty = true; - } - - /* DocumentListener interface methods */ - public void changedUpdate(DocumentEvent e) { - dirty = true; - } - - public void insertUpdate(DocumentEvent e) { - dirty = true; - } - - public void removeUpdate(DocumentEvent e) { - dirty = true; - } - - /* Let the config code hook on a listener */ - public void addActionListener(ActionListener l) { - listener = l; - } - - /* set and get all of the dialog values */ - public void set_product(String product) { - product_value.setText(product); - } - - public void set_version(String version) { - version_value.setText(version); - } - - public void set_serial(int serial) { - serial_value.setText(String.format("%d", serial)); - } - - public void set_main_deploy(int new_main_deploy) { - main_deploy_value.setSelectedItem(Integer.toString(new_main_deploy)); - } - - public int main_deploy() { - return Integer.parseInt(main_deploy_value.getSelectedItem().toString()); - } - - public void set_apogee_delay(int new_apogee_delay) { - apogee_delay_value.setSelectedItem(Integer.toString(new_apogee_delay)); - } - - public int apogee_delay() { - return Integer.parseInt(apogee_delay_value.getSelectedItem().toString()); - } - - public void set_radio_channel(int new_radio_channel) { - radio_channel_value.setSelectedIndex(new_radio_channel); - } - - public int radio_channel() { - return radio_channel_value.getSelectedIndex(); - } - - public void set_radio_calibration(int new_radio_calibration) { - radio_calibration_value.setText(String.format("%d", new_radio_calibration)); - } - - public int radio_calibration() { - return Integer.parseInt(radio_calibration_value.getText()); - } - - public void set_callsign(String new_callsign) { - callsign_value.setText(new_callsign); - } - - public String callsign() { - return callsign_value.getText(); - } - - public void set_clean() { - dirty = false; - } - - } \ No newline at end of file diff --git a/ao-tools/altosui/AltosConfigureUI.java b/ao-tools/altosui/AltosConfigureUI.java deleted file mode 100644 index 153c59fd..00000000 --- a/ao-tools/altosui/AltosConfigureUI.java +++ /dev/null @@ -1,187 +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 javax.swing.event.*; -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosConfigureUI - extends JDialog - implements DocumentListener -{ - JFrame owner; - AltosVoice voice; - Container pane; - - JRadioButton enable_voice; - JButton test_voice; - JButton close; - - JButton configure_log; - JTextField log_directory; - - JLabel callsign_label; - JTextField callsign_value; - - /* DocumentListener interface methods */ - public void changedUpdate(DocumentEvent e) { - AltosPreferences.set_callsign(callsign_value.getText()); - } - - public void insertUpdate(DocumentEvent e) { - changedUpdate(e); - } - - public void removeUpdate(DocumentEvent e) { - changedUpdate(e); - } - - public AltosConfigureUI(JFrame in_owner, AltosVoice in_voice) { - super(in_owner, "Configure AltosUI", false); - - GridBagConstraints c; - - Insets insets = new Insets(4, 4, 4, 4); - - owner = in_owner; - voice = in_voice; - pane = getContentPane(); - pane.setLayout(new GridBagLayout()); - - c = new GridBagConstraints(); - c.insets = insets; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.WEST; - - /* Nice label at the top */ - c.gridx = 0; - c.gridy = 0; - 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(); - boolean enabled = item.isSelected(); - AltosPreferences.set_voice(enabled); - if (enabled) - voice.speak_always("Enable voice."); - else - voice.speak_always("Disable voice."); - } - }); - c.gridx = 1; - 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) { - voice.speak("That's one small step for man; one giant leap for mankind."); - } - }); - pane.add(test_voice, c); - - /* 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(); - configure_log.setText(AltosPreferences.logdir().getPath()); - } - }); - c.gridx = 1; - c.gridy = 2; - c.gridwidth = 2; - c.fill = GridBagConstraints.BOTH; - c.anchor = GridBagConstraints.WEST; - pane.add(configure_log, c); - - /* Callsign setting */ - c.gridx = 0; - c.gridy = 3; - 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) { - setVisible(false); - } - }); - c.gridx = 0; - c.gridy = 4; - c.gridwidth = 3; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.CENTER; - pane.add(close, c); - - pack(); - setLocationRelativeTo(owner); - setVisible(true); - } -} diff --git a/ao-tools/altosui/AltosConvert.java b/ao-tools/altosui/AltosConvert.java deleted file mode 100644 index 8cc1df27..00000000 --- a/ao-tools/altosui/AltosConvert.java +++ /dev/null @@ -1,192 +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. - */ - -/* - * Sensor data conversion functions - */ -package altosui; - -public class AltosConvert { - /* - * Pressure Sensor Model, version 1.1 - * - * written by Holly Grimes - * - * Uses the International Standard Atmosphere as described in - * "A Quick Derivation relating altitude to air pressure" (version 1.03) - * from the Portland State Aerospace Society, except that the atmosphere - * is divided into layers with each layer having a different lapse rate. - * - * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007 - * at site MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */ - return 0; - - /* calculate the base temperature and pressure for the atmospheric layer - associated with the inputted altitude */ - for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) { - delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; - if (lapse_rate[layer_number] == 0.0) { - exponent = GRAVITATIONAL_ACCELERATION * delta_z - / AIR_GAS_CONSTANT / base_temperature; - base_pressure *= Math.exp(exponent); - } - else { - base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; - exponent = GRAVITATIONAL_ACCELERATION / - (AIR_GAS_CONSTANT * lapse_rate[layer_number]); - base_pressure *= Math.pow(base, exponent); - } - base_temperature += delta_z * lapse_rate[layer_number]; - } - - /* calculate the pressure at the inputted altitude */ - delta_z = altitude - base_altitude[layer_number]; - if (lapse_rate[layer_number] == 0.0) { - exponent = GRAVITATIONAL_ACCELERATION * delta_z - / AIR_GAS_CONSTANT / base_temperature; - pressure = base_pressure * Math.exp(exponent); - } - else { - base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; - exponent = GRAVITATIONAL_ACCELERATION / - (AIR_GAS_CONSTANT * lapse_rate[layer_number]); - pressure = base_pressure * Math.pow(base, exponent); - } - - return pressure; - } - - -/* outputs the altitude associated with the given pressure. the altitude - returned is measured with respect to the mean sea level */ - static double - pressure_to_altitude(double pressure) - { - - double next_base_temperature = LAYER0_BASE_TEMPERATURE; - double next_base_pressure = LAYER0_BASE_PRESSURE; - - double altitude; - double base_pressure; - double base_temperature; - double base; /* base for function to determine base pressure of next layer */ - double exponent; /* exponent for function to determine base pressure - of next layer */ - double coefficient; - int layer_number; /* identifies layer in the atmosphere */ - int delta_z; /* difference between two altitudes */ - - if (pressure < 0) /* illegal pressure */ - return -1; - if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */ - return MAXIMUM_ALTITUDE; - - /* calculate the base temperature and pressure for the atmospheric layer - associated with the inputted pressure. */ - layer_number = -1; - do { - layer_number++; - base_pressure = next_base_pressure; - base_temperature = next_base_temperature; - delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; - if (lapse_rate[layer_number] == 0.0) { - exponent = GRAVITATIONAL_ACCELERATION * delta_z - / AIR_GAS_CONSTANT / base_temperature; - next_base_pressure *= Math.exp(exponent); - } - else { - base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; - exponent = GRAVITATIONAL_ACCELERATION / - (AIR_GAS_CONSTANT * lapse_rate[layer_number]); - next_base_pressure *= Math.pow(base, exponent); - } - next_base_temperature += delta_z * lapse_rate[layer_number]; - } - while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure); - - /* calculate the altitude associated with the inputted pressure */ - if (lapse_rate[layer_number] == 0.0) { - coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION) - * base_temperature; - altitude = base_altitude[layer_number] - + coefficient * Math.log(pressure / base_pressure); - } - else { - base = pressure / base_pressure; - exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number] - / GRAVITATIONAL_ACCELERATION; - coefficient = base_temperature / lapse_rate[layer_number]; - altitude = base_altitude[layer_number] - + coefficient * (Math.pow(base, exponent) - 1); - } - - return altitude; - } - - static double - cc_battery_to_voltage(double battery) - { - return battery / 32767.0 * 5.0; - } - - static double - cc_ignitor_to_voltage(double ignite) - { - return ignite / 32767 * 15.0; - } -} diff --git a/ao-tools/altosui/AltosDataChooser.java b/ao-tools/altosui/AltosDataChooser.java deleted file mode 100644 index 15de05c2..00000000 --- a/ao-tools/altosui/AltosDataChooser.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 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/AltosDataPoint.java b/ao-tools/altosui/AltosDataPoint.java deleted file mode 100644 index 66313e03..00000000 --- a/ao-tools/altosui/AltosDataPoint.java +++ /dev/null @@ -1,29 +0,0 @@ - -// Copyright (c) 2010 Anthony Towns -// GPL v2 or later - -package altosui; - -interface AltosDataPoint { - int version(); - int serial(); - int flight(); - String callsign(); - double time(); - double rssi(); - - int state(); - String state_name(); - - double acceleration(); - double pressure(); - double altitude(); - double height(); - double accel_speed(); - double baro_speed(); - double temperature(); - double battery_voltage(); - double drogue_voltage(); - double main_voltage(); -} - diff --git a/ao-tools/altosui/AltosDataPointReader.java b/ao-tools/altosui/AltosDataPointReader.java deleted file mode 100644 index 7704310b..00000000 --- a/ao-tools/altosui/AltosDataPointReader.java +++ /dev/null @@ -1,72 +0,0 @@ - -// Copyright (c) 2010 Anthony Towns -// GPL v2 or later - -package altosui; - -import java.io.IOException; -import java.text.ParseException; -import java.lang.UnsupportedOperationException; -import java.util.NoSuchElementException; -import java.util.Iterator; - -class AltosDataPointReader implements Iterable { - Iterator iter; - AltosState state; - AltosRecord record; - - public AltosDataPointReader(Iterable reader) { - this.iter = reader.iterator(); - this.state = null; - } - - private void read_next_record() - throws NoSuchElementException - { - record = iter.next(); - state = new AltosState(record, state); - } - - private AltosDataPoint current_dp() { - assert this.record != null; - - return new AltosDataPoint() { - public int version() { return record.version; } - public int serial() { return record.serial; } - public int flight() { return record.flight; } - public String callsign() { return record.callsign; } - public double time() { return record.time; } - public double rssi() { return record.rssi; } - - public int state() { return record.state; } - public String state_name() { return record.state(); } - - public double acceleration() { return record.acceleration(); } - public double pressure() { return record.raw_pressure(); } - public double altitude() { return record.raw_altitude(); } - public double height() { return record.raw_height(); } - public double accel_speed() { return record.accel_speed(); } - public double baro_speed() { return state.baro_speed; } - public double temperature() { return record.temperature(); } - public double battery_voltage() { return record.battery_voltage(); } - public double drogue_voltage() { return record.drogue_voltage(); } - public double main_voltage() { return record.main_voltage(); } - }; - } - - public Iterator iterator() { - return new Iterator() { - public void remove() { - throw new UnsupportedOperationException(); - } - public boolean hasNext() { - return iter.hasNext(); - } - public AltosDataPoint next() { - read_next_record(); - return current_dp(); - } - }; - } -} - diff --git a/ao-tools/altosui/AltosDebug.java b/ao-tools/altosui/AltosDebug.java deleted file mode 100644 index 8d435b66..00000000 --- a/ao-tools/altosui/AltosDebug.java +++ /dev/null @@ -1,267 +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.lang.*; -import java.io.*; -import java.util.concurrent.*; -import java.util.*; - -import libaltosJNI.*; - -public class AltosDebug extends AltosSerial { - - public static final byte WR_CONFIG = 0x1d; - public static final byte RD_CONFIG = 0x24; - public static final byte CONFIG_TIMERS_OFF = (1 << 3); - public static final byte CONFIG_DMA_PAUSE = (1 << 2); - public static final byte CONFIG_TIMER_SUSPEND = (1 << 1); - public static final byte SET_FLASH_INFO_PAGE = (1 << 0); - - public static final byte GET_PC = 0x28; - public static final byte READ_STATUS = 0x34; - public static final byte STATUS_CHIP_ERASE_DONE = (byte) (1 << 7); - public static final byte STATUS_PCON_IDLE = (1 << 6); - public static final byte STATUS_CPU_HALTED = (1 << 5); - public static final byte STATUS_POWER_MODE_0 = (1 << 4); - public static final byte STATUS_HALT_STATUS = (1 << 3); - public static final byte STATUS_DEBUG_LOCKED = (1 << 2); - public static final byte STATUS_OSCILLATOR_STABLE = (1 << 1); - public static final byte STATUS_STACK_OVERFLOW = (1 << 0); - - public static final byte SET_HW_BRKPNT = 0x3b; - public static byte HW_BRKPNT_N(byte n) { return (byte) ((n) << 3); } - public static final byte HW_BRKPNT_N_MASK = (0x3 << 3); - public static final byte HW_BRKPNT_ENABLE = (1 << 2); - - public static final byte HALT = 0x44; - public static final byte RESUME = 0x4c; - public static byte DEBUG_INSTR(byte n) { return (byte) (0x54|(n)); } - public static final byte STEP_INSTR = 0x5c; - public static byte STEP_REPLACE(byte n) { return (byte) (0x64|(n)); } - public static final byte GET_CHIP_ID = 0x68; - - - boolean debug_mode; - - void ensure_debug_mode() { - if (!debug_mode) { - printf("D\n"); - flush_input(); - debug_mode = true; - } - } - - void dump_memory(String header, int address, byte[] bytes, int start, int len) { - System.out.printf("%s\n", header); - for (int j = 0; j < len; j++) { - if ((j & 15) == 0) { - if (j != 0) - System.out.printf("\n"); - System.out.printf ("%04x:", address + j); - } - System.out.printf(" %02x", bytes[start + j]); - } - System.out.printf("\n"); - } - - /* - * Write target memory - */ - public void write_memory(int address, byte[] bytes, int start, int len) { - ensure_debug_mode(); -// dump_memory("write_memory", address, bytes, start, len); - printf("O %x %x\n", len, address); - for (int i = 0; i < len; i++) - printf("%02x", bytes[start + i]); - } - - public void write_memory(int address, byte[] bytes) { - write_memory(address, bytes, 0, bytes.length); - } - - /* - * Read target memory - */ - public byte[] read_memory(int address, int length) - throws IOException, InterruptedException { - byte[] data = new byte[length]; - - flush_input(); - ensure_debug_mode(); - printf("I %x %x\n", length, address); - int i = 0; - int start = 0; - while (i < length) { - String line = get_reply().trim(); - if (!Altos.ishex(line) || line.length() % 2 != 0) - throw new IOException( - String.format - ("Invalid reply \"%s\"", line)); - int this_time = line.length() / 2; - for (int j = 0; j < this_time; j++) - data[start + j] = (byte) ((Altos.fromhex(line.charAt(j*2)) << 4) + - Altos.fromhex(line.charAt(j*2+1))); - start += this_time; - i += this_time; - } -// dump_memory("read_memory", address, data, 0, length); - - return data; - } - - /* - * Write raw bytes to the debug link using the 'P' command - */ - public void write_bytes(byte[] bytes) throws IOException { - int i = 0; - ensure_debug_mode(); - while (i < bytes.length) { - int this_time = bytes.length - i; - if (this_time > 8) - this_time = 0; - printf("P"); - for (int j = 0; j < this_time; j++) - printf(" %02x", bytes[i+j]); - printf("\n"); - i += this_time; - } - } - - public void write_byte(byte b) throws IOException { - byte[] bytes = { b }; - write_bytes(bytes); - } - - /* - * Read raw bytes from the debug link using the 'G' command - */ - public byte[] read_bytes(int length) - throws IOException, InterruptedException { - - flush_input(); - ensure_debug_mode(); - printf("G %x\n", length); - int i = 0; - byte[] data = new byte[length]; - while (i < length) { - String line = get_reply().trim(); - String tokens[] = line.split("\\s+"); - for (int j = 0; j < tokens.length; j++) { - if (!Altos.ishex(tokens[j]) || - tokens[j].length() != 2) - throw new IOException( - String.format - ("Invalid read_bytes reply \"%s\"", line)); - try { - data[i + j] = (byte) Integer.parseInt(tokens[j], 16); - } catch (NumberFormatException ne) { - throw new IOException( - String.format - ("Invalid read_bytes reply \"%s\"", line)); - } - } - i += tokens.length; - } - return data; - } - - public byte read_byte() throws IOException, InterruptedException { - return read_bytes(1)[0]; - } - - public byte debug_instr(byte[] instruction) throws IOException, InterruptedException { - byte[] command = new byte[1 + instruction.length]; - command[0] = DEBUG_INSTR((byte) instruction.length); - for (int i = 0; i < instruction.length; i++) - command[i+1] = instruction[i]; - write_bytes(command); - return read_byte(); - } - - public byte resume() throws IOException, InterruptedException { - write_byte(RESUME); - return read_byte(); - } - - public int read_uint16() throws IOException, InterruptedException { - byte[] d = read_bytes(2); - return ((int) (d[0] & 0xff) << 8) | (d[1] & 0xff); - } - - public int read_uint8() throws IOException, InterruptedException { - byte[] d = read_bytes(1); - return (int) (d[0] & 0xff); - } - - public int get_chip_id() throws IOException, InterruptedException { - write_byte(GET_CHIP_ID); - return read_uint16(); - } - - public int get_pc() throws IOException, InterruptedException { - write_byte(GET_PC); - return read_uint16(); - } - - public byte read_status() throws IOException, InterruptedException { - write_byte(READ_STATUS); - return read_byte(); - } - - static final byte LJMP = 0x02; - - public void set_pc(int pc) throws IOException, InterruptedException { - byte high = (byte) (pc >> 8); - byte low = (byte) pc; - byte[] jump_mem = { LJMP, high, low }; - debug_instr(jump_mem); - } - - public boolean check_connection() throws IOException, InterruptedException { - byte reply = read_status(); - if ((reply & STATUS_CHIP_ERASE_DONE) == 0) - return false; - if ((reply & STATUS_PCON_IDLE) != 0) - return false; - if ((reply & STATUS_POWER_MODE_0) == 0) - return false; - return true; - } - - public AltosRomconfig romconfig() { - try { - byte[] bytes = read_memory(0xa0, 10); - return new AltosRomconfig(bytes, 0); - } catch (IOException ie) { - } catch (InterruptedException ie) { - } - return new AltosRomconfig(); - } - - /* - * Reset target - */ - public void reset() { - printf ("R\n"); - } - - public AltosDebug (AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException { - super(in_device); - } -} \ No newline at end of file diff --git a/ao-tools/altosui/AltosDescent.java b/ao-tools/altosui/AltosDescent.java deleted file mode 100644 index 16ccd458..00000000 --- a/ao-tools/altosui/AltosDescent.java +++ /dev/null @@ -1,353 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosDescent extends JComponent implements AltosFlightDisplay { - GridBagLayout layout; - - public abstract class DescentStatus { - JLabel label; - JTextField value; - AltosLights lights; - - abstract void show(AltosState state, int crc_errors); - void reset() { - value.setText(""); - lights.set(false); - } - - public DescentStatus (GridBagLayout layout, int y, String text) { - GridBagConstraints c = new GridBagConstraints(); - c.weighty = 1; - - lights = new AltosLights(); - c.gridx = 0; c.gridy = y; - c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.VERTICAL; - c.weightx = 0; - layout.setConstraints(lights, c); - add(lights); - - label = new JLabel(text); - label.setFont(Altos.label_font); - label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = 1; c.gridy = y; - c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.VERTICAL; - c.gridwidth = 3; - c.weightx = 0; - layout.setConstraints(label, c); - add(label); - - value = new JTextField(Altos.text_width); - value.setFont(Altos.value_font); - value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 4; c.gridy = y; - c.gridwidth = 1; - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - layout.setConstraints(value, c); - add(value); - - } - } - - public abstract class DescentValue { - JLabel label; - JTextField value; - - void reset() { - value.setText(""); - } - - abstract void show(AltosState state, int crc_errors); - - void show(String format, double v) { - value.setText(String.format(format, v)); - } - - void show(String v) { - value.setText(v); - } - - public DescentValue (GridBagLayout layout, int x, int y, String text) { - GridBagConstraints c = new GridBagConstraints(); - c.weighty = 1; - - label = new JLabel(text); - label.setFont(Altos.label_font); - label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = x + 1; c.gridy = y; - c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.VERTICAL; - c.weightx = 0; - add(label, c); - - value = new JTextField(Altos.text_width); - value.setFont(Altos.value_font); - value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = x + 2; c.gridy = y; - c.gridwidth = 1; - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - add(value, c); - } - } - - public abstract class DescentDualValue { - JLabel label; - JTextField value1; - JTextField value2; - - void reset() { - value1.setText(""); - value2.setText(""); - } - - abstract void show(AltosState state, int crc_errors); - void show(String v1, String v2) { - value1.setText(v1); - value2.setText(v2); - } - void show(String f1, double v1, String f2, double v2) { - value1.setText(String.format(f1, v1)); - value2.setText(String.format(f2, v2)); - } - - public DescentDualValue (GridBagLayout layout, int x, int y, String text) { - GridBagConstraints c = new GridBagConstraints(); - c.weighty = 1; - - label = new JLabel(text); - label.setFont(Altos.label_font); - label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = x + 1; c.gridy = y; - c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.VERTICAL; - c.weightx = 0; - layout.setConstraints(label, c); - add(label); - - value1 = new JTextField(Altos.text_width); - value1.setFont(Altos.value_font); - value1.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = x + 2; c.gridy = y; - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - layout.setConstraints(value1, c); - add(value1); - - value2 = new JTextField(Altos.text_width); - value2.setFont(Altos.value_font); - value2.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = x + 4; c.gridy = y; - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - c.gridwidth = 1; - layout.setConstraints(value2, c); - add(value2); - } - } - - class Height extends DescentValue { - void show (AltosState state, int crc_errors) { - show("%6.0f m", state.height); - } - public Height (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Height"); - } - } - - Height height; - - class Speed extends DescentValue { - void show (AltosState state, int crc_errors) { - double speed = state.speed; - if (!state.ascent) - speed = state.baro_speed; - show("%6.0f m/s", speed); - } - public Speed (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Speed"); - } - } - - Speed speed; - - String pos(double p, String pos, String neg) { - String h = pos; - if (p < 0) { - h = neg; - p = -p; - } - int deg = (int) Math.floor(p); - double min = (p - Math.floor(p)) * 60.0; - return String.format("%s %d° %9.6f", h, deg, min); - } - - class Lat extends DescentValue { - void show (AltosState state, int crc_errors) { - if (state.gps != null) - show(pos(state.gps.lat,"N", "S")); - else - show("???"); - } - public Lat (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Latitude"); - } - } - - Lat lat; - - class Lon extends DescentValue { - void show (AltosState state, int crc_errors) { - if (state.gps != null) - show(pos(state.gps.lon,"W", "E")); - else - show("???"); - } - public Lon (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Longitude"); - } - } - - Lon lon; - - class Apogee extends DescentStatus { - void show (AltosState state, int crc_errors) { - value.setText(String.format("%4.2f V", state.drogue_sense)); - lights.set(state.drogue_sense > 3.2); - } - public Apogee (GridBagLayout layout, int y) { - super(layout, y, "Apogee Igniter Voltage"); - } - } - - Apogee apogee; - - class Main extends DescentStatus { - void show (AltosState state, int crc_errors) { - value.setText(String.format("%4.2f V", state.main_sense)); - lights.set(state.main_sense > 3.2); - } - public Main (GridBagLayout layout, int y) { - super(layout, y, "Main Igniter Voltage"); - } - } - - Main main; - - class Bearing extends DescentDualValue { - void show (AltosState state, int crc_errors) { - if (state.from_pad != null) { - show( String.format("%3.0f°", state.from_pad.bearing), - state.from_pad.bearing_words( - AltosGreatCircle.BEARING_LONG)); - } else { - show("???", "???"); - } - } - public Bearing (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Bearing"); - } - } - - Bearing bearing; - - class Range extends DescentValue { - void show (AltosState state, int crc_errors) { - show("%6.0f m", state.range); - } - public Range (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Range"); - } - } - - Range range; - - class Elevation extends DescentValue { - void show (AltosState state, int crc_errors) { - show("%3.0f°", state.elevation); - } - public Elevation (GridBagLayout layout, int x, int y) { - super (layout, x, y, "Elevation"); - } - } - - Elevation elevation; - - public void reset() { - lat.reset(); - lon.reset(); - height.reset(); - speed.reset(); - bearing.reset(); - range.reset(); - elevation.reset(); - main.reset(); - apogee.reset(); - } - - public void show(AltosState state, int crc_errors) { - height.show(state, crc_errors); - speed.show(state, crc_errors); - bearing.show(state, crc_errors); - range.show(state, crc_errors); - elevation.show(state, crc_errors); - lat.show(state, crc_errors); - lon.show(state, crc_errors); - main.show(state, crc_errors); - apogee.show(state, crc_errors); - } - - public AltosDescent() { - layout = new GridBagLayout(); - - setLayout(layout); - - /* Elements in descent display */ - speed = new Speed(layout, 0, 0); - height = new Height(layout, 2, 0); - elevation = new Elevation(layout, 0, 1); - range = new Range(layout, 2, 1); - bearing = new Bearing(layout, 0, 2); - lat = new Lat(layout, 0, 3); - lon = new Lon(layout, 2, 3); - - apogee = new Apogee(layout, 4); - main = new Main(layout, 5); - } -} diff --git a/ao-tools/altosui/AltosDevice.java b/ao-tools/altosui/AltosDevice.java deleted file mode 100644 index f0fda57b..00000000 --- a/ao-tools/altosui/AltosDevice.java +++ /dev/null @@ -1,170 +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.lang.*; -import java.util.*; -import libaltosJNI.*; - -public class AltosDevice extends altos_device { - - static public boolean initialized = false; - static public boolean loaded_library = false; - - public static boolean load_library() { - if (!initialized) { - try { - System.loadLibrary("altos"); - libaltos.altos_init(); - loaded_library = true; - } catch (UnsatisfiedLinkError e) { - loaded_library = false; - } - initialized = true; - } - return loaded_library; - } - - static int usb_vendor_altusmetrum() { - if (load_library()) - return libaltosConstants.USB_VENDOR_ALTUSMETRUM; - return 0x000a; - } - - static int usb_product_altusmetrum() { - if (load_library()) - return libaltosConstants.USB_PRODUCT_ALTUSMETRUM; - return 0x000a; - } - - static int usb_product_altusmetrum_min() { - if (load_library()) - return libaltosConstants.USB_PRODUCT_ALTUSMETRUM_MIN; - return 0x000a; - } - - static int usb_product_altusmetrum_max() { - if (load_library()) - return libaltosConstants.USB_PRODUCT_ALTUSMETRUM_MAX; - return 0x000d; - } - - static int usb_product_telemetrum() { - if (load_library()) - return libaltosConstants.USB_PRODUCT_TELEMETRUM; - return 0x000b; - } - - static int usb_product_teledongle() { - if (load_library()) - return libaltosConstants.USB_PRODUCT_TELEDONGLE; - return 0x000c; - } - - static int usb_product_teleterra() { - if (load_library()) - return libaltosConstants.USB_PRODUCT_TELETERRA; - return 0x000d; - } - - public final static int vendor_altusmetrum = usb_vendor_altusmetrum(); - public final static int product_altusmetrum = usb_product_altusmetrum(); - public final static int product_telemetrum = usb_product_telemetrum(); - public final static int product_teledongle = usb_product_teledongle(); - public final static int product_teleterra = usb_product_teleterra(); - public final static int product_altusmetrum_min = usb_product_altusmetrum_min(); - public final static int product_altusmetrum_max = usb_product_altusmetrum_max(); - - - public final static int product_any = 0x10000; - public final static int product_basestation = 0x10000 + 1; - - public String toString() { - String name = getName(); - if (name == null) - name = "Altus Metrum"; - return String.format("%-20.20s %4d %s", - 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; - if (getProduct() < product_altusmetrum_min) - return false; - if (getProduct() > product_altusmetrum_max) - return false; - return true; - } - - public boolean matchProduct(int want_product) { - - if (!isAltusMetrum()) - return false; - - if (want_product == product_any) - return true; - - if (want_product == product_basestation) - return matchProduct(product_teledongle) || matchProduct(product_teleterra); - - int have_product = getProduct(); - - if (have_product == product_altusmetrum) /* old devices match any request */ - return true; - - if (want_product == have_product) - return true; - - return false; - } - - static AltosDevice[] list(int product) { - if (!load_library()) - return null; - - SWIGTYPE_p_altos_list list = libaltos.altos_list_start(); - - ArrayList device_list = new ArrayList(); - if (list != null) { - SWIGTYPE_p_altos_file file; - - for (;;) { - AltosDevice device = new AltosDevice(); - if (libaltos.altos_list_next(list, device) == 0) - break; - if (device.matchProduct(product)) - device_list.add(device); - } - libaltos.altos_list_finish(list); - } - - AltosDevice[] devices = new AltosDevice[device_list.size()]; - for (int i = 0; i < device_list.size(); i++) - devices[i] = device_list.get(i); - return devices; - } -} \ No newline at end of file diff --git a/ao-tools/altosui/AltosDeviceDialog.java b/ao-tools/altosui/AltosDeviceDialog.java deleted file mode 100644 index 2966ad1e..00000000 --- a/ao-tools/altosui/AltosDeviceDialog.java +++ /dev/null @@ -1,164 +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.lang.*; -import java.util.*; -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; -import libaltosJNI.libaltos; -import libaltosJNI.altos_device; -import libaltosJNI.SWIGTYPE_p_altos_file; -import libaltosJNI.SWIGTYPE_p_altos_list; - -public class AltosDeviceDialog extends JDialog implements ActionListener { - - private static AltosDeviceDialog dialog; - private static AltosDevice value = null; - private JList list; - - public static AltosDevice show (Component frameComp, int product) { - - Frame frame = JOptionPane.getFrameForComponent(frameComp); - AltosDevice[] devices; - devices = AltosDevice.list(product); - - if (devices != null && devices.length > 0) { - value = null; - dialog = new AltosDeviceDialog(frame, frameComp, - devices, - devices[0]); - - dialog.setVisible(true); - return value; - } else { - /* check for missing altos JNI library, which - * will put up its own error dialog - */ - if (AltosUI.load_library(frame)) { - JOptionPane.showMessageDialog(frame, - "No AltOS devices available", - "No AltOS devices", - JOptionPane.ERROR_MESSAGE); - } - return null; - } - } - - private AltosDeviceDialog (Frame frame, Component location, - AltosDevice[] devices, - AltosDevice initial) { - super(frame, "Device Selection", true); - - value = null; - - JButton cancelButton = new JButton("Cancel"); - cancelButton.addActionListener(this); - - final JButton selectButton = new JButton("Select"); - selectButton.setActionCommand("select"); - selectButton.addActionListener(this); - getRootPane().setDefaultButton(selectButton); - - list = new JList(devices) { - //Subclass JList to workaround bug 4832765, which can cause the - //scroll pane to not let the user easily scroll up to the beginning - //of the list. An alternative would be to set the unitIncrement - //of the JScrollBar to a fixed value. You wouldn't get the nice - //aligned scrolling, but it should work. - public int getScrollableUnitIncrement(Rectangle visibleRect, - int orientation, - int direction) { - int row; - if (orientation == SwingConstants.VERTICAL && - direction < 0 && (row = getFirstVisibleIndex()) != -1) { - Rectangle r = getCellBounds(row, row); - if ((r.y == visibleRect.y) && (row != 0)) { - Point loc = r.getLocation(); - loc.y--; - int prevIndex = locationToIndex(loc); - Rectangle prevR = getCellBounds(prevIndex, prevIndex); - - if (prevR == null || prevR.y >= r.y) { - return 0; - } - return prevR.height; - } - } - return super.getScrollableUnitIncrement( - visibleRect, orientation, direction); - } - }; - - list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - list.setLayoutOrientation(JList.HORIZONTAL_WRAP); - list.setVisibleRowCount(-1); - list.addMouseListener(new MouseAdapter() { - public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 2) { - selectButton.doClick(); //emulate button click - } - } - }); - JScrollPane listScroller = new JScrollPane(list); - listScroller.setPreferredSize(new Dimension(400, 80)); - listScroller.setAlignmentX(LEFT_ALIGNMENT); - - //Create a container so that we can add a title around - //the scroll pane. Can't add a title directly to the - //scroll pane because its background would be white. - //Lay out the label and scroll pane from top to bottom. - JPanel listPane = new JPanel(); - listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS)); - - JLabel label = new JLabel("Select Device"); - label.setLabelFor(list); - listPane.add(label); - listPane.add(Box.createRigidArea(new Dimension(0,5))); - listPane.add(listScroller); - listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); - - //Lay out the buttons from left to right. - JPanel buttonPane = new JPanel(); - buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS)); - buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10)); - buttonPane.add(Box.createHorizontalGlue()); - buttonPane.add(cancelButton); - buttonPane.add(Box.createRigidArea(new Dimension(10, 0))); - buttonPane.add(selectButton); - - //Put everything together, using the content pane's BorderLayout. - Container contentPane = getContentPane(); - contentPane.add(listPane, BorderLayout.CENTER); - contentPane.add(buttonPane, BorderLayout.PAGE_END); - - //Initialize values. - list.setSelectedValue(initial, true); - pack(); - setLocationRelativeTo(location); - } - - //Handle clicks on the Set and Cancel buttons. - public void actionPerformed(ActionEvent e) { - if ("select".equals(e.getActionCommand())) - AltosDeviceDialog.value = (AltosDevice)(list.getSelectedValue()); - AltosDeviceDialog.dialog.setVisible(false); - } - -} diff --git a/ao-tools/altosui/AltosDisplayThread.java b/ao-tools/altosui/AltosDisplayThread.java deleted file mode 100644 index 3e719130..00000000 --- a/ao-tools/altosui/AltosDisplayThread.java +++ /dev/null @@ -1,249 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosDisplayThread extends Thread { - - Frame parent; - IdleThread idle_thread; - AltosVoice voice; - String name; - AltosFlightReader reader; - int crc_errors; - AltosFlightDisplay display; - - synchronized void show(AltosState state, int crc_errors) { - if (state != null) - display.show(state, crc_errors); - } - - class IdleThread extends Thread { - - boolean started; - private AltosState state; - int reported_landing; - int report_interval; - long report_time; - - public synchronized void report(boolean last) { - if (state == null) - return; - - /* reset the landing count once we hear about a new flight */ - if (state.state < Altos.ao_flight_drogue) - reported_landing = 0; - - /* Shut up once the rocket is on the ground */ - if (reported_landing > 2) { - return; - } - - /* If the rocket isn't on the pad, then report height */ - if (Altos.ao_flight_drogue <= state.state && - state.state < Altos.ao_flight_landed && - state.range >= 0) - { - voice.speak("Height %d, bearing %s %d, elevation %d, range %d.\n", - (int) (state.height + 0.5), - state.from_pad.bearing_words( - AltosGreatCircle.BEARING_VOICE), - (int) (state.from_pad.bearing + 0.5), - (int) (state.elevation + 0.5), - (int) (state.range + 0.5)); - } else if (state.state > Altos.ao_flight_pad) { - voice.speak("%d meters", (int) (state.height + 0.5)); - } else { - reported_landing = 0; - } - - /* If the rocket is coming down, check to see if it has landed; - * either we've got a landed report or we haven't heard from it in - * a long time - */ - if (state.state >= Altos.ao_flight_drogue && - (last || - System.currentTimeMillis() - state.report_time >= 15000 || - state.state == Altos.ao_flight_landed)) - { - if (Math.abs(state.baro_speed) < 20 && state.height < 100) - voice.speak("rocket landed safely"); - else - voice.speak("rocket may have crashed"); - if (state.from_pad != null) - voice.speak("Bearing %d degrees, range %d meters.", - (int) (state.from_pad.bearing + 0.5), - (int) (state.from_pad.distance + 0.5)); - ++reported_landing; - if (state.state != Altos.ao_flight_landed) { - state.state = Altos.ao_flight_landed; - show(state, 0); - } - } - } - - long now () { - return System.currentTimeMillis(); - } - - void set_report_time() { - report_time = now() + report_interval; - } - - public void run () { - try { - for (;;) { - set_report_time(); - for (;;) { - voice.drain(); - synchronized (this) { - long sleep_time = report_time - now(); - if (sleep_time <= 0) - break; - wait(sleep_time); - } - } - report(false); - } - } catch (InterruptedException ie) { - try { - voice.drain(); - } catch (InterruptedException iie) { } - } - } - - public synchronized void notice(AltosState new_state, boolean spoken) { - AltosState old_state = state; - state = new_state; - if (!started && state.state > Altos.ao_flight_pad) { - started = true; - start(); - } - - if (state.state < Altos.ao_flight_drogue) - report_interval = 10000; - else - report_interval = 20000; - if (old_state != null && old_state.state != state.state) { - report_time = now(); - this.notify(); - } else if (spoken) - set_report_time(); - } - - public IdleThread() { - state = null; - reported_landing = 0; - report_interval = 10000; - } - } - - boolean tell(AltosState state, AltosState old_state) { - boolean ret = false; - if (old_state == null || old_state.state != state.state) { - voice.speak(state.data.state()); - if ((old_state == null || old_state.state <= Altos.ao_flight_boost) && - state.state > Altos.ao_flight_boost) { - voice.speak("max speed: %d meters per second.", - (int) (state.max_speed + 0.5)); - ret = true; - } else if ((old_state == null || old_state.state < Altos.ao_flight_drogue) && - state.state >= Altos.ao_flight_drogue) { - voice.speak("max height: %d meters.", - (int) (state.max_height + 0.5)); - ret = true; - } - } - if (old_state == null || old_state.gps_ready != state.gps_ready) { - if (state.gps_ready) { - voice.speak("GPS ready"); - ret = true; - } - else if (old_state != null) { - voice.speak("GPS lost"); - ret = true; - } - } - old_state = state; - return ret; - } - - public void run() { - boolean interrupted = false; - String line; - AltosState state = null; - AltosState old_state = null; - boolean told; - - idle_thread = new IdleThread(); - - display.reset(); - try { - for (;;) { - try { - AltosRecord record = reader.read(); - if (record == null) - break; - old_state = state; - state = new AltosState(record, state); - reader.update(state); - show(state, crc_errors); - told = tell(state, old_state); - idle_thread.notice(state, told); - } catch (ParseException pp) { - System.out.printf("Parse error: %d \"%s\"\n", pp.getErrorOffset(), pp.getMessage()); - } catch (AltosCRCException ce) { - ++crc_errors; - show(state, crc_errors); - } - } - } catch (InterruptedException ee) { - interrupted = true; - } catch (IOException ie) { - JOptionPane.showMessageDialog(parent, - String.format("Error reading from \"%s\"", name), - "Telemetry Read Error", - JOptionPane.ERROR_MESSAGE); - } finally { - if (!interrupted) - idle_thread.report(true); - reader.close(interrupted); - idle_thread.interrupt(); - try { - idle_thread.join(); - } catch (InterruptedException ie) {} - } - } - - public AltosDisplayThread(Frame in_parent, AltosVoice in_voice, AltosFlightDisplay in_display, AltosFlightReader in_reader) { - parent = in_parent; - voice = in_voice; - display = in_display; - reader = in_reader; - } -} diff --git a/ao-tools/altosui/AltosEepromDownload.java b/ao-tools/altosui/AltosEepromDownload.java deleted file mode 100644 index 02fc36f2..00000000 --- a/ao-tools/altosui/AltosEepromDownload.java +++ /dev/null @@ -1,285 +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.*; -import java.util.concurrent.*; - -import libaltosJNI.*; - -public class AltosEepromDownload implements Runnable { - - static final String[] state_names = { - "startup", - "idle", - "pad", - "boost", - "fast", - "coast", - "drogue", - "main", - "landed", - "invalid", - }; - - int[] ParseHex(String line) { - String[] tokens = line.split("\\s+"); - int[] array = new int[tokens.length]; - - for (int i = 0; i < tokens.length; i++) - try { - array[i] = Integer.parseInt(tokens[i], 16); - } catch (NumberFormatException ne) { - return null; - } - return array; - } - - int checksum(int[] line) { - int csum = 0x5a; - for (int i = 1; i < line.length; i++) - csum += line[i]; - return csum & 0xff; - } - - void FlushPending(FileWriter file, LinkedList pending) throws IOException { - while (!pending.isEmpty()) { - file.write(pending.remove()); - } - } - - JFrame frame; - AltosDevice device; - AltosSerial serial_line; - boolean remote; - Thread eeprom_thread; - AltosEepromMonitor monitor; - - void CaptureLog() throws IOException, InterruptedException, TimeoutException { - int serial = 0; - int block, state_block = 0; - int addr; - int flight = 0; - int year = 0, month = 0, day = 0; - int state = 0; - boolean done = false; - boolean want_file = false; - boolean any_valid; - FileWriter eeprom_file = null; - AltosFile eeprom_name; - LinkedList eeprom_pending = new LinkedList(); - - serial_line.printf("\nc s\nv\n"); - - /* Pull the serial number out of the version information */ - - for (;;) { - String line = serial_line.get_reply(5000); - - if (line == null) - throw new TimeoutException(); - if (line.startsWith("serial-number")) { - try { - serial = Integer.parseInt(line.substring(13).trim()); - } catch (NumberFormatException ne) { - serial = 0; - } - } - - eeprom_pending.add(String.format("%s\n", line)); - - /* signals the end of the version info */ - if (line.startsWith("software-version")) - break; - } - if (serial == 0) - throw new IOException("no serial number found"); - - monitor.set_serial(serial); - /* Now scan the eeprom, reading blocks of data and converting to .eeprom file form */ - - state = 0; state_block = 0; - for (block = 0; !done && block < 511; block++) { - serial_line.printf("e %x\n", block); - any_valid = false; - monitor.set_value(state_names[state], state, block - state_block); - for (addr = 0; addr < 0x100;) { - String line = serial_line.get_reply(5000); - if (line == null) - throw new TimeoutException(); - int[] values = ParseHex(line); - - if (values == null) { - System.out.printf("invalid line: %s\n", line); - continue; - } else if (values[0] != addr) { - System.out.printf("data address out of sync at 0x%x\n", - block * 256 + values[0]); - } else if (checksum(values) != 0) { - System.out.printf("invalid checksum at 0x%x\n", - block * 256 + values[0]); - } else { - any_valid = true; - int cmd = values[1]; - int tick = values[3] + (values[4] << 8); - int a = values[5] + (values[6] << 8); - int b = values[7] + (values[8] << 8); - - if (cmd == Altos.AO_LOG_FLIGHT) { - flight = b; - monitor.set_flight(flight); - } - - /* Monitor state transitions to update display */ - if (cmd == Altos.AO_LOG_STATE && a <= Altos.ao_flight_landed) { - if (a > Altos.ao_flight_pad) - want_file = true; - if (a > state) - state_block = block; - state = a; - } - - if (cmd == Altos.AO_LOG_GPS_DATE) { - year = 2000 + (a & 0xff); - month = (a >> 8) & 0xff; - day = (b & 0xff); - want_file = true; - } - - if (eeprom_file == null) { - if (serial != 0 && flight != 0 && want_file) { - if (year != 0 && month != 0 && day != 0) - eeprom_name = new AltosFile(year, month, day, serial, flight, "eeprom"); - else - eeprom_name = new AltosFile(serial, flight, "eeprom"); - - monitor.set_file(eeprom_name.getName()); - eeprom_file = new FileWriter(eeprom_name); - if (eeprom_file != null) { - FlushPending(eeprom_file, eeprom_pending); - eeprom_pending = null; - } - } - } - - String log_line = String.format("%c %4x %4x %4x\n", - cmd, tick, a, b); - if (eeprom_file != null) - eeprom_file.write(log_line); - else - eeprom_pending.add(log_line); - - if (cmd == Altos.AO_LOG_STATE && a == Altos.ao_flight_landed) { - done = true; - } - } - addr += 8; - } - if (!any_valid) - done = true; - } - if (eeprom_file == null) { - eeprom_name = new AltosFile(serial,flight,"eeprom"); - eeprom_file = new FileWriter(eeprom_name); - if (eeprom_file != null) { - FlushPending(eeprom_file, eeprom_pending); - } - } - if (eeprom_file != null) { - eeprom_file.flush(); - eeprom_file.close(); - } - } - - public void run () { - if (remote) { - serial_line.set_radio(); - serial_line.printf("p\nE 0\n"); - serial_line.flush_input(); - } - - monitor = new AltosEepromMonitor(frame, Altos.ao_flight_boost, Altos.ao_flight_landed); - monitor.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - eeprom_thread.interrupt(); - } - }); - try { - CaptureLog(); - } catch (IOException ee) { - JOptionPane.showMessageDialog(frame, - device.toShortString(), - ee.getLocalizedMessage(), - JOptionPane.ERROR_MESSAGE); - } catch (InterruptedException ie) { - } catch (TimeoutException te) { - JOptionPane.showMessageDialog(frame, - String.format("Connection to \"%s\" failed", - device.toShortString()), - "Connection Failed", - JOptionPane.ERROR_MESSAGE); - } - if (remote) - serial_line.printf("~"); - monitor.done(); - serial_line.flush_output(); - serial_line.close(); - } - - public AltosEepromDownload(JFrame given_frame) { - frame = given_frame; - device = AltosDeviceDialog.show(frame, AltosDevice.product_any); - - remote = false; - - if (device != null) { - try { - serial_line = new AltosSerial(device); - if (!device.matchProduct(AltosDevice.product_telemetrum)) - remote = true; - eeprom_thread = new Thread(this); - eeprom_thread.start(); - } catch (FileNotFoundException ee) { - JOptionPane.showMessageDialog(frame, - String.format("Cannot open device \"%s\"", - device.toShortString()), - "Cannot open target device", - JOptionPane.ERROR_MESSAGE); - } catch (AltosSerialInUseException si) { - JOptionPane.showMessageDialog(frame, - String.format("Device \"%s\" already in use", - device.toShortString()), - "Device in use", - JOptionPane.ERROR_MESSAGE); - } catch (IOException ee) { - JOptionPane.showMessageDialog(frame, - device.toShortString(), - ee.getLocalizedMessage(), - JOptionPane.ERROR_MESSAGE); - } - } - } -} diff --git a/ao-tools/altosui/AltosEepromIterable.java b/ao-tools/altosui/AltosEepromIterable.java deleted file mode 100644 index f8e6d7e5..00000000 --- a/ao-tools/altosui/AltosEepromIterable.java +++ /dev/null @@ -1,423 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -/* - * AltosRecords with an index field so they can be sorted by tick while preserving - * the original ordering for elements with matching ticks - */ -class AltosOrderedRecord extends AltosEepromRecord implements Comparable { - - public int index; - - public AltosOrderedRecord(String line, int in_index, int prev_tick, boolean prev_tick_valid) - throws ParseException { - super(line); - if (prev_tick_valid) { - tick |= (prev_tick & ~0xffff); - if (tick < prev_tick) { - if (prev_tick - tick > 0x8000) - tick += 0x10000; - } else { - if (tick - prev_tick > 0x8000) - tick -= 0x10000; - } - } - index = in_index; - } - - public AltosOrderedRecord(int in_cmd, int in_tick, int in_a, int in_b, int in_index) { - super(in_cmd, in_tick, in_a, in_b); - index = in_index; - } - - public int compareTo(AltosOrderedRecord o) { - int tick_diff = tick - o.tick; - if (tick_diff != 0) - return tick_diff; - return index - o.index; - } -} - -public class AltosEepromIterable extends AltosRecordIterable { - - static final int seen_flight = 1; - static final int seen_sensor = 2; - static final int seen_temp_volt = 4; - static final int seen_deploy = 8; - static final int seen_gps_time = 16; - static final int seen_gps_lat = 32; - static final int seen_gps_lon = 64; - - static final int seen_basic = seen_flight|seen_sensor|seen_temp_volt|seen_deploy; - - AltosEepromRecord flight_record; - AltosEepromRecord gps_date_record; - - TreeSet records; - - LinkedList list; - - class EepromState { - int seen; - int n_pad_samples; - double ground_pres; - int gps_tick; - int boost_tick; - - EepromState() { - seen = 0; - n_pad_samples = 0; - ground_pres = 0.0; - gps_tick = 0; - } - } - - void update_state(AltosRecord state, AltosEepromRecord record, EepromState eeprom) { - state.tick = record.tick; - switch (record.cmd) { - case Altos.AO_LOG_FLIGHT: - eeprom.seen |= seen_flight; - state.ground_accel = record.a; - state.flight_accel = record.a; - state.flight = record.b; - eeprom.boost_tick = record.tick; - break; - case Altos.AO_LOG_SENSOR: - state.accel = record.a; - state.pres = record.b; - if (state.state < Altos.ao_flight_boost) { - eeprom.n_pad_samples++; - eeprom.ground_pres += state.pres; - state.ground_pres = (int) (eeprom.ground_pres / eeprom.n_pad_samples); - state.flight_pres = state.ground_pres; - } else { - state.flight_pres = (state.flight_pres * 15 + state.pres) / 16; - state.flight_accel = (state.flight_accel * 15 + state.accel) / 16; - state.flight_vel += (state.accel_plus_g - state.accel); - } - eeprom.seen |= seen_sensor; - break; - case Altos.AO_LOG_TEMP_VOLT: - state.temp = record.a; - state.batt = record.b; - eeprom.seen |= seen_temp_volt; - break; - case Altos.AO_LOG_DEPLOY: - state.drogue = record.a; - state.main = record.b; - eeprom.seen |= seen_deploy; - break; - case Altos.AO_LOG_STATE: - state.state = record.a; - break; - case Altos.AO_LOG_GPS_TIME: - eeprom.gps_tick = state.tick; - AltosGPS old = state.gps; - state.gps = new AltosGPS(); - - /* GPS date doesn't get repeated through the file */ - if (old != null) { - state.gps.year = old.year; - state.gps.month = old.month; - state.gps.day = old.day; - } - state.gps.hour = (record.a & 0xff); - state.gps.minute = (record.a >> 8); - state.gps.second = (record.b & 0xff); - - int flags = (record.b >> 8); - state.gps.connected = (flags & Altos.AO_GPS_RUNNING) != 0; - state.gps.locked = (flags & Altos.AO_GPS_VALID) != 0; - state.gps.date_valid = (flags & Altos.AO_GPS_DATE_VALID) != 0; - state.gps.nsat = (flags & Altos.AO_GPS_NUM_SAT_MASK) >> - Altos.AO_GPS_NUM_SAT_SHIFT; - break; - case Altos.AO_LOG_GPS_LAT: - int lat32 = record.a | (record.b << 16); - state.gps.lat = (double) lat32 / 1e7; - break; - case Altos.AO_LOG_GPS_LON: - int lon32 = record.a | (record.b << 16); - state.gps.lon = (double) lon32 / 1e7; - break; - case Altos.AO_LOG_GPS_ALT: - state.gps.alt = record.a; - break; - case Altos.AO_LOG_GPS_SAT: - if (state.tick == eeprom.gps_tick) { - int svid = record.a; - int c_n0 = record.b >> 8; - state.gps.add_sat(svid, c_n0); - } - break; - case Altos.AO_LOG_GPS_DATE: - state.gps.year = (record.a & 0xff) + 2000; - state.gps.month = record.a >> 8; - state.gps.day = record.b & 0xff; - break; - - case Altos.AO_LOG_CONFIG_VERSION: - break; - case Altos.AO_LOG_MAIN_DEPLOY: - break; - case Altos.AO_LOG_APOGEE_DELAY: - break; - case Altos.AO_LOG_RADIO_CHANNEL: - break; - case Altos.AO_LOG_CALLSIGN: - state.callsign = record.data; - break; - case Altos.AO_LOG_ACCEL_CAL: - state.accel_plus_g = record.a; - state.accel_minus_g = record.b; - break; - case Altos.AO_LOG_RADIO_CAL: - break; - case Altos.AO_LOG_MANUFACTURER: - break; - case Altos.AO_LOG_PRODUCT: - break; - case Altos.AO_LOG_SERIAL_NUMBER: - state.serial = record.a; - break; - case Altos.AO_LOG_SOFTWARE_VERSION: - break; - } - } - - LinkedList make_list() { - LinkedList list = new LinkedList(); - Iterator iterator = records.iterator(); - AltosOrderedRecord record = null; - AltosRecord state = new AltosRecord(); - boolean last_reported = false; - EepromState eeprom = new EepromState(); - - state.state = Altos.ao_flight_pad; - state.accel_plus_g = 15758; - state.accel_minus_g = 16294; - - /* Pull in static data from the flight and gps_date records */ - if (flight_record != null) - update_state(state, flight_record, eeprom); - if (gps_date_record != null) - update_state(state, gps_date_record, eeprom); - - while (iterator.hasNext()) { - record = iterator.next(); - if ((eeprom.seen & seen_basic) == seen_basic && record.tick != state.tick) { - AltosRecord r = new AltosRecord(state); - r.time = (r.tick - eeprom.boost_tick) / 100.0; - list.add(r); - } - update_state(state, record, eeprom); - } - AltosRecord r = new AltosRecord(state); - r.time = (r.tick - eeprom.boost_tick) / 100.0; - list.add(r); - return list; - } - - public Iterator iterator() { - if (list == null) - list = make_list(); - return list.iterator(); - } - - public void write_comments(PrintStream out) { - Iterator iterator = records.iterator(); - out.printf("# Comments\n"); - while (iterator.hasNext()) { - AltosOrderedRecord record = iterator.next(); - switch (record.cmd) { - case Altos.AO_LOG_CONFIG_VERSION: - out.printf("# Config version: %s\n", record.data); - break; - case Altos.AO_LOG_MAIN_DEPLOY: - out.printf("# Main deploy: %s\n", record.a); - break; - case Altos.AO_LOG_APOGEE_DELAY: - out.printf("# Apogee delay: %s\n", record.a); - break; - case Altos.AO_LOG_RADIO_CHANNEL: - out.printf("# Radio channel: %s\n", record.a); - break; - case Altos.AO_LOG_CALLSIGN: - out.printf("# Callsign: %s\n", record.data); - break; - case Altos.AO_LOG_ACCEL_CAL: - out.printf ("# Accel cal: %d %d\n", record.a, record.b); - break; - case Altos.AO_LOG_RADIO_CAL: - out.printf ("# Radio cal: %d\n", record.a); - break; - case Altos.AO_LOG_MANUFACTURER: - out.printf ("# Manufacturer: %s\n", record.data); - break; - case Altos.AO_LOG_PRODUCT: - out.printf ("# Product: %s\n", record.data); - break; - case Altos.AO_LOG_SERIAL_NUMBER: - out.printf ("# Serial number: %d\n", record.a); - break; - case Altos.AO_LOG_SOFTWARE_VERSION: - out.printf ("# Software version: %s\n", record.data); - break; - } - } - } - - /* - * Given an AO_LOG_GPS_TIME record with correct time, and one - * missing time, rewrite the missing time values with the good - * ones, assuming that the difference between them is 'diff' seconds - */ - void update_time(AltosOrderedRecord good, AltosOrderedRecord bad) { - - int diff = (bad.tick - good.tick + 50) / 100; - - int hour = (good.a & 0xff); - int minute = (good.a >> 8); - int second = (good.b & 0xff); - int flags = (good.b >> 8); - int seconds = hour * 3600 + minute * 60 + second; - - /* Make sure this looks like a good GPS value */ - if ((flags & Altos.AO_GPS_NUM_SAT_MASK) >> Altos.AO_GPS_NUM_SAT_SHIFT < 4) - flags = (flags & ~Altos.AO_GPS_NUM_SAT_MASK) | (4 << Altos.AO_GPS_NUM_SAT_SHIFT); - flags |= Altos.AO_GPS_RUNNING; - flags |= Altos.AO_GPS_VALID; - - int new_seconds = seconds + diff; - if (new_seconds < 0) - new_seconds += 24 * 3600; - int new_second = (new_seconds % 60); - int new_minutes = (new_seconds / 60); - int new_minute = (new_minutes % 60); - int new_hours = (new_minutes / 60); - int new_hour = (new_hours % 24); - - bad.a = new_hour + (new_minute << 8); - bad.b = new_second + (flags << 8); - } - - /* - * Read the whole file, dumping records into a RB tree so - * we can enumerate them in time order -- the eeprom data - * are sometimes out of order with GPS data getting timestamps - * matching the first packet out of the GPS unit but not - * written until the final GPS packet has been received. - */ - public AltosEepromIterable (FileInputStream input) { - records = new TreeSet(); - - AltosOrderedRecord last_gps_time = null; - - int index = 0; - int prev_tick = 0; - boolean prev_tick_valid = false; - boolean missing_time = false; - - try { - for (;;) { - String line = AltosRecord.gets(input); - if (line == null) - break; - AltosOrderedRecord record = new AltosOrderedRecord(line, index++, prev_tick, prev_tick_valid); - if (record == null) - break; - if (record.cmd == Altos.AO_LOG_INVALID) - continue; - prev_tick = record.tick; - if (record.cmd < Altos.AO_LOG_CONFIG_VERSION) - prev_tick_valid = true; - if (record.cmd == Altos.AO_LOG_FLIGHT) { - flight_record = record; - continue; - } - - /* Two firmware bugs caused the loss of some GPS data. - * The flight date would never be recorded, and often - * the flight time would get overwritten by another - * record. Detect the loss of the GPS date and fix up the - * missing time records - */ - if (record.cmd == Altos.AO_LOG_GPS_DATE) { - gps_date_record = record; - continue; - } - - /* go back and fix up any missing time values */ - if (record.cmd == Altos.AO_LOG_GPS_TIME) { - last_gps_time = record; - if (missing_time) { - Iterator iterator = records.iterator(); - while (iterator.hasNext()) { - AltosOrderedRecord old = iterator.next(); - if (old.cmd == Altos.AO_LOG_GPS_TIME && - old.a == -1 && old.b == -1) - { - update_time(record, old); - } - } - missing_time = false; - } - } - - if (record.cmd == Altos.AO_LOG_GPS_LAT) { - if (last_gps_time == null || last_gps_time.tick != record.tick) { - AltosOrderedRecord add_gps_time = new AltosOrderedRecord(Altos.AO_LOG_GPS_TIME, - record.tick, - -1, -1, index-1); - if (last_gps_time != null) - update_time(last_gps_time, add_gps_time); - else - missing_time = true; - - records.add(add_gps_time); - record.index = index++; - } - } - records.add(record); - - /* Bail after reading the 'landed' record; we're all done */ - if (record.cmd == Altos.AO_LOG_STATE && - record.a == Altos.ao_flight_landed) - break; - } - } catch (IOException io) { - } catch (ParseException pe) { - } - try { - input.close(); - } catch (IOException ie) { - } - } -} diff --git a/ao-tools/altosui/AltosEepromMonitor.java b/ao-tools/altosui/AltosEepromMonitor.java deleted file mode 100644 index 7ff00ead..00000000 --- a/ao-tools/altosui/AltosEepromMonitor.java +++ /dev/null @@ -1,176 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosEepromMonitor extends JDialog { - - Container pane; - Box box; - JLabel serial_label; - JLabel flight_label; - JLabel file_label; - JLabel serial_value; - JLabel flight_value; - JLabel file_value; - JButton cancel; - JProgressBar pbar; - int min_state, max_state; - - public AltosEepromMonitor(JFrame owner, int in_min_state, int in_max_state) { - super (owner, "Download Flight Data", false); - - GridBagConstraints c; - Insets il = new Insets(4,4,4,4); - Insets ir = new Insets(4,4,4,4); - - pane = getContentPane(); - pane.setLayout(new GridBagLayout()); - - c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 0; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - serial_label = new JLabel("Serial:"); - pane.add(serial_label, c); - - c = new GridBagConstraints(); - c.gridx = 1; c.gridy = 0; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - serial_value = new JLabel(""); - pane.add(serial_value, c); - - c = new GridBagConstraints(); - c.fill = GridBagConstraints.NONE; - c.gridx = 0; c.gridy = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - flight_label = new JLabel("Flight:"); - pane.add(flight_label, c); - - c = new GridBagConstraints(); - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.gridx = 1; c.gridy = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - flight_value = new JLabel(""); - pane.add(flight_value, c); - - c = new GridBagConstraints(); - c.fill = GridBagConstraints.NONE; - c.gridx = 0; c.gridy = 2; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - file_label = new JLabel("File:"); - pane.add(file_label, c); - - c = new GridBagConstraints(); - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.gridx = 1; c.gridy = 2; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - file_value = new JLabel(""); - pane.add(file_value, c); - - min_state = in_min_state; - max_state = in_max_state; - pbar = new JProgressBar(); - pbar.setMinimum(0); - pbar.setMaximum((max_state - min_state) * 100); - pbar.setValue(0); - pbar.setString("startup"); - pbar.setStringPainted(true); - pbar.setPreferredSize(new Dimension(600, 20)); - c = new GridBagConstraints(); - c.fill = GridBagConstraints.HORIZONTAL; - c.anchor = GridBagConstraints.CENTER; - c.gridx = 0; c.gridy = 3; - c.gridwidth = GridBagConstraints.REMAINDER; - Insets ib = new Insets(4,4,4,4); - c.insets = ib; - pane.add(pbar, c); - - - cancel = new JButton("Cancel"); - c = new GridBagConstraints(); - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.CENTER; - c.gridx = 0; c.gridy = 4; - c.gridwidth = GridBagConstraints.REMAINDER; - Insets ic = new Insets(4,4,4,4); - c.insets = ic; - pane.add(cancel, c); - - pack(); - setLocationRelativeTo(owner); - setVisible(true); - } - - public void addActionListener (ActionListener l) { - cancel.addActionListener(l); - } - - public void set_value(String state_name, int in_state, int in_block) { - int block = in_block; - int state = in_state; - - if (block > 100) - block = 100; - if (state < min_state) state = min_state; - if (state >= max_state) state = max_state - 1; - state -= min_state; - - int pos = state * 100 + block; - - pbar.setString(state_name); - pbar.setValue(pos); - } - - public void set_serial(int serial) { - serial_value.setText(String.format("%d", serial)); - } - - public void set_flight(int flight) { - flight_value.setText(String.format("%d", flight)); - } - - public void set_file(String file) { - file_value.setText(String.format("%s", file)); - } - - public void done() { - setVisible(false); - dispose(); - } -} diff --git a/ao-tools/altosui/AltosEepromRecord.java b/ao-tools/altosui/AltosEepromRecord.java deleted file mode 100644 index 5a673817..00000000 --- a/ao-tools/altosui/AltosEepromRecord.java +++ /dev/null @@ -1,115 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosEepromRecord { - public int cmd; - public int tick; - public int a; - public int b; - public String data; - public boolean tick_valid; - - public AltosEepromRecord (String line) { - tick_valid = false; - tick = 0; - a = 0; - b = 0; - data = null; - if (line == null) { - cmd = Altos.AO_LOG_INVALID; - data = ""; - } else { - try { - String[] tokens = line.split("\\s+"); - - if (tokens[0].length() == 1) { - if (tokens.length != 4) { - cmd = Altos.AO_LOG_INVALID; - data = line; - } else { - cmd = tokens[0].codePointAt(0); - tick = Integer.parseInt(tokens[1],16); - tick_valid = true; - a = Integer.parseInt(tokens[2],16); - b = Integer.parseInt(tokens[3],16); - } - } else if (tokens[0].equals("Config") && tokens[1].equals("version:")) { - cmd = Altos.AO_LOG_CONFIG_VERSION; - data = tokens[2]; - } else if (tokens[0].equals("Main") && tokens[1].equals("deploy:")) { - cmd = Altos.AO_LOG_MAIN_DEPLOY; - a = Integer.parseInt(tokens[2]); - } else if (tokens[0].equals("Apogee") && tokens[1].equals("delay:")) { - cmd = Altos.AO_LOG_APOGEE_DELAY; - a = Integer.parseInt(tokens[2]); - } else if (tokens[0].equals("Radio") && tokens[1].equals("channel:")) { - cmd = Altos.AO_LOG_RADIO_CHANNEL; - a = Integer.parseInt(tokens[2]); - } else if (tokens[0].equals("Callsign:")) { - cmd = Altos.AO_LOG_CALLSIGN; - data = tokens[1].replaceAll("\"",""); - } else if (tokens[0].equals("Accel") && tokens[1].equals("cal")) { - cmd = Altos.AO_LOG_ACCEL_CAL; - a = Integer.parseInt(tokens[3]); - b = Integer.parseInt(tokens[5]); - } else if (tokens[0].equals("Radio") && tokens[1].equals("cal:")) { - cmd = Altos.AO_LOG_RADIO_CAL; - a = Integer.parseInt(tokens[2]); - } else if (tokens[0].equals("manufacturer")) { - cmd = Altos.AO_LOG_MANUFACTURER; - data = tokens[1]; - } else if (tokens[0].equals("product")) { - cmd = Altos.AO_LOG_PRODUCT; - data = tokens[1]; - } else if (tokens[0].equals("serial-number")) { - cmd = Altos.AO_LOG_SERIAL_NUMBER; - a = Integer.parseInt(tokens[1]); - } else if (tokens[0].equals("software-version")) { - cmd = Altos.AO_LOG_SOFTWARE_VERSION; - data = tokens[1]; - } else { - cmd = Altos.AO_LOG_INVALID; - data = line; - } - } catch (NumberFormatException ne) { - cmd = Altos.AO_LOG_INVALID; - data = line; - } - } - } - - public AltosEepromRecord(int in_cmd, int in_tick, int in_a, int in_b) { - tick_valid = true; - cmd = in_cmd; - tick = in_tick; - a = in_a; - b = in_b; - } -} diff --git a/ao-tools/altosui/AltosFile.java b/ao-tools/altosui/AltosFile.java deleted file mode 100644 index 06360572..00000000 --- a/ao-tools/altosui/AltosFile.java +++ /dev/null @@ -1,44 +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.lang.*; -import java.io.File; -import java.util.*; - -class AltosFile extends File { - - public AltosFile(int year, int month, int day, int serial, int flight, String extension) { - super (AltosPreferences.logdir(), - String.format("%04d-%02d-%02d-serial-%03d-flight-%03d.%s", - year, month, day, serial, flight, extension)); - } - - public AltosFile(int serial, int flight, String extension) { - this(Calendar.getInstance().get(Calendar.YEAR), - Calendar.getInstance().get(Calendar.MONTH) + 1, - Calendar.getInstance().get(Calendar.DAY_OF_MONTH), - serial, - flight, - extension); - } - - public AltosFile(AltosTelemetry telem) { - this(telem.serial, telem.flight, "telem"); - } -} diff --git a/ao-tools/altosui/AltosFlash.java b/ao-tools/altosui/AltosFlash.java deleted file mode 100644 index 3af25c23..00000000 --- a/ao-tools/altosui/AltosFlash.java +++ /dev/null @@ -1,344 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosFlash { - File file; - FileInputStream input; - AltosHexfile image; - JFrame frame; - AltosDevice debug_dongle; - AltosDebug debug; - AltosRomconfig rom_config; - ActionListener listener; - boolean aborted; - - static final byte MOV_direct_data = (byte) 0x75; - static final byte MOV_DPTR_data16 = (byte) 0x90; - static final byte MOV_A_data = (byte) 0x74; - static final byte MOVX_atDPTR_A = (byte) 0xf0; - static final byte MOVX_A_atDPTR = (byte) 0xe0; - static final byte INC_DPTR = (byte) 0xa3; - static final byte TRAP = (byte) 0xa5; - - static final byte JB = (byte) 0x20; - - static final byte MOV_A_direct = (byte) 0xe5; - static final byte MOV_direct1_direct2 = (byte) 0x85; - static final byte MOV_direct_A = (byte) 0xf5; - static final byte MOV_R0_data = (byte) (0x78 | 0); - static final byte MOV_R1_data = (byte) (0x78 | 1); - static final byte MOV_R2_data = (byte) (0x78 | 2); - static final byte MOV_R3_data = (byte) (0x78 | 3); - static final byte MOV_R4_data = (byte) (0x78 | 4); - static final byte MOV_R5_data = (byte) (0x78 | 5); - static final byte MOV_R6_data = (byte) (0x78 | 6); - static final byte MOV_R7_data = (byte) (0x78 | 7); - static final byte DJNZ_R0_rel = (byte) (0xd8 | 0); - static final byte DJNZ_R1_rel = (byte) (0xd8 | 1); - static final byte DJNZ_R2_rel = (byte) (0xd8 | 2); - static final byte DJNZ_R3_rel = (byte) (0xd8 | 3); - static final byte DJNZ_R4_rel = (byte) (0xd8 | 4); - static final byte DJNZ_R5_rel = (byte) (0xd8 | 5); - static final byte DJNZ_R6_rel = (byte) (0xd8 | 6); - static final byte DJNZ_R7_rel = (byte) (0xd8 | 7); - - static final byte P1DIR = (byte) 0xFE; - static final byte P1 = (byte) 0x90; - - /* flash controller */ - static final byte FWT = (byte) 0xAB; - static final byte FADDRL = (byte) 0xAC; - static final byte FADDRH = (byte) 0xAD; - static final byte FCTL = (byte) 0xAE; - static final byte FCTL_BUSY = (byte) 0x80; - static final byte FCTL_BUSY_BIT = (byte) 7; - static final byte FCTL_SWBSY = (byte) 0x40; - static final byte FCTL_SWBSY_BIT = (byte) 6; - static final byte FCTL_CONTRD = (byte) 0x10; - static final byte FCTL_WRITE = (byte) 0x02; - static final byte FCTL_ERASE = (byte) 0x01; - static final byte FWDATA = (byte) 0xAF; - - static final byte ACC = (byte) 0xE0; - - /* offsets within the flash_page program */ - static final int FLASH_ADDR_HIGH = 8; - static final int FLASH_ADDR_LOW = 11; - static final int RAM_ADDR_HIGH = 13; - static final int RAM_ADDR_LOW = 14; - static final int FLASH_WORDS_HIGH = 16; - static final int FLASH_WORDS_LOW = 18; - static final int FLASH_TIMING = 21; - - /* sleep mode control */ - static final int SLEEP = (byte) 0xbe; - static final int SLEEP_USB_EN = (byte) 0x80; - static final int SLEEP_XOSC_STB = (byte) 0x40; - static final int SLEEP_HFRC_STB = (byte) 0x20; - static final int SLEEP_RST_MASK = (byte) 0x18; - static final int SLEEP_RST_POWERON = (byte) 0x00; - static final int SLEEP_RST_EXTERNAL = (byte) 0x10; - static final int SLEEP_RST_WATCHDOG = (byte) 0x08; - static final int SLEEP_OSC_PD = (byte) 0x04; - static final int SLEEP_MODE_MASK = (byte) 0x03; - static final int SLEEP_MODE_PM0 = (byte) 0x00; - static final int SLEEP_MODE_PM1 = (byte) 0x01; - static final int SLEEP_MODE_PM2 = (byte) 0x02; - static final int SLEEP_MODE_PM3 = (byte) 0x03; - - /* clock controller */ - static final byte CLKCON = (byte) 0xC6; - static final byte CLKCON_OSC32K = (byte) 0x80; - static final byte CLKCON_OSC = (byte) 0x40; - static final byte CLKCON_TICKSPD = (byte) 0x38; - static final byte CLKCON_CLKSPD = (byte) 0x07; - - static final byte[] flash_page_proto = { - - MOV_direct_data, P1DIR, (byte) 0x02, - MOV_direct_data, P1, (byte) 0xFF, - - MOV_direct_data, FADDRH, 0, /* FLASH_ADDR_HIGH */ - - MOV_direct_data, FADDRL, 0, /* FLASH_ADDR_LOW */ - - MOV_DPTR_data16, 0, 0, /* RAM_ADDR_HIGH, RAM_ADDR_LOW */ - - MOV_R7_data, 0, /* FLASH_WORDS_HIGH */ - - MOV_R6_data, 0, /* FLASH_WORDS_LOW */ - - - MOV_direct_data, FWT, 0x20, /* FLASH_TIMING */ - - MOV_direct_data, FCTL, FCTL_ERASE, -/* eraseWaitLoop: */ - MOV_A_direct, FCTL, - JB, ACC|FCTL_BUSY_BIT, (byte) 0xfb, - - MOV_direct_data, P1, (byte) 0xfd, - - MOV_direct_data, FCTL, FCTL_WRITE, -/* writeLoop: */ - MOV_R5_data, 2, -/* writeWordLoop: */ - MOVX_A_atDPTR, - INC_DPTR, - MOV_direct_A, FWDATA, - DJNZ_R5_rel, (byte) 0xfa, /* writeWordLoop */ -/* writeWaitLoop: */ - MOV_A_direct, FCTL, - JB, ACC|FCTL_SWBSY_BIT, (byte) 0xfb, /* writeWaitLoop */ - DJNZ_R6_rel, (byte) 0xf1, /* writeLoop */ - DJNZ_R7_rel, (byte) 0xef, /* writeLoop */ - - MOV_direct_data, P1DIR, (byte) 0x00, - MOV_direct_data, P1, (byte) 0xFF, - TRAP, - }; - - public byte[] make_flash_page(int flash_addr, int ram_addr, int byte_count) { - int flash_word_addr = flash_addr >> 1; - int flash_word_count = ((byte_count + 1) >> 1); - - byte[] flash_page = new byte[flash_page_proto.length]; - for (int i = 0; i < flash_page.length; i++) - flash_page[i] = flash_page_proto[i]; - - flash_page[FLASH_ADDR_HIGH] = (byte) (flash_word_addr >> 8); - flash_page[FLASH_ADDR_LOW] = (byte) (flash_word_addr); - flash_page[RAM_ADDR_HIGH] = (byte) (ram_addr >> 8); - flash_page[RAM_ADDR_LOW] = (byte) (ram_addr); - - byte flash_words_low = (byte) (flash_word_count); - byte flash_words_high = (byte) (flash_word_count >> 8); - /* the flashing code has a minor 'bug' */ - if (flash_words_low != 0) - flash_words_high++; - - flash_page[FLASH_WORDS_HIGH] = (byte) flash_words_high; - flash_page[FLASH_WORDS_LOW] = (byte) flash_words_low; - return flash_page; - } - - static byte[] set_clkcon_fast = { - MOV_direct_data, CLKCON, 0x00 - }; - - static byte[] get_sleep = { - MOV_A_direct, SLEEP - }; - - public void clock_init() throws IOException, InterruptedException { - debug.debug_instr(set_clkcon_fast); - - byte status; - for (int times = 0; times < 20; times++) { - Thread.sleep(1); - status = debug.debug_instr(get_sleep); - if ((status & SLEEP_XOSC_STB) != 0) - return; - } - throw new IOException("Failed to initialize target clock"); - } - - void action(String s, int percent) { - if (listener != null && !aborted) - listener.actionPerformed(new ActionEvent(this, - percent, - s)); - } - - void action(int part, int total) { - int percent = 100 * part / total; - action(String.format("%d/%d (%d%%)", - part, total, percent), - percent); - } - - void run(int pc) throws IOException, InterruptedException { - debug.set_pc(pc); - int set_pc = debug.get_pc(); - if (pc != set_pc) - throw new IOException("Failed to set target program counter"); - debug.resume(); - - for (int times = 0; times < 20; times++) { - byte status = debug.read_status(); - if ((status & AltosDebug.STATUS_CPU_HALTED) != 0) - return; - } - - throw new IOException("Failed to execute program on target"); - } - - public void flash() throws IOException, FileNotFoundException, InterruptedException { - if (!check_rom_config()) - throw new IOException("Invalid rom config settings"); - if (image.address + image.data.length > 0x8000) - throw new IOException(String.format("Flash image too long %d", - image.address + - image.data.length)); - if ((image.address & 0x3ff) != 0) - throw new IOException(String.format("Flash image must start on page boundary (is 0x%x)", - image.address)); - int ram_address = 0xf000; - int flash_prog = 0xf400; - - /* - * Store desired config values into image - */ - rom_config.write(image); - /* - * Bring up the clock - */ - clock_init(); - - int remain = image.data.length; - int flash_addr = image.address; - int image_start = 0; - - action("start", 0); - action(0, image.data.length); - while (remain > 0 && !aborted) { - int this_time = remain; - if (this_time > 0x400) - this_time = 0x400; - - /* write the data */ - debug.write_memory(ram_address, image.data, - image_start, this_time); - - /* write the flash program */ - byte[] flash_page = make_flash_page(flash_addr, - ram_address, - this_time); - debug.write_memory(flash_prog, flash_page); - - run(flash_prog); - - byte[] check = debug.read_memory(flash_addr, this_time); - for (int i = 0; i < this_time; i++) - if (check[i] != image.data[image_start + i]) - throw new IOException(String.format("Flash write failed at 0x%x (%02x != %02x)", - image.address + image_start + i, - check[i], image.data[image_start + i])); - remain -= this_time; - flash_addr += this_time; - image_start += this_time; - - action(image.data.length - remain, image.data.length); - } - if (!aborted) { - action("done", 100); - debug.set_pc(image.address); - debug.resume(); - } - debug.close(); - } - - public void abort() { - aborted = true; - debug.close(); - } - - public void addActionListener(ActionListener l) { - listener = l; - } - - public boolean check_rom_config() { - if (rom_config == null) - rom_config = debug.romconfig(); - return rom_config != null && rom_config.valid(); - } - - public void set_romconfig (AltosRomconfig romconfig) { - rom_config = romconfig; - } - - public AltosRomconfig romconfig() { - if (!check_rom_config()) - return null; - return rom_config; - } - - public AltosFlash(File in_file, AltosDevice in_debug_dongle) - throws IOException, FileNotFoundException, AltosSerialInUseException, InterruptedException { - file = in_file; - debug_dongle = in_debug_dongle; - debug = new AltosDebug(in_debug_dongle); - input = new FileInputStream(file); - image = new AltosHexfile(input); - if (!debug.check_connection()) { - debug.close(); - throw new IOException("Debug port not connected"); - } - } -} \ No newline at end of file diff --git a/ao-tools/altosui/AltosFlashUI.java b/ao-tools/altosui/AltosFlashUI.java deleted file mode 100644 index f63097ac..00000000 --- a/ao-tools/altosui/AltosFlashUI.java +++ /dev/null @@ -1,218 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosFlashUI - extends JDialog - implements Runnable, ActionListener -{ - Container pane; - Box box; - JLabel serial_label; - JLabel serial_value; - JLabel file_label; - JLabel file_value; - JProgressBar pbar; - JButton cancel; - - File file; - Thread thread; - JFrame frame; - AltosDevice debug_dongle; - AltosFlash flash; - - public void actionPerformed(ActionEvent e) { - if (e.getSource() == cancel) { - abort(); - dispose(); - } else { - String cmd = e.getActionCommand(); - if (cmd.equals("done")) - ; - else if (cmd.equals("start")) { - setVisible(true); - } else { - pbar.setValue(e.getID()); - pbar.setString(cmd); - } - } - } - - public void run() { - try { - flash = new AltosFlash(file, debug_dongle); - flash.addActionListener(this); - AltosRomconfigUI romconfig_ui = new AltosRomconfigUI (frame); - - romconfig_ui.set(flash.romconfig()); - AltosRomconfig romconfig = romconfig_ui.showDialog(); - - if (romconfig != null && romconfig.valid()) { - flash.set_romconfig(romconfig); - serial_value.setText(String.format("%d", - flash.romconfig().serial_number)); - file_value.setText(file.toString()); - setVisible(true); - flash.flash(); - flash = null; - } - } catch (FileNotFoundException ee) { - JOptionPane.showMessageDialog(frame, - "Cannot open image", - file.toString(), - JOptionPane.ERROR_MESSAGE); - } catch (AltosSerialInUseException si) { - JOptionPane.showMessageDialog(frame, - String.format("Device \"%s\" already in use", - debug_dongle.toShortString()), - "Device in use", - JOptionPane.ERROR_MESSAGE); - } catch (IOException e) { - JOptionPane.showMessageDialog(frame, - e.getMessage(), - file.toString(), - JOptionPane.ERROR_MESSAGE); - } catch (InterruptedException ie) { - } finally { - abort(); - } - dispose(); - } - - public void abort() { - if (flash != null) - flash.abort(); - } - - public void build_dialog() { - GridBagConstraints c; - Insets il = new Insets(4,4,4,4); - Insets ir = new Insets(4,4,4,4); - - pane = getContentPane(); - pane.setLayout(new GridBagLayout()); - - c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 0; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - serial_label = new JLabel("Serial:"); - pane.add(serial_label, c); - - c = new GridBagConstraints(); - c.gridx = 1; c.gridy = 0; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - serial_value = new JLabel(""); - pane.add(serial_value, c); - - c = new GridBagConstraints(); - c.fill = GridBagConstraints.NONE; - c.gridx = 0; c.gridy = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - file_label = new JLabel("File:"); - pane.add(file_label, c); - - c = new GridBagConstraints(); - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.gridx = 1; c.gridy = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - file_value = new JLabel(""); - pane.add(file_value, c); - - pbar = new JProgressBar(); - pbar.setMinimum(0); - pbar.setMaximum(100); - pbar.setValue(0); - pbar.setString(""); - pbar.setStringPainted(true); - pbar.setPreferredSize(new Dimension(600, 20)); - c = new GridBagConstraints(); - c.fill = GridBagConstraints.HORIZONTAL; - c.anchor = GridBagConstraints.CENTER; - c.gridx = 0; c.gridy = 2; - c.gridwidth = GridBagConstraints.REMAINDER; - Insets ib = new Insets(4,4,4,4); - c.insets = ib; - pane.add(pbar, c); - - cancel = new JButton("Cancel"); - c = new GridBagConstraints(); - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.CENTER; - c.gridx = 0; c.gridy = 3; - c.gridwidth = GridBagConstraints.REMAINDER; - Insets ic = new Insets(4,4,4,4); - c.insets = ic; - pane.add(cancel, c); - cancel.addActionListener(this); - pack(); - setLocationRelativeTo(frame); - } - - public AltosFlashUI(JFrame in_frame) { - super(in_frame, "Program Altusmetrum Device", false); - - frame = in_frame; - - build_dialog(); - - debug_dongle = AltosDeviceDialog.show(frame, AltosDevice.product_any); - - if (debug_dongle == null) - return; - - JFileChooser hexfile_chooser = new JFileChooser(); - - File firmwaredir = AltosPreferences.firmwaredir(); - if (firmwaredir != null) - hexfile_chooser.setCurrentDirectory(firmwaredir); - - hexfile_chooser.setDialogTitle("Select Flash Image"); - hexfile_chooser.setFileFilter(new FileNameExtensionFilter("Flash Image", "ihx")); - int returnVal = hexfile_chooser.showOpenDialog(frame); - - if (returnVal != JFileChooser.APPROVE_OPTION) - return; - - file = hexfile_chooser.getSelectedFile(); - - if (file != null) - AltosPreferences.set_firmwaredir(file.getParentFile()); - - thread = new Thread(this); - thread.start(); - } -} \ No newline at end of file diff --git a/ao-tools/altosui/AltosFlightDisplay.java b/ao-tools/altosui/AltosFlightDisplay.java deleted file mode 100644 index d18d1d1f..00000000 --- a/ao-tools/altosui/AltosFlightDisplay.java +++ /dev/null @@ -1,24 +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; - -public interface AltosFlightDisplay { - void reset(); - - void show(AltosState state, int crc_errors); -} diff --git a/ao-tools/altosui/AltosFlightInfoTableModel.java b/ao-tools/altosui/AltosFlightInfoTableModel.java deleted file mode 100644 index e23eff68..00000000 --- a/ao-tools/altosui/AltosFlightInfoTableModel.java +++ /dev/null @@ -1,84 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosFlightInfoTableModel extends AbstractTableModel { - final static private String[] columnNames = {"Field", "Value"}; - - int rows; - int cols; - private String[][] data; - - public int getColumnCount() { return cols; } - public int getRowCount() { return rows; } - public String getColumnName(int col) { return columnNames[col & 1]; } - - public Object getValueAt(int row, int col) { - if (row >= rows || col >= cols) - return ""; - return data[row][col]; - } - - int[] current_row; - - public void reset() { - for (int i = 0; i < cols / 2; i++) - current_row[i] = 0; - } - - public void clear() { - reset(); - for (int c = 0; c < cols; c++) - for (int r = 0; r < rows; r++) - data[r][c] = ""; - fireTableDataChanged(); - } - - public void addRow(int col, String name, String value) { - if (current_row[col] < rows) { - data[current_row[col]][col * 2] = name; - data[current_row[col]][col * 2 + 1] = value; - } - current_row[col]++; - } - - public void finish() { - for (int c = 0; c < cols / 2; c++) - while (current_row[c] < rows) - addRow(c, "", ""); - fireTableDataChanged(); - } - - public AltosFlightInfoTableModel (int in_rows, int in_cols) { - rows = in_rows; - cols = in_cols * 2; - data = new String[rows][cols]; - current_row = new int[in_cols]; - } -} diff --git a/ao-tools/altosui/AltosFlightReader.java b/ao-tools/altosui/AltosFlightReader.java deleted file mode 100644 index 3d59de9a..00000000 --- a/ao-tools/altosui/AltosFlightReader.java +++ /dev/null @@ -1,38 +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.lang.*; -import java.text.*; -import java.io.*; - -public class AltosFlightReader { - String name; - - int serial; - - void init() { } - - AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; } - - void close(boolean interrupted) { } - - void set_channel(int channel) { } - - void update(AltosState state) throws InterruptedException { } -} diff --git a/ao-tools/altosui/AltosFlightStatus.java b/ao-tools/altosui/AltosFlightStatus.java deleted file mode 100644 index 59c9e9db..00000000 --- a/ao-tools/altosui/AltosFlightStatus.java +++ /dev/null @@ -1,154 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosFlightStatus extends JComponent implements AltosFlightDisplay { - GridBagLayout layout; - - public class FlightValue { - JLabel label; - JTextField value; - - void show(AltosState state, int crc_errors) {} - - void reset() { - value.setText(""); - } - public FlightValue (GridBagLayout layout, int x, String text) { - GridBagConstraints c = new GridBagConstraints(); - c.insets = new Insets(5, 5, 5, 5); - c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - c.weighty = 1; - - label = new JLabel(text); - label.setFont(Altos.status_font); - label.setHorizontalAlignment(SwingConstants.CENTER); - c.gridx = x; c.gridy = 0; - layout.setConstraints(label, c); - add(label); - - value = new JTextField(""); - value.setFont(Altos.status_font); - value.setHorizontalAlignment(SwingConstants.CENTER); - c.gridx = x; c.gridy = 1; - layout.setConstraints(value, c); - add(value); - } - } - - class Call extends FlightValue { - void show(AltosState state, int crc_errors) { - value.setText(state.data.callsign); - } - public Call (GridBagLayout layout, int x) { - super (layout, x, "Callsign"); - } - } - - Call call; - - class Serial extends FlightValue { - void show(AltosState state, int crc_errors) { - value.setText(String.format("%d", state.data.serial)); - } - public Serial (GridBagLayout layout, int x) { - super (layout, x, "Serial"); - } - } - - Serial serial; - - class Flight extends FlightValue { - void show(AltosState state, int crc_errors) { - value.setText(String.format("%d", state.data.flight)); - } - public Flight (GridBagLayout layout, int x) { - super (layout, x, "Flight"); - } - } - - Flight flight; - - class FlightState extends FlightValue { - void show(AltosState state, int crc_errors) { - value.setText(state.data.state()); - } - public FlightState (GridBagLayout layout, int x) { - super (layout, x, "State"); - } - } - - FlightState flight_state; - - class RSSI extends FlightValue { - void show(AltosState state, int crc_errors) { - value.setText(String.format("%d", state.data.rssi)); - } - public RSSI (GridBagLayout layout, int x) { - super (layout, x, "RSSI (dBm)"); - } - } - - RSSI rssi; - - public void reset () { - call.reset(); - serial.reset(); - flight.reset(); - flight_state.reset(); - rssi.reset(); - } - - public void show (AltosState state, int crc_errors) { - call.show(state, crc_errors); - serial.show(state, crc_errors); - flight.show(state, crc_errors); - flight_state.show(state, crc_errors); - rssi.show(state, crc_errors); - } - - public int height() { - Dimension d = layout.preferredLayoutSize(this); - return d.height; - } - - public AltosFlightStatus() { - layout = new GridBagLayout(); - - setLayout(layout); - - call = new Call(layout, 0); - serial = new Serial(layout, 1); - flight = new Flight(layout, 2); - flight_state = new FlightState(layout, 3); - rssi = new RSSI(layout, 4); - } -} diff --git a/ao-tools/altosui/AltosFlightStatusTableModel.java b/ao-tools/altosui/AltosFlightStatusTableModel.java deleted file mode 100644 index 4c24b6ac..00000000 --- a/ao-tools/altosui/AltosFlightStatusTableModel.java +++ /dev/null @@ -1,61 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosFlightStatusTableModel extends AbstractTableModel { - private String[] columnNames = {"Height (m)", "State", "RSSI (dBm)", "Speed (m/s)" }; - private Object[] data = { 0, "idle", 0, 0 }; - - public int getColumnCount() { return columnNames.length; } - public int getRowCount() { return 2; } - public Object getValueAt(int row, int col) { - if (row == 0) - return columnNames[col]; - return data[col]; - } - - public void setValueAt(Object value, int col) { - data[col] = value; - fireTableCellUpdated(1, col); - } - - public void setValueAt(Object value, int row, int col) { - setValueAt(value, col); - } - - public void set(AltosState state) { - setValueAt(String.format("%1.0f", state.height), 0); - setValueAt(state.data.state(), 1); - setValueAt(state.data.rssi, 2); - double speed = state.baro_speed; - if (state.ascent) - speed = state.speed; - setValueAt(String.format("%1.0f", speed), 3); - } -} diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java deleted file mode 100644 index 24d25bd7..00000000 --- a/ao-tools/altosui/AltosFlightUI.java +++ /dev/null @@ -1,221 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosFlightUI extends JFrame implements AltosFlightDisplay { - String[] statusNames = { "Height (m)", "State", "RSSI (dBm)", "Speed (m/s)" }; - Object[][] statusData = { { "0", "pad", "-50", "0" } }; - - AltosVoice voice; - AltosFlightReader reader; - AltosDisplayThread thread; - - JTabbedPane pane; - - AltosPad pad; - AltosAscent ascent; - AltosDescent descent; - AltosLanded landed; - AltosSiteMap sitemap; - - private AltosFlightStatus flightStatus; - private AltosInfoTable flightInfo; - - static final int tab_pad = 1; - static final int tab_ascent = 2; - static final int tab_descent = 3; - static final int tab_landed = 4; - - int cur_tab = 0; - - boolean exit_on_close = false; - - int which_tab(AltosState state) { - if (state.state < Altos.ao_flight_boost) - return tab_pad; - if (state.state <= Altos.ao_flight_coast) - return tab_ascent; - if (state.state <= Altos.ao_flight_main) - return tab_descent; - return tab_landed; - } - - void stop_display() { - if (thread != null && thread.isAlive()) { - thread.interrupt(); - try { - thread.join(); - } catch (InterruptedException ie) {} - } - thread = null; - } - - void disconnect() { - stop_display(); - } - - public void reset() { - pad.reset(); - ascent.reset(); - descent.reset(); - landed.reset(); - flightInfo.clear(); - sitemap.reset(); - } - - public void show(AltosState state, int crc_errors) { - int tab = which_tab(state); - pad.show(state, crc_errors); - ascent.show(state, crc_errors); - descent.show(state, crc_errors); - landed.show(state, crc_errors); - if (tab != cur_tab) { - switch (tab) { - case tab_pad: - pane.setSelectedComponent(pad); - break; - case tab_ascent: - pane.setSelectedComponent(ascent); - break; - case tab_descent: - pane.setSelectedComponent(descent); - break; - case tab_landed: - pane.setSelectedComponent(landed); - } - cur_tab = tab; - } - flightStatus.show(state, crc_errors); - flightInfo.show(state, crc_errors); - sitemap.show(state, crc_errors); - } - - public void set_exit_on_close() { - exit_on_close = true; - } - - Container bag; - JComboBox channels; - - 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)); - - /* Stick channel selector at top of table for telemetry monitoring */ - if (serial >= 0) { - // Channel menu - channels = new AltosChannelMenu(AltosPreferences.channel(serial)); - channels.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int channel = channels.getSelectedIndex(); - reader.set_channel(channel); - } - }); - c.gridx = 0; - c.gridy = 0; - c.anchor = GridBagConstraints.WEST; - bag.add (channels, c); - } - - /* Flight status is always visible */ - flightStatus = new AltosFlightStatus(); - c.gridx = 0; - c.gridy = 1; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - bag.add(flightStatus, c); - - /* The rest of the window uses a tabbed pane to - * show one of the alternate data views - */ - pane = new JTabbedPane(); - - pad = new AltosPad(); - pane.add("Launch Pad", pad); - - ascent = new AltosAscent(); - pane.add("Ascent", ascent); - - descent = new AltosDescent(); - pane.add("Descent", descent); - - landed = new AltosLanded(); - pane.add("Landed", landed); - - flightInfo = new AltosInfoTable(); - pane.add("Table", new JScrollPane(flightInfo)); - - sitemap = new AltosSiteMap(); - pane.add("Site Map", sitemap); - - /* Make the tabbed pane use the rest of the window space */ - c.gridx = 0; - c.gridy = 2; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - c.weighty = 1; - bag.add(pane, c); - - setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - disconnect(); - setVisible(false); - dispose(); - if (exit_on_close) - System.exit(0); - } - }); - - pack(); - setVisible(true); - - thread = new AltosDisplayThread(this, voice, this, reader); - - thread.start(); - } - - public AltosFlightUI (AltosVoice in_voice, AltosFlightReader in_reader) { - this(in_voice, in_reader, -1); - } -} diff --git a/ao-tools/altosui/AltosGPS.java b/ao-tools/altosui/AltosGPS.java deleted file mode 100644 index 83821842..00000000 --- a/ao-tools/altosui/AltosGPS.java +++ /dev/null @@ -1,215 +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.lang.*; -import java.text.*; - -public class AltosGPS { - public class AltosGPSSat { - int svid; - int c_n0; - } - - int nsat; - boolean locked; - boolean connected; - boolean date_valid; - double lat; /* degrees (+N -S) */ - double lon; /* degrees (+E -W) */ - int alt; /* m */ - int year; - int month; - int day; - int hour; - int minute; - int second; - - int gps_extended; /* has extra data */ - double ground_speed; /* m/s */ - int course; /* degrees */ - double climb_rate; /* m/s */ - double hdop; /* unitless? */ - int h_error; /* m */ - int v_error; /* m */ - - AltosGPSSat[] cc_gps_sat; /* tracking data */ - - void ParseGPSDate(String date) throws ParseException { - String[] ymd = date.split("-"); - if (ymd.length != 3) - throw new ParseException("error parsing GPS date " + date + " got " + ymd.length, 0); - year = AltosParse.parse_int(ymd[0]); - month = AltosParse.parse_int(ymd[1]); - day = AltosParse.parse_int(ymd[2]); - } - - void ParseGPSTime(String time) throws ParseException { - String[] hms = time.split(":"); - if (hms.length != 3) - throw new ParseException("Error parsing GPS time " + time + " got " + hms.length, 0); - hour = AltosParse.parse_int(hms[0]); - minute = AltosParse.parse_int(hms[1]); - second = AltosParse.parse_int(hms[2]); - } - - void ClearGPSTime() { - year = month = day = 0; - hour = minute = second = 0; - } - - public AltosGPS(String[] words, int i, int version) throws ParseException { - AltosParse.word(words[i++], "GPS"); - nsat = AltosParse.parse_int(words[i++]); - AltosParse.word(words[i++], "sat"); - - connected = false; - locked = false; - lat = lon = 0; - alt = 0; - ClearGPSTime(); - if ((words[i]).equals("unlocked")) { - connected = true; - i++; - } else if ((words[i]).equals("not-connected")) { - i++; - } else if (words.length >= 40) { - locked = true; - connected = true; - - if (version > 1) - ParseGPSDate(words[i++]); - else - year = month = day = 0; - ParseGPSTime(words[i++]); - lat = AltosParse.parse_coord(words[i++]); - lon = AltosParse.parse_coord(words[i++]); - alt = AltosParse.parse_int(words[i++]); - if (version > 1 || (i < words.length && !words[i].equals("SAT"))) { - ground_speed = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(H)")); - course = AltosParse.parse_int(words[i++]); - climb_rate = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(V)")); - hdop = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "(hdop)")); - h_error = AltosParse.parse_int(words[i++]); - v_error = AltosParse.parse_int(words[i++]); - } - } else { - i++; - } - if (i < words.length) { - AltosParse.word(words[i++], "SAT"); - int tracking_channels = 0; - if (words[i].equals("not-connected")) - tracking_channels = 0; - else - tracking_channels = AltosParse.parse_int(words[i]); - i++; - cc_gps_sat = new AltosGPS.AltosGPSSat[tracking_channels]; - for (int chan = 0; chan < tracking_channels; chan++) { - cc_gps_sat[chan] = new AltosGPS.AltosGPSSat(); - cc_gps_sat[chan].svid = AltosParse.parse_int(words[i++]); - /* Older versions included SiRF status bits */ - if (version < 2) - i++; - cc_gps_sat[chan].c_n0 = AltosParse.parse_int(words[i++]); - } - } else - cc_gps_sat = new AltosGPS.AltosGPSSat[0]; - } - - public void set_latitude(int in_lat) { - lat = in_lat / 10.0e7; - } - - public void set_longitude(int in_lon) { - lon = in_lon / 10.0e7; - } - - public void set_time(int hour, int minute, int second) { - hour = hour; - minute = minute; - second = second; - } - - public void set_date(int year, int month, int day) { - year = year; - month = month; - day = day; - } - - public void set_flags(int flags) { - flags = flags; - } - - public void set_altitude(int altitude) { - altitude = altitude; - } - - public void add_sat(int svid, int c_n0) { - if (cc_gps_sat == null) { - cc_gps_sat = new AltosGPS.AltosGPSSat[1]; - } else { - AltosGPSSat[] new_gps_sat = new AltosGPS.AltosGPSSat[cc_gps_sat.length + 1]; - for (int i = 0; i < cc_gps_sat.length; i++) - new_gps_sat[i] = cc_gps_sat[i]; - cc_gps_sat = new_gps_sat; - } - AltosGPS.AltosGPSSat sat = new AltosGPS.AltosGPSSat(); - sat.svid = svid; - sat.c_n0 = c_n0; - cc_gps_sat[cc_gps_sat.length - 1] = sat; - } - - public AltosGPS() { - ClearGPSTime(); - cc_gps_sat = null; - } - - public AltosGPS(AltosGPS old) { - nsat = old.nsat; - locked = old.locked; - connected = old.connected; - date_valid = old.date_valid; - lat = old.lat; /* degrees (+N -S) */ - lon = old.lon; /* degrees (+E -W) */ - alt = old.alt; /* m */ - year = old.year; - month = old.month; - day = old.day; - hour = old.hour; - minute = old.minute; - second = old.second; - - gps_extended = old.gps_extended; /* has extra data */ - ground_speed = old.ground_speed; /* m/s */ - course = old.course; /* degrees */ - climb_rate = old.climb_rate; /* m/s */ - hdop = old.hdop; /* unitless? */ - h_error = old.h_error; /* m */ - v_error = old.v_error; /* m */ - - if (old.cc_gps_sat != null) { - cc_gps_sat = new AltosGPSSat[old.cc_gps_sat.length]; - for (int i = 0; i < old.cc_gps_sat.length; i++) { - cc_gps_sat[i] = new AltosGPSSat(); - cc_gps_sat[i].svid = old.cc_gps_sat[i].svid; - cc_gps_sat[i].c_n0 = old.cc_gps_sat[i].c_n0; - } - } - } -} diff --git a/ao-tools/altosui/AltosGraph.java b/ao-tools/altosui/AltosGraph.java deleted file mode 100644 index 58c27979..00000000 --- a/ao-tools/altosui/AltosGraph.java +++ /dev/null @@ -1,25 +0,0 @@ - -// Copyright (c) 2010 Anthony Towns -// GPL v2 or later - -package altosui; - -import java.io.*; - -import org.jfree.chart.JFreeChart; -import org.jfree.chart.ChartUtilities; - -abstract class AltosGraph { - public String filename; - public abstract void addData(AltosDataPoint d); - public abstract JFreeChart createChart(); - public void toPNG() throws java.io.IOException { toPNG(300, 500); } - public void toPNG(int width, int height) - throws java.io.IOException - { - File pngout = new File(filename); - JFreeChart chart = createChart(); - ChartUtilities.saveChartAsPNG(pngout, chart, width, height); - System.out.println("Created " + filename); - } -} diff --git a/ao-tools/altosui/AltosGraphTime.java b/ao-tools/altosui/AltosGraphTime.java deleted file mode 100644 index a5451280..00000000 --- a/ao-tools/altosui/AltosGraphTime.java +++ /dev/null @@ -1,233 +0,0 @@ - -// Copyright (c) 2010 Anthony Towns -// GPL v2 or later - -package altosui; - -import java.awt.Color; -import java.util.ArrayList; -import java.util.HashMap; - -import org.jfree.chart.ChartUtilities; -import org.jfree.chart.JFreeChart; -import org.jfree.chart.axis.AxisLocation; -import org.jfree.chart.axis.NumberAxis; -import org.jfree.chart.labels.StandardXYToolTipGenerator; -import org.jfree.chart.plot.PlotOrientation; -import org.jfree.chart.plot.XYPlot; -import org.jfree.chart.plot.ValueMarker; -import org.jfree.chart.renderer.xy.StandardXYItemRenderer; -import org.jfree.chart.renderer.xy.XYItemRenderer; -import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; -import org.jfree.data.xy.XYSeries; -import org.jfree.data.xy.XYSeriesCollection; -import org.jfree.ui.RectangleAnchor; -import org.jfree.ui.TextAnchor; - -class AltosGraphTime extends AltosGraph { - static interface Element { - void attachGraph(AltosGraphTime g); - void gotTimeData(double time, AltosDataPoint d); - void addToPlot(AltosGraphTime g, XYPlot plot); - } - - static class TimeAxis implements Element { - private int axis; - private Color color; - private String label; - private AxisLocation locn; - private double min_y = Double.NaN; - - public TimeAxis(int axis, String label, Color color, AxisLocation locn) - { - this.axis = axis; - this.color = color; - this.label = label; - this.locn = locn; - } - - public void setLowerBound(double min_y) { - this.min_y = min_y; - } - - public void attachGraph(AltosGraphTime g) { return; } - public void gotTimeData(double time, AltosDataPoint d) { return; } - - public void addToPlot(AltosGraphTime g, XYPlot plot) { - NumberAxis numAxis = new NumberAxis(label); - if (!Double.isNaN(min_y)) - numAxis.setLowerBound(min_y); - plot.setRangeAxis(axis, numAxis); - plot.setRangeAxisLocation(axis, locn); - numAxis.setLabelPaint(color); - numAxis.setTickLabelPaint(color); - numAxis.setAutoRangeIncludesZero(false); - } - } - - abstract static class TimeSeries implements Element { - protected XYSeries series; - private String axisName; - private Color color; - - public TimeSeries(String axisName, String label, Color color) { - this.series = new XYSeries(label); - this.axisName = axisName; - this.color = color; - } - - public void attachGraph(AltosGraphTime g) { - g.setAxis(this, axisName, color); - } - abstract public void gotTimeData(double time, AltosDataPoint d); - - public void addToPlot(AltosGraphTime g, XYPlot plot) { - XYSeriesCollection dataset = new XYSeriesCollection(); - dataset.addSeries(this.series); - - XYItemRenderer renderer = new StandardXYItemRenderer(); - renderer.setSeriesPaint(0, color); - - int dataNum = g.getDataNum(this); - int axisNum = g.getAxisNum(this); - - plot.setDataset(dataNum, dataset); - plot.mapDatasetToRangeAxis(dataNum, axisNum); - plot.setRenderer(dataNum, renderer); - } - } - - static class StateMarker implements Element { - private double val = Double.NaN; - private String name; - private int state; - - StateMarker(int state, String name) { - this.state = state; - this.name = name; - } - - public void attachGraph(AltosGraphTime g) { return; } - public void gotTimeData(double time, AltosDataPoint d) { - if (Double.isNaN(val) || time < val) { - if (d.state() == state) { - val = time; - } - } - } - - public void addToPlot(AltosGraphTime g, XYPlot plot) { - if (Double.isNaN(val)) - return; - - ValueMarker m = new ValueMarker(val); - m.setLabel(name); - m.setLabelAnchor(RectangleAnchor.TOP_RIGHT); - m.setLabelTextAnchor(TextAnchor.TOP_LEFT); - plot.addDomainMarker(m); - } - } - - private String callsign = null; - private Integer serial = null; - private Integer flight = null; - - private String title; - private ArrayList elements; - private HashMap axes; - private HashMap datasets; - private ArrayList datasetAxis; - - public AltosGraphTime(String title) { - this.filename = title.toLowerCase().replaceAll("[^a-z0-9]","_")+".png"; - this.title = title; - this.elements = new ArrayList(); - this.axes = new HashMap(); - this.datasets = new HashMap(); - this.datasetAxis = new ArrayList(); - } - - public AltosGraphTime addElement(Element e) { - e.attachGraph(this); - elements.add(e); - return this; - } - - public void setAxis(Element ds, String axisName, Color color) { - Integer axisNum = axes.get(axisName); - int dsNum = datasetAxis.size(); - if (axisNum == null) { - axisNum = newAxis(axisName, color); - } - datasets.put(ds, dsNum); - datasetAxis.add(axisNum); - } - - public int getAxisNum(Element ds) { - return datasetAxis.get( datasets.get(ds) ).intValue(); - } - public int getDataNum(Element ds) { - return datasets.get(ds).intValue(); - } - - private Integer newAxis(String name, Color color) { - int cnt = axes.size(); - AxisLocation locn = AxisLocation.BOTTOM_OR_LEFT; - if (cnt > 0) { - locn = AxisLocation.TOP_OR_RIGHT; - } - Integer res = new Integer(cnt); - axes.put(name, res); - this.addElement(new TimeAxis(cnt, name, color, locn)); - return res; - } - - public void addData(AltosDataPoint d) { - double time = d.time(); - for (Element e : elements) { - e.gotTimeData(time, d); - } - if (callsign == null) callsign = d.callsign(); - if (serial == null) serial = new Integer(d.serial()); - if (flight == null) flight = new Integer(d.flight()); - } - - public JFreeChart createChart() { - NumberAxis xAxis = new NumberAxis("Time (s)"); - xAxis.setAutoRangeIncludesZero(false); - XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false); - XYPlot plot = new XYPlot(); - plot.setDomainAxis(xAxis); - plot.setRenderer(renderer); - plot.setOrientation(PlotOrientation.VERTICAL); - - if (serial != null && flight != null) { - title = serial + "/" + flight + ": " + title; - } - if (callsign != null) { - title = callsign + " - " + title; - } - - renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); - JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, - plot, true); - ChartUtilities.applyCurrentTheme(chart); - - plot.setDomainPannable(true); - plot.setRangePannable(true); - - for (Element e : elements) { - e.addToPlot(this, plot); - } - - return chart; - } - - public void toPNG() throws java.io.IOException { - if (axes.size() > 1) { - toPNG(800, 500); - } else { - toPNG(300, 500); - } - } -} diff --git a/ao-tools/altosui/AltosGraphUI.java b/ao-tools/altosui/AltosGraphUI.java deleted file mode 100644 index cd158651..00000000 --- a/ao-tools/altosui/AltosGraphUI.java +++ /dev/null @@ -1,274 +0,0 @@ - -// Copyright (c) 2010 Anthony Towns -// GPL v2 or later - -package altosui; - -import java.io.*; -import java.util.ArrayList; - -import javax.swing.JFrame; -import java.awt.Color; - -import org.jfree.chart.ChartPanel; -import org.jfree.chart.ChartUtilities; -import org.jfree.chart.JFreeChart; -import org.jfree.chart.axis.AxisLocation; -import org.jfree.ui.ApplicationFrame; -import org.jfree.ui.RefineryUtilities; - -public class AltosGraphUI extends JFrame -{ - static final private Color red = new Color(194,31,31); - static final private Color green = new Color(31,194,31); - static final private Color blue = new Color(31,31,194); - static final private Color black = new Color(31,31,31); - - static private class OverallGraphs { - AltosGraphTime.Element height = - new AltosGraphTime.TimeSeries("Height (m)", "Height (AGL)", red) { - public void gotTimeData(double time, AltosDataPoint d) { - series.add(time, d.height()); - } - }; - - AltosGraphTime.Element speed = - new AltosGraphTime.TimeSeries("Speed (m/s)", "Vertical Speed", green) { - public void gotTimeData(double time, AltosDataPoint d) { - if (d.state() < Altos.ao_flight_drogue) { - series.add(time, d.accel_speed()); - } else { - series.add(time, d.baro_speed()); - } - } - }; - - AltosGraphTime.Element acceleration = - new AltosGraphTime.TimeSeries("Acceleration (m/s\u00B2)", - "Axial Acceleration", blue) - { - public void gotTimeData(double time, AltosDataPoint d) { - series.add(time, d.acceleration()); - } - }; - - AltosGraphTime.Element temperature = - new AltosGraphTime.TimeSeries("Temperature (\u00B0C)", - "Board temperature", red) - { - public void gotTimeData(double time, AltosDataPoint d) { - series.add(time, d.temperature()); - } - }; - - AltosGraphTime.Element drogue_voltage = - new AltosGraphTime.TimeSeries("Voltage (V)", "Drogue Continuity", blue) - { - public void gotTimeData(double time, AltosDataPoint d) { - series.add(time, d.drogue_voltage()); - } - }; - - AltosGraphTime.Element main_voltage = - new AltosGraphTime.TimeSeries("Voltage (V)", "Main Continuity", green) - { - public void gotTimeData(double time, AltosDataPoint d) { - series.add(time, d.main_voltage()); - } - }; - - AltosGraphTime.Element e_pad = new AltosGraphTime.StateMarker(Altos.ao_flight_pad, "Pad"); - AltosGraphTime.Element e_boost = new AltosGraphTime.StateMarker(Altos.ao_flight_boost, "Boost"); - AltosGraphTime.Element e_fast = new AltosGraphTime.StateMarker(Altos.ao_flight_fast, "Fast"); - AltosGraphTime.Element e_coast = new AltosGraphTime.StateMarker(Altos.ao_flight_coast, "Coast"); - AltosGraphTime.Element e_drogue = new AltosGraphTime.StateMarker(Altos.ao_flight_drogue, "Drogue"); - AltosGraphTime.Element e_main = new AltosGraphTime.StateMarker(Altos.ao_flight_main, "Main"); - AltosGraphTime.Element e_landed = new AltosGraphTime.StateMarker(Altos.ao_flight_landed, "Landed"); - - protected AltosGraphTime myAltosGraphTime(String suffix) { - return (new AltosGraphTime("Overall " + suffix)) - .addElement(e_boost) - .addElement(e_drogue) - .addElement(e_main) - .addElement(e_landed); - } - - public ArrayList graphs() { - ArrayList graphs = new ArrayList(); - - graphs.add( myAltosGraphTime("Summary") - .addElement(height) - .addElement(speed) - .addElement(acceleration) ); - - graphs.add( myAltosGraphTime("Altitude") - .addElement(height) ); - - graphs.add( myAltosGraphTime("Speed") - .addElement(speed) ); - - graphs.add( myAltosGraphTime("Acceleration") - .addElement(acceleration) ); - - graphs.add( myAltosGraphTime("Temperature") - .addElement(temperature) ); - - graphs.add( myAltosGraphTime("Continuity") - .addElement(drogue_voltage) - .addElement(main_voltage) ); - - return graphs; - } - } - - static private class AscentGraphs extends OverallGraphs { - protected AltosGraphTime myAltosGraphTime(String suffix) { - return (new AltosGraphTime("Ascent " + suffix) { - public void addData(AltosDataPoint d) { - int state = d.state(); - if (Altos.ao_flight_boost <= state && state <= Altos.ao_flight_coast) { - super.addData(d); - } - } - }).addElement(e_boost) - .addElement(e_fast) - .addElement(e_coast); - } - } - - static private class DescentGraphs extends OverallGraphs { - protected AltosGraphTime myAltosGraphTime(String suffix) { - return (new AltosGraphTime("Descent " + suffix) { - public void addData(AltosDataPoint d) { - int state = d.state(); - if (Altos.ao_flight_drogue <= state && state <= Altos.ao_flight_main) { - super.addData(d); - } - } - }).addElement(e_drogue) - .addElement(e_main); - // ((XYGraph)graph[8]).ymin = new Double(-50); - } - } - - public AltosGraphUI(AltosRecordIterable records) { - super("Altos Graph"); - - Iterable reader = new AltosDataPointReader (records); - if (reader == null) - return; - - init(reader, 0); - } - - public AltosGraphUI(Iterable data, int which) - { - super("Altos Graph"); - init(data, which); - } - - private void init(Iterable data, int which) { - AltosGraph graph = createGraph(data, which); - - JFreeChart chart = graph.createChart(); - ChartPanel chartPanel = new ChartPanel(chart); - chartPanel.setMouseWheelEnabled(true); - chartPanel.setPreferredSize(new java.awt.Dimension(800, 500)); - setContentPane(chartPanel); - - pack(); - - RefineryUtilities.centerFrameOnScreen(this); - - setDefaultCloseOperation(DISPOSE_ON_CLOSE); - setVisible(true); - } - - private static AltosGraph createGraph(Iterable data, - int which) - { - return createGraphsWhich(data, which).get(0); - } - - private static ArrayList createGraphs( - Iterable data) - { - return createGraphsWhich(data, -1); - } - - private static ArrayList createGraphsWhich( - Iterable data, int which) - { - ArrayList graph = new ArrayList(); - graph.addAll((new OverallGraphs()).graphs()); - graph.addAll((new AscentGraphs()).graphs()); - graph.addAll((new DescentGraphs()).graphs()); - - if (which > 0) { - if (which >= graph.size()) { - which = 0; - } - AltosGraph g = graph.get(which); - graph = new ArrayList(); - graph.add(g); - } - - for (AltosDataPoint dp : data) { - for (AltosGraph g : graph) { - g.addData(dp); - } - } - - return graph; - } -} - -/* gnuplot bits... - * -300x400 - --------------------------------------------------------- -TOO HARD! :) - -"ascent-gps-accuracy.png" "Vertical error margin to apogee - GPS v Baro (m)" - 5:($7 < 6 ? $24-$11 : 1/0) -"descent-gps-accuracy.png" "Vertical error margin during descent - GPS v Baro (m)" - 5:($7 < 6 ? 1/0 : $24-$11) - -set output "overall-gps-accuracy.png" -set ylabel "distance above sea level (m)" -plot "telemetry.csv" using 5:11 with lines ti "baro altitude" axis x1y1, \ - "telemetry.csv" using 5:24 with lines ti "gps altitude" axis x1y1 - -set term png tiny size 700,700 enhanced -set xlabel "m" -set ylabel "m" -set polar -set grid polar -set rrange[*:*] -set angles degrees - -set output "overall-gps-path.png" -#:30 with yerrorlines -plot "telemetry.csv" using (90-$33):($7 == 2 ? $31 : 1/0) with lines ti "pad", \ - "telemetry.csv" using (90-$33):($7 == 3 ? $31 : 1/0) with lines ti "boost", \ - "telemetry.csv" using (90-$33):($7 == 4 ? $31 : 1/0) with lines ti "fast", \ - "telemetry.csv" using (90-$33):($7 == 5 ? $31 : 1/0) with lines ti "coast", \ - "telemetry.csv" using (90-$33):($7 == 6 ? $31 : 1/0) with lines ti "drogue", \ - "telemetry.csv" using (90-$33):($7 == 7 ? $31 : 1/0) with lines ti "main", \ - "telemetry.csv" using (90-$33):($7 == 8 ? $31 : 1/0) with lines ti "landed" - -set output "ascent-gps-path.png" -plot "telemetry.csv" using (90-$33):($7 == 2 ? $31 : 1/0):30 with lines ti "pad", \ - "telemetry.csv" using (90-$33):($7 == 3 ? $31 : 1/0):20 with lines ti "boost", \ - "telemetry.csv" using (90-$33):($7 == 4 ? $31 : 1/0):10 with lines ti "fast", \ - "telemetry.csv" using (90-$33):($7 == 5 ? $31 : 1/0):5 with lines ti "coast" - -set output "descent-gps-path.png" -plot "telemetry.csv" using (90-$33):($7 == 6 ? $31 : 1/0) with lines ti "drogue", \ - "telemetry.csv" using (90-$33):($7 == 7 ? $31 : 1/0) with lines ti "main", \ - "telemetry.csv" using (90-$33):($7 == 8 ? $31 : 1/0) with lines ti "landed" - - */ - - diff --git a/ao-tools/altosui/AltosGreatCircle.java b/ao-tools/altosui/AltosGreatCircle.java deleted file mode 100644 index fb1b6ab3..00000000 --- a/ao-tools/altosui/AltosGreatCircle.java +++ /dev/null @@ -1,100 +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.lang.Math; - -public class AltosGreatCircle { - double distance; - double bearing; - - double sqr(double a) { return a * a; } - - static final double rad = Math.PI / 180; - static final double earth_radius = 6371.2 * 1000; /* in meters */ - - static int BEARING_LONG = 0; - static int BEARING_SHORT = 1; - static int BEARING_VOICE = 2; - String bearing_words(int length) { - String [][] bearing_string = { - { - "North", "North North East", "North East", "East North East", - "East", "East South East", "South East", "South South East", - "South", "South South West", "South West", "West South West", - "West", "West North West", "North West", "North North West" - }, { - "N", "NNE", "NE", "ENE", - "E", "ESE", "SE", "SSE", - "S", "SSW", "SW", "WSW", - "W", "WNW", "NW", "NNW" - }, { - "north", "nor nor east", "north east", "east nor east", - "east", "east sow east", "south east", "sow sow east", - "south", "sow sow west", "south west", "west sow west", - "west", "west nor west", "north west", "nor nor west " - } - }; - return bearing_string[length][(int)((bearing / 90 * 8 + 1) / 2)%16]; - } - - public AltosGreatCircle (double start_lat, double start_lon, - double end_lat, double end_lon) - { - double lat1 = rad * start_lat; - double lon1 = rad * -start_lon; - double lat2 = rad * end_lat; - double lon2 = rad * -end_lon; - - double d_lon = lon2 - lon1; - - /* From http://en.wikipedia.org/wiki/Great-circle_distance */ - double vdn = Math.sqrt(sqr(Math.cos(lat2) * Math.sin(d_lon)) + - sqr(Math.cos(lat1) * Math.sin(lat2) - - Math.sin(lat1) * Math.cos(lat2) * Math.cos(d_lon))); - double vdd = Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(d_lon); - double d = Math.atan2(vdn,vdd); - double course; - - if (Math.cos(lat1) < 1e-20) { - if (lat1 > 0) - course = Math.PI; - else - course = -Math.PI; - } else { - if (d < 1e-10) - course = 0; - else - course = Math.acos((Math.sin(lat2)-Math.sin(lat1)*Math.cos(d)) / - (Math.sin(d)*Math.cos(lat1))); - if (Math.sin(lon2-lon1) > 0) - course = 2 * Math.PI-course; - } - distance = d * earth_radius; - bearing = course * 180/Math.PI; - } - - public AltosGreatCircle(AltosGPS start, AltosGPS end) { - this(start.lat, start.lon, end.lat, end.lon); - } - - public AltosGreatCircle() { - distance = 0; - bearing = 0; - } -} diff --git a/ao-tools/altosui/AltosHexfile.java b/ao-tools/altosui/AltosHexfile.java deleted file mode 100644 index 19e35ae1..00000000 --- a/ao-tools/altosui/AltosHexfile.java +++ /dev/null @@ -1,252 +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.lang.*; -import java.io.*; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.LinkedList; -import java.util.Iterator; -import java.util.Arrays; - -class HexFileInputStream extends PushbackInputStream { - public int line; - - public HexFileInputStream(FileInputStream o) { - super(new BufferedInputStream(o)); - line = 1; - } - - public int read() throws IOException { - int c = super.read(); - if (c == '\n') - line++; - return c; - } - - public void unread(int c) throws IOException { - if (c == '\n') - line--; - if (c != -1) - super.unread(c); - } -} - -class HexRecord implements Comparable { - public int address; - public int type; - public byte checksum; - public byte[] data; - - static final int NORMAL = 0; - static final int EOF = 1; - static final int EXTENDED_ADDRESS = 2; - - enum read_state { - marker, - length, - address, - type, - data, - checksum, - newline, - white, - done, - } - - boolean ishex(int c) { - if ('0' <= c && c <= '9') - return true; - if ('a' <= c && c <= 'f') - return true; - if ('A' <= c && c <= 'F') - return true; - return false; - } - - boolean isspace(int c) { - switch (c) { - case ' ': - case '\t': - return true; - } - return false; - } - - int fromhex(int c) { - if ('0' <= c && c <= '9') - return c - '0'; - if ('a' <= c && c <= 'f') - return c - 'a' + 10; - if ('A' <= c && c <= 'F') - return c - 'A' + 10; - return -1; - } - - public byte checksum() { - byte got = 0; - - got += data.length; - got += (address >> 8) & 0xff; - got += (address ) & 0xff; - got += type; - for (int i = 0; i < data.length; i++) - got += data[i]; - return (byte) (-got); - } - - public int compareTo(Object other) { - HexRecord o = (HexRecord) other; - return address - o.address; - } - - public String toString() { - return String.format("%04x: %02x (%d)", address, type, data.length); - } - - public HexRecord(HexFileInputStream input) throws IOException { - read_state state = read_state.marker; - int nhexbytes = 0; - int hex = 0; - int ndata = 0; - byte got_checksum; - - while (state != read_state.done) { - int c = input.read(); - if (c < 0 && state != read_state.white) - throw new IOException(String.format("%d: Unexpected EOF", input.line)); - if (c == ' ') - continue; - switch (state) { - case marker: - if (c != ':') - throw new IOException("Missing ':'"); - state = read_state.length; - nhexbytes = 2; - hex = 0; - break; - case length: - case address: - case type: - case data: - case checksum: - if(!ishex(c)) - throw new IOException(String.format("Non-hex char '%c'", c)); - hex = hex << 4 | fromhex(c); - --nhexbytes; - if (nhexbytes != 0) - break; - - switch (state) { - case length: - data = new byte[hex]; - state = read_state.address; - nhexbytes = 4; - break; - case address: - address = hex; - state = read_state.type; - nhexbytes = 2; - break; - case type: - type = hex; - if (data.length > 0) - state = read_state.data; - else - state = read_state.checksum; - nhexbytes = 2; - ndata = 0; - break; - case data: - data[ndata] = (byte) hex; - ndata++; - nhexbytes = 2; - if (ndata == data.length) - state = read_state.checksum; - break; - case checksum: - checksum = (byte) hex; - state = read_state.newline; - break; - default: - break; - } - hex = 0; - break; - case newline: - if (c != '\n' && c != '\r') - throw new IOException("Missing newline"); - state = read_state.white; - break; - case white: - if (!isspace(c)) { - input.unread(c); - state = read_state.done; - } - break; - case done: - break; - } - } - got_checksum = checksum(); - if (got_checksum != checksum) - throw new IOException(String.format("Invalid checksum (read 0x%02x computed 0x%02x)\n", - checksum, got_checksum)); - } -} - -public class AltosHexfile { - public int address; - public byte[] data; - - public byte get_byte(int a) { - return data[a - address]; - } - - public AltosHexfile(FileInputStream file) throws IOException { - HexFileInputStream input = new HexFileInputStream(file); - LinkedList record_list = new LinkedList(); - boolean done = false; - - while (!done) { - HexRecord record = new HexRecord(input); - - if (record.type == HexRecord.EOF) - done = true; - else - record_list.add(record); - } - HexRecord[] records = record_list.toArray(new HexRecord[0]); - Arrays.sort(records); - if (records.length > 0) { - int base = records[0].address; - int bound = records[records.length-1].address + - records[records.length-1].data.length; - - data = new byte[bound - base]; - address = base; - Arrays.fill(data, (byte) 0xff); - - /* Paint the records into the new array */ - for (int i = 0; i < records.length; i++) { - for (int j = 0; j < records[i].data.length; j++) - data[records[i].address - base + j] = records[i].data[j]; - } - } - } -} \ No newline at end of file diff --git a/ao-tools/altosui/AltosIgnite.java b/ao-tools/altosui/AltosIgnite.java deleted file mode 100644 index 3cbd8a75..00000000 --- a/ao-tools/altosui/AltosIgnite.java +++ /dev/null @@ -1,173 +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.io.*; -import java.util.concurrent.*; - -public class AltosIgnite { - AltosDevice device; - AltosSerial serial; - boolean remote; - boolean serial_started; - 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 { - serial_started = true; - if (remote) { - serial.set_radio(); - serial.printf("p\nE 0\n"); - serial.flush_input(); - } - } - - private void stop_serial() throws InterruptedException { - if (!serial_started) - return; - serial_started = false; - 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) throws InterruptedException, TimeoutException { - int status = Unknown; - if (serial == null) - return status; - string_ref status_name = new string_ref(); - start_serial(); - serial.printf("t\n"); - for (;;) { - String line = serial.get_reply(5000); - 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; - 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 void close() { - try { - stop_serial(); - } catch (InterruptedException ie) { - } - serial.close(); - serial = null; - } - - public AltosIgnite(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException { - - device = in_device; - 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 deleted file mode 100644 index d542729c..00000000 --- a/ao-tools/altosui/AltosIgniteUI.java +++ /dev/null @@ -1,317 +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 javax.swing.event.*; -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()) - arm.setText(String.format("%d", time_remaining)); - else - arm.setText("Arm"); - } - - void start_timer() { - time_remaining = 10; - set_arm_text(); - timer_running = true; - } - - void stop_timer() { - time_remaining = 0; - arm.setSelected(false); - arm.setEnabled(false); - fire.setEnabled(false); - timer_running = false; - set_arm_text(); - } - - void cancel () { - apogee.setSelected(false); - main.setSelected(false); - fire.setEnabled(false); - 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.toShortString()), - "Connection Failed", - JOptionPane.ERROR_MESSAGE); - } - - void tick_timer() { - 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() { - 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; - ignite.fire(igniter); - cancel(); - } - } - - public void actionPerformed(ActionEvent e) { - String cmd = e.getActionCommand(); - if (cmd.equals("apogee") || cmd.equals("main")) { - stop_timer(); - } - - if (cmd.equals("apogee") && apogee.isSelected()) { - main.setSelected(false); - arm.setEnabled(true); - } - if (cmd.equals("main") && main.isSelected()) { - apogee.setSelected(false); - arm.setEnabled(true); - } - - 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(); - 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.toShortString()), - "Cannot open target device", - JOptionPane.ERROR_MESSAGE); - } catch (AltosSerialInUseException si) { - JOptionPane.showMessageDialog(owner, - String.format("Device \"%s\" already in use", - device.toShortString()), - "Device in use", - JOptionPane.ERROR_MESSAGE); - } catch (IOException ee) { - JOptionPane.showMessageDialog(owner, - device.toShortString(), - 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; - - 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; - 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); - apogee.setActionCommand("apogee"); - - 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 = 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); - arm.setActionCommand("arm"); - arm.setEnabled(false); - - c.gridx = 1; - c.gridy = 3; - c.gridwidth = 1; - c.anchor = GridBagConstraints.CENTER; - fire = new JButton ("Fire"); - fire.setEnabled(false); - pane.add(fire, c); - fire.addActionListener(this); - fire.setActionCommand("fire"); - - pack(); - setLocationRelativeTo(owner); - setVisible(true); - - addWindowListener(new ConfigListener(this)); - } -} \ No newline at end of file diff --git a/ao-tools/altosui/AltosInfoTable.java b/ao-tools/altosui/AltosInfoTable.java deleted file mode 100644 index 723f8301..00000000 --- a/ao-tools/altosui/AltosInfoTable.java +++ /dev/null @@ -1,190 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosInfoTable extends JTable { - private AltosFlightInfoTableModel model; - - private Font infoLabelFont = new Font("SansSerif", Font.PLAIN, 14); - private Font infoValueFont = new Font("Monospaced", Font.PLAIN, 14); - - static final int info_columns = 3; - static final int info_rows = 17; - - int desired_row_height() { - FontMetrics infoValueMetrics = getFontMetrics(infoValueFont); - return (infoValueMetrics.getHeight() + infoValueMetrics.getLeading()) * 18 / 10; - } - - public AltosInfoTable() { - super(new AltosFlightInfoTableModel(info_rows, info_columns)); - model = (AltosFlightInfoTableModel) getModel(); - setFont(infoValueFont); - setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS); - setShowGrid(true); - setRowHeight(desired_row_height()); - doLayout(); - } - - public Dimension getPreferredScrollableViewportSize() { - return getPreferredSize(); - } - - void info_reset() { - model.reset(); - } - - void info_add_row(int col, String name, String value) { - model.addRow(col, name, value); - } - - void info_add_row(int col, String name, String format, Object... parameters) { - info_add_row (col, name, String.format(format, parameters)); - } - - void info_add_deg(int col, String name, double v, int pos, int neg) { - int c = pos; - if (v < 0) { - c = neg; - v = -v; - } - double deg = Math.floor(v); - double min = (v - deg) * 60; - - info_add_row(col, name, String.format("%3.0f°%08.5f'", deg, min)); - } - - void info_finish() { - model.finish(); - } - - public void clear() { - model.clear(); - } - - public void show(AltosState state, int crc_errors) { - if (state == null) - return; - info_reset(); - info_add_row(0, "Rocket state", "%s", state.data.state()); - info_add_row(0, "Callsign", "%s", state.data.callsign); - info_add_row(0, "Rocket serial", "%6d", state.data.serial); - info_add_row(0, "Rocket flight", "%6d", state.data.flight); - - info_add_row(0, "RSSI", "%6d dBm", state.data.rssi); - info_add_row(0, "CRC Errors", "%6d", crc_errors); - info_add_row(0, "Height", "%6.0f m", state.height); - info_add_row(0, "Max height", "%6.0f m", state.max_height); - info_add_row(0, "Acceleration", "%8.1f m/s²", state.acceleration); - info_add_row(0, "Max acceleration", "%8.1f m/s²", state.max_acceleration); - info_add_row(0, "Speed", "%8.1f m/s", state.ascent ? state.speed : state.baro_speed); - info_add_row(0, "Max Speed", "%8.1f m/s", state.max_speed); - info_add_row(0, "Temperature", "%9.2f °C", state.temperature); - info_add_row(0, "Battery", "%9.2f V", state.battery); - info_add_row(0, "Drogue", "%9.2f V", state.drogue_sense); - info_add_row(0, "Main", "%9.2f V", state.main_sense); - info_add_row(0, "Pad altitude", "%6.0f m", state.ground_altitude); - if (state.gps == null) { - info_add_row(1, "GPS", "not available"); - } else { - if (state.gps_ready) - info_add_row(1, "GPS state", "%s", "ready"); - else - info_add_row(1, "GPS state", "wait (%d)", - state.gps_waiting); - if (state.data.gps.locked) - info_add_row(1, "GPS", " locked"); - else if (state.data.gps.connected) - info_add_row(1, "GPS", " unlocked"); - else - info_add_row(1, "GPS", " missing"); - info_add_row(1, "Satellites", "%6d", state.data.gps.nsat); - info_add_deg(1, "Latitude", state.gps.lat, 'N', 'S'); - info_add_deg(1, "Longitude", state.gps.lon, 'E', 'W'); - info_add_row(1, "GPS altitude", "%6d", state.gps.alt); - info_add_row(1, "GPS height", "%6.0f", state.gps_height); - - /* The SkyTraq GPS doesn't report these values */ - if (false) { - info_add_row(1, "GPS ground speed", "%8.1f m/s %3d°", - state.gps.ground_speed, - state.gps.course); - info_add_row(1, "GPS climb rate", "%8.1f m/s", - state.gps.climb_rate); - info_add_row(1, "GPS error", "%6d m(h)%3d m(v)", - state.gps.h_error, state.gps.v_error); - } - info_add_row(1, "GPS hdop", "%8.1f", state.gps.hdop); - - if (state.npad > 0) { - if (state.from_pad != null) { - info_add_row(1, "Distance from pad", "%6d m", - (int) (state.from_pad.distance + 0.5)); - info_add_row(1, "Direction from pad", "%6d°", - (int) (state.from_pad.bearing + 0.5)); - info_add_row(1, "Elevation from pad", "%6d°", - (int) (state.elevation + 0.5)); - info_add_row(1, "Range from pad", "%6d m", - (int) (state.range + 0.5)); - } else { - info_add_row(1, "Distance from pad", "unknown"); - info_add_row(1, "Direction from pad", "unknown"); - info_add_row(1, "Elevation from pad", "unknown"); - info_add_row(1, "Range from pad", "unknown"); - } - info_add_deg(1, "Pad latitude", state.pad_lat, 'N', 'S'); - info_add_deg(1, "Pad longitude", state.pad_lon, 'E', 'W'); - info_add_row(1, "Pad GPS alt", "%6.0f m", state.pad_alt); - } - info_add_row(1, "GPS date", "%04d-%02d-%02d", - state.gps.year, - state.gps.month, - state.gps.day); - info_add_row(1, "GPS time", " %02d:%02d:%02d", - state.gps.hour, - state.gps.minute, - state.gps.second); - int nsat_vis = 0; - int c; - - if (state.gps.cc_gps_sat == null) - info_add_row(2, "Satellites Visible", "%4d", 0); - else { - info_add_row(2, "Satellites Visible", "%4d", state.gps.cc_gps_sat.length); - for (c = 0; c < state.gps.cc_gps_sat.length; c++) { - info_add_row(2, "Satellite id,C/N0", - "%4d, %4d", - state.gps.cc_gps_sat[c].svid, - state.gps.cc_gps_sat[c].c_n0); - } - } - } - info_finish(); - } -} diff --git a/ao-tools/altosui/AltosKML.java b/ao-tools/altosui/AltosKML.java deleted file mode 100644 index d586033f..00000000 --- a/ao-tools/altosui/AltosKML.java +++ /dev/null @@ -1,169 +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.lang.*; -import java.io.*; -import java.text.*; -import java.util.*; - -public class AltosKML implements AltosWriter { - - File name; - PrintStream out; - int state = -1; - AltosRecord prev = null; - - static final String[] kml_state_colors = { - "FF000000", - "FF000000", - "FF000000", - "FF0000FF", - "FF4080FF", - "FF00FFFF", - "FFFF0000", - "FF00FF00", - "FF000000", - "FFFFFFFF" - }; - - static final String kml_header_start = - "\n" + - "\n" + - "\n" + - " AO Flight#%d S/N: %03d\n" + - " \n"; - static final String kml_header_end = - " \n" + - " 0\n"; - - static final String kml_style_start = - " \n"; - - static final String kml_placemark_start = - " \n" + - " %s\n" + - " #ao-flightstate-%s\n" + - " \n" + - " 1\n" + - " absolute\n" + - " \n"; - - static final String kml_coord_fmt = - " %12.7f, %12.7f, %12.7f \n"; - - static final String kml_placemark_end = - " \n" + - " \n" + - " \n"; - - static final String kml_footer = - "\n" + - "\n"; - - void start (AltosRecord record) { - out.printf(kml_header_start, record.flight, record.serial); - out.printf("Date: %04d-%02d-%02d\n", - record.gps.year, record.gps.month, record.gps.day); - out.printf("Time: %2d:%02d:%02d\n", - record.gps.hour, record.gps.minute, record.gps.second); - out.printf("%s", kml_header_end); - } - - boolean started = false; - - void state_start(AltosRecord record) { - String state_name = Altos.state_name(record.state); - out.printf(kml_style_start, state_name, kml_state_colors[record.state]); - out.printf("\tState: %s\n", state_name); - out.printf("%s", kml_style_end); - out.printf(kml_placemark_start, state_name, state_name); - } - - void state_end(AltosRecord record) { - out.printf("%s", kml_placemark_end); - } - - void coord(AltosRecord record) { - AltosGPS gps = record.gps; - out.printf(kml_coord_fmt, - gps.lon, gps.lat, - record.filtered_altitude(), (double) gps.alt, - record.time, gps.nsat); - } - - void end() { - out.printf("%s", kml_footer); - } - - public void close() { - if (prev != null) { - state_end(prev); - end(); - prev = null; - } - } - - public void write(AltosRecord record) { - AltosGPS gps = record.gps; - - if (gps == null) - return; - if (!started) { - start(record); - started = true; - } - if (prev != null && - prev.gps.second == record.gps.second && - prev.gps.minute == record.gps.minute && - prev.gps.hour == record.gps.hour) - return; - if (record.state != state) { - state = record.state; - if (prev != null) { - coord(record); - state_end(prev); - } - state_start(record); - } - coord(record); - prev = record; - } - - public void write(AltosRecordIterable iterable) { - for (AltosRecord record : iterable) - write(record); - } - - public AltosKML(File in_name) throws FileNotFoundException { - name = in_name; - out = new PrintStream(name); - } - - public AltosKML(String in_string) throws FileNotFoundException { - this(new File(in_string)); - } -} diff --git a/ao-tools/altosui/AltosLanded.java b/ao-tools/altosui/AltosLanded.java deleted file mode 100644 index d34efe6d..00000000 --- a/ao-tools/altosui/AltosLanded.java +++ /dev/null @@ -1,212 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosLanded extends JComponent implements AltosFlightDisplay { - GridBagLayout layout; - Font label_font; - Font value_font; - - public class LandedValue { - JLabel label; - JTextField value; - void show(AltosState state, int crc_errors) {} - - void reset() { - value.setText(""); - } - - void show(String format, double v) { - value.setText(String.format(format, v)); - } - - public LandedValue (GridBagLayout layout, int y, String text) { - GridBagConstraints c = new GridBagConstraints(); - c.weighty = 1; - - label = new JLabel(text); - label.setFont(label_font); - label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = 0; c.gridy = y; - c.insets = new Insets(10, 10, 10, 10); - c.anchor = GridBagConstraints.WEST; - c.weightx = 0; - c.fill = GridBagConstraints.VERTICAL; - layout.setConstraints(label, c); - add(label); - - value = new JTextField(Altos.text_width); - value.setFont(value_font); - value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 1; c.gridy = y; - c.anchor = GridBagConstraints.WEST; - c.weightx = 1; - c.fill = GridBagConstraints.BOTH; - layout.setConstraints(value, c); - add(value); - } - } - - String pos(double p, String pos, String neg) { - String h = pos; - if (p < 0) { - h = neg; - p = -p; - } - int deg = (int) Math.floor(p); - double min = (p - Math.floor(p)) * 60.0; - return String.format("%s %4d° %9.6f", h, deg, min); - } - - class Lat extends LandedValue { - void show (AltosState state, int crc_errors) { - if (state.gps != null) - value.setText(pos(state.gps.lat,"N", "S")); - else - value.setText("???"); - } - public Lat (GridBagLayout layout, int y) { - super (layout, y, "Latitude"); - } - } - - Lat lat; - - class Lon extends LandedValue { - void show (AltosState state, int crc_errors) { - if (state.gps != null) - value.setText(pos(state.gps.lon,"E", "W")); - else - value.setText("???"); - } - public Lon (GridBagLayout layout, int y) { - super (layout, y, "Longitude"); - } - } - - Lon lon; - - class Bearing extends LandedValue { - void show (AltosState state, int crc_errors) { - if (state.from_pad != null) - show("%3.0f°", state.from_pad.bearing); - else - value.setText("???"); - } - public Bearing (GridBagLayout layout, int y) { - super (layout, y, "Bearing"); - } - } - - Bearing bearing; - - class Distance extends LandedValue { - void show (AltosState state, int crc_errors) { - if (state.from_pad != null) - show("%6.0f m", state.from_pad.distance); - else - value.setText("???"); - } - public Distance (GridBagLayout layout, int y) { - super (layout, y, "Distance"); - } - } - - Distance distance; - - class Height extends LandedValue { - void show (AltosState state, int crc_errors) { - show("%6.0f m", state.max_height); - } - public Height (GridBagLayout layout, int y) { - super (layout, y, "Maximum Height"); - } - } - - Height height; - - class Speed extends LandedValue { - void show (AltosState state, int crc_errors) { - show("%6.0f m/s", state.max_speed); - } - public Speed (GridBagLayout layout, int y) { - super (layout, y, "Maximum Speed"); - } - } - - Speed speed; - - class Accel extends LandedValue { - void show (AltosState state, int crc_errors) { - show("%6.0f m/s²", state.max_acceleration); - } - public Accel (GridBagLayout layout, int y) { - super (layout, y, "Maximum Acceleration"); - } - } - - Accel accel; - - public void reset() { - lat.reset(); - lon.reset(); - bearing.reset(); - distance.reset(); - height.reset(); - speed.reset(); - accel.reset(); - } - - public void show(AltosState state, int crc_errors) { - bearing.show(state, crc_errors); - distance.show(state, crc_errors); - lat.show(state, crc_errors); - lon.show(state, crc_errors); - height.show(state, crc_errors); - speed.show(state, crc_errors); - accel.show(state, crc_errors); - } - - public AltosLanded() { - layout = new GridBagLayout(); - - label_font = new Font("Dialog", Font.PLAIN, 22); - value_font = new Font("Monospaced", Font.PLAIN, 22); - setLayout(layout); - - /* Elements in descent display */ - bearing = new Bearing(layout, 0); - distance = new Distance(layout, 1); - lat = new Lat(layout, 2); - lon = new Lon(layout, 3); - height = new Height(layout, 4); - speed = new Speed(layout, 5); - accel = new Accel(layout, 6); - } -} diff --git a/ao-tools/altosui/AltosLed.java b/ao-tools/altosui/AltosLed.java deleted file mode 100644 index e08e9960..00000000 --- a/ao-tools/altosui/AltosLed.java +++ /dev/null @@ -1,54 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosLed extends JLabel { - ImageIcon on, off; - - ImageIcon create_icon(String path) { - java.net.URL imgURL = AltosUI.class.getResource(path); - if (imgURL != null) - return new ImageIcon(imgURL); - System.err.printf("Cannot find icon \"%s\"\n", path); - return null; - } - - public void set(boolean set) { - if (set) - setIcon(on); - else - setIcon(off); - } - - public AltosLed(String on_path, String off_path) { - on = create_icon(on_path); - off = create_icon(off_path); - setIcon(off); - } -} diff --git a/ao-tools/altosui/AltosLights.java b/ao-tools/altosui/AltosLights.java deleted file mode 100644 index 2fa38412..00000000 --- a/ao-tools/altosui/AltosLights.java +++ /dev/null @@ -1,73 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosLights extends JComponent { - - GridBagLayout gridbag; - - AltosLed red, green; - - ImageIcon create_icon(String path, String description) { - java.net.URL imgURL = AltosUI.class.getResource(path); - if (imgURL != null) - return new ImageIcon(imgURL, description); - System.err.printf("Cannot find icon \"%s\"\n", path); - return null; - } - - public void set (boolean on) { - if (on) { - red.set(false); - green.set(true); - } else { - red.set(true); - green.set(false); - } - } - - public AltosLights() { - GridBagConstraints c; - gridbag = new GridBagLayout(); - setLayout(gridbag); - - c = new GridBagConstraints(); - red = new AltosLed("/redled.png", "/grayled.png"); - c.gridx = 0; c.gridy = 0; - c.insets = new Insets (0, 5, 0, 5); - gridbag.setConstraints(red, c); - add(red); - red.set(true); - green = new AltosLed("/greenled.png", "/grayled.png"); - c.gridx = 1; c.gridy = 0; - gridbag.setConstraints(green, c); - add(green); - green.set(false); - } -} diff --git a/ao-tools/altosui/AltosLine.java b/ao-tools/altosui/AltosLine.java deleted file mode 100644 index 86e9d4c6..00000000 --- a/ao-tools/altosui/AltosLine.java +++ /dev/null @@ -1,30 +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; - -public class AltosLine { - public String line; - - public AltosLine() { - line = null; - } - - public AltosLine(String s) { - line = s; - } -} \ No newline at end of file diff --git a/ao-tools/altosui/AltosLog.java b/ao-tools/altosui/AltosLog.java deleted file mode 100644 index dd147d21..00000000 --- a/ao-tools/altosui/AltosLog.java +++ /dev/null @@ -1,115 +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.io.*; -import java.lang.*; -import java.util.*; -import java.text.ParseException; -import java.util.concurrent.LinkedBlockingQueue; - -/* - * This creates a thread to capture telemetry data and write it to - * a log file - */ -class AltosLog implements Runnable { - - LinkedBlockingQueue input_queue; - LinkedBlockingQueue pending_queue; - int serial; - int flight; - FileWriter log_file; - Thread log_thread; - - private void close_log_file() { - if (log_file != null) { - try { - log_file.close(); - } catch (IOException io) { - } - log_file = null; - } - } - - void close() { - close_log_file(); - if (log_thread != null) { - log_thread.interrupt(); - log_thread = null; - } - } - - boolean open (AltosTelemetry telem) throws IOException { - AltosFile a = new AltosFile(telem); - - log_file = new FileWriter(a, true); - if (log_file != null) { - while (!pending_queue.isEmpty()) { - try { - String s = pending_queue.take(); - log_file.write(s); - log_file.write('\n'); - } catch (InterruptedException ie) { - } - } - log_file.flush(); - } - return log_file != null; - } - - public void run () { - try { - for (;;) { - AltosLine line = input_queue.take(); - if (line.line == null) - continue; - try { - AltosTelemetry telem = new AltosTelemetry(line.line); - if (telem.serial != serial || telem.flight != flight || log_file == null) { - close_log_file(); - serial = telem.serial; - flight = telem.flight; - open(telem); - } - } catch (ParseException pe) { - } catch (AltosCRCException ce) { - } - if (log_file != null) { - log_file.write(line.line); - log_file.write('\n'); - log_file.flush(); - } else - pending_queue.put(line.line); - } - } catch (InterruptedException ie) { - } catch (IOException ie) { - } - close(); - } - - public AltosLog (AltosSerial s) { - pending_queue = new LinkedBlockingQueue (); - input_queue = new LinkedBlockingQueue (); - s.add_monitor(input_queue); - serial = -1; - flight = -1; - log_file = null; - log_thread = new Thread(this); - log_thread.start(); - } -} diff --git a/ao-tools/altosui/AltosPad.java b/ao-tools/altosui/AltosPad.java deleted file mode 100644 index 66954347..00000000 --- a/ao-tools/altosui/AltosPad.java +++ /dev/null @@ -1,269 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosPad extends JComponent implements AltosFlightDisplay { - GridBagLayout layout; - - public class LaunchStatus { - JLabel label; - JTextField value; - AltosLights lights; - - void show(AltosState state, int crc_errors) {} - void reset() { - value.setText(""); - lights.set(false); - } - - public LaunchStatus (GridBagLayout layout, int y, String text) { - GridBagConstraints c = new GridBagConstraints(); - c.weighty = 1; - - lights = new AltosLights(); - c.gridx = 0; c.gridy = y; - c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.VERTICAL; - c.weightx = 0; - layout.setConstraints(lights, c); - add(lights); - - label = new JLabel(text); - label.setFont(Altos.label_font); - label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = 1; c.gridy = y; - c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.VERTICAL; - c.weightx = 0; - layout.setConstraints(label, c); - add(label); - - value = new JTextField(Altos.text_width); - value.setFont(Altos.value_font); - value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 2; c.gridy = y; - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - layout.setConstraints(value, c); - add(value); - - } - } - - public class LaunchValue { - JLabel label; - JTextField value; - void show(AltosState state, int crc_errors) {} - - void reset() { - value.setText(""); - } - public LaunchValue (GridBagLayout layout, int y, String text) { - GridBagConstraints c = new GridBagConstraints(); - c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); - c.weighty = 1; - - label = new JLabel(text); - label.setFont(Altos.label_font); - label.setHorizontalAlignment(SwingConstants.LEFT); - c.gridx = 1; c.gridy = y; - c.anchor = GridBagConstraints.WEST; - c.fill = GridBagConstraints.VERTICAL; - c.weightx = 0; - layout.setConstraints(label, c); - add(label); - - value = new JTextField(Altos.text_width); - value.setFont(Altos.value_font); - value.setHorizontalAlignment(SwingConstants.RIGHT); - c.gridx = 2; c.gridy = y; - c.anchor = GridBagConstraints.EAST; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - layout.setConstraints(value, c); - add(value); - } - } - - class Battery extends LaunchStatus { - void show (AltosState state, int crc_errors) { - value.setText(String.format("%4.2f V", state.battery)); - lights.set(state.battery > 3.7); - } - public Battery (GridBagLayout layout, int y) { - super(layout, y, "Battery Voltage"); - } - } - - Battery battery; - - class Apogee extends LaunchStatus { - void show (AltosState state, int crc_errors) { - value.setText(String.format("%4.2f V", state.drogue_sense)); - lights.set(state.drogue_sense > 3.2); - } - public Apogee (GridBagLayout layout, int y) { - super(layout, y, "Apogee Igniter Voltage"); - } - } - - Apogee apogee; - - class Main extends LaunchStatus { - void show (AltosState state, int crc_errors) { - value.setText(String.format("%4.2f V", state.main_sense)); - lights.set(state.main_sense > 3.2); - } - public Main (GridBagLayout layout, int y) { - super(layout, y, "Main Igniter Voltage"); - } - } - - Main main; - - class GPSLocked extends LaunchStatus { - void show (AltosState state, int crc_errors) { - value.setText(String.format("%4d sats", state.gps.nsat)); - lights.set(state.gps.locked); - } - public GPSLocked (GridBagLayout layout, int y) { - super (layout, y, "GPS Locked"); - } - } - - GPSLocked gps_locked; - - class GPSReady extends LaunchStatus { - void show (AltosState state, int crc_errors) { - if (state.gps_ready) - value.setText("Ready"); - else - value.setText(String.format("Waiting %d", state.gps_waiting)); - lights.set(state.gps_ready); - } - public GPSReady (GridBagLayout layout, int y) { - super (layout, y, "GPS Ready"); - } - } - - GPSReady gps_ready; - - String pos(double p, String pos, String neg) { - String h = pos; - if (p < 0) { - h = neg; - p = -p; - } - int deg = (int) Math.floor(p); - double min = (p - Math.floor(p)) * 60.0; - return String.format("%s %4d° %9.6f", h, deg, min); - } - - class PadLat extends LaunchValue { - void show (AltosState state, int crc_errors) { - value.setText(pos(state.pad_lat,"N", "S")); - } - public PadLat (GridBagLayout layout, int y) { - super (layout, y, "Pad Latitude"); - } - } - - PadLat pad_lat; - - class PadLon extends LaunchValue { - void show (AltosState state, int crc_errors) { - value.setText(pos(state.pad_lon,"E", "W")); - } - public PadLon (GridBagLayout layout, int y) { - super (layout, y, "Pad Longitude"); - } - } - - PadLon pad_lon; - - class PadAlt extends LaunchValue { - void show (AltosState state, int crc_errors) { - value.setText(String.format("%4.0f m", state.pad_alt)); - } - public PadAlt (GridBagLayout layout, int y) { - super (layout, y, "Pad Altitude"); - } - } - - PadAlt pad_alt; - - public void reset() { - battery.reset(); - apogee.reset(); - main.reset(); - gps_locked.reset(); - gps_ready.reset(); - pad_lat.reset(); - pad_lon.reset(); - pad_alt.reset(); - } - - public void show(AltosState state, int crc_errors) { - battery.show(state, crc_errors); - apogee.show(state, crc_errors); - main.show(state, crc_errors); - gps_locked.show(state, crc_errors); - gps_ready.show(state, crc_errors); - pad_lat.show(state, crc_errors); - pad_lon.show(state, crc_errors); - pad_alt.show(state, crc_errors); - } - - public AltosPad() { - layout = new GridBagLayout(); - - setLayout(layout); - - /* Elements in pad display: - * - * Battery voltage - * Igniter continuity - * GPS lock status - * GPS ready status - * GPS location - * Pad altitude - * RSSI - */ - battery = new Battery(layout, 0); - apogee = new Apogee(layout, 1); - main = new Main(layout, 2); - gps_locked = new GPSLocked(layout, 3); - gps_ready = new GPSReady(layout, 4); - pad_lat = new PadLat(layout, 5); - pad_lon = new PadLon(layout, 6); - pad_alt = new PadAlt(layout, 7); - } -} diff --git a/ao-tools/altosui/AltosParse.java b/ao-tools/altosui/AltosParse.java deleted file mode 100644 index fbfcaaee..00000000 --- a/ao-tools/altosui/AltosParse.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.text.*; -import java.lang.*; - -public class AltosParse { - static boolean isdigit(char c) { - return '0' <= c && c <= '9'; - } - - static int parse_int(String v) throws ParseException { - try { - return Altos.fromdec(v); - } catch (NumberFormatException e) { - throw new ParseException("error parsing int " + v, 0); - } - } - - static int parse_hex(String v) throws ParseException { - try { - return Altos.fromhex(v); - } catch (NumberFormatException e) { - throw new ParseException("error parsing hex " + v, 0); - } - } - - static double parse_double(String v) throws ParseException { - try { - return Double.parseDouble(v); - } catch (NumberFormatException e) { - throw new ParseException("error parsing double " + v, 0); - } - } - - static double parse_coord(String coord) throws ParseException { - String[] dsf = coord.split("\\D+"); - - if (dsf.length != 3) { - throw new ParseException("error parsing coord " + coord, 0); - } - int deg = parse_int(dsf[0]); - int min = parse_int(dsf[1]); - int frac = parse_int(dsf[2]); - - double r = deg + (min + frac / 10000.0) / 60.0; - if (coord.endsWith("S") || coord.endsWith("W")) - r = -r; - return r; - } - - static String strip_suffix(String v, String suffix) { - if (v.endsWith(suffix)) - return v.substring(0, v.length() - suffix.length()); - return v; - } - - static void word(String v, String m) throws ParseException { - if (!v.equals(m)) { - throw new ParseException("error matching '" + v + "' '" + m + "'", 0); - } - } -} diff --git a/ao-tools/altosui/AltosPreferences.java b/ao-tools/altosui/AltosPreferences.java deleted file mode 100644 index e2a3df3b..00000000 --- a/ao-tools/altosui/AltosPreferences.java +++ /dev/null @@ -1,205 +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.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.util.concurrent.LinkedBlockingQueue; -import java.awt.Component; -import javax.swing.*; -import javax.swing.filechooser.FileSystemView; - -class AltosPreferences { - static Preferences preferences; - - /* logdir preference name */ - final static String logdirPreference = "LOGDIR"; - - /* channel preference name */ - final static String channelPreferenceFormat = "CHANNEL-%d"; - - /* voice preference name */ - final static String voicePreference = "VOICE"; - - /* callsign preference name */ - final static String callsignPreference = "CALLSIGN"; - - /* firmware directory preference name */ - final static String firmwaredirPreference = "FIRMWARE"; - - /* Default logdir is ~/TeleMetrum */ - final static String logdirName = "TeleMetrum"; - - /* UI Component to pop dialogs up */ - static Component component; - - /* Log directory */ - static File logdir; - - /* Channel (map serial to channel) */ - static Hashtable channels; - - /* Voice preference */ - static boolean voice; - - /* Callsign preference */ - static String callsign; - - /* Firmware directory */ - static File firmwaredir; - - public static void init(Component ui) { - preferences = Preferences.userRoot().node("/org/altusmetrum/altosui"); - - component = ui; - - /* Initialize logdir from preferences */ - String logdir_string = preferences.get(logdirPreference, null); - if (logdir_string != null) - logdir = new File(logdir_string); - else { - /* Use the file system view default directory */ - logdir = new File(FileSystemView.getFileSystemView().getDefaultDirectory(), logdirName); - if (!logdir.exists()) - logdir.mkdirs(); - } - - channels = new Hashtable(); - - voice = preferences.getBoolean(voicePreference, true); - - callsign = preferences.get(callsignPreference,"N0CALL"); - - String firmwaredir_string = preferences.get(firmwaredirPreference, null); - if (firmwaredir_string != null) - firmwaredir = new File(firmwaredir_string); - else - firmwaredir = null; - } - - static void flush_preferences() { - try { - preferences.flush(); - } catch (BackingStoreException ee) { - JOptionPane.showMessageDialog(component, - preferences.absolutePath(), - "Cannot save prefernces", - JOptionPane.ERROR_MESSAGE); - } - } - - public static void set_logdir(File new_logdir) { - logdir = new_logdir; - synchronized (preferences) { - preferences.put(logdirPreference, logdir.getPath()); - flush_preferences(); - } - } - - private static boolean check_dir(File dir) { - if (!dir.exists()) { - if (!dir.mkdirs()) { - JOptionPane.showMessageDialog(component, - dir.getName(), - "Cannot create directory", - JOptionPane.ERROR_MESSAGE); - return false; - } - } else if (!dir.isDirectory()) { - JOptionPane.showMessageDialog(component, - dir.getName(), - "Is not a directory", - JOptionPane.ERROR_MESSAGE); - return false; - } - return true; - } - - /* Configure the log directory. This is where all telemetry and eeprom files - * will be written to, and where replay will look for telemetry files - */ - public static void ConfigureLog() { - JFileChooser logdir_chooser = new JFileChooser(logdir.getParentFile()); - - logdir_chooser.setDialogTitle("Configure Data Logging Directory"); - logdir_chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - - if (logdir_chooser.showDialog(component, "Select Directory") == JFileChooser.APPROVE_OPTION) { - File dir = logdir_chooser.getSelectedFile(); - if (check_dir(dir)) - set_logdir(dir); - } - } - - public static File logdir() { - return logdir; - } - - public static void set_channel(int serial, int new_channel) { - channels.put(serial, new_channel); - synchronized (preferences) { - preferences.putInt(String.format(channelPreferenceFormat, serial), new_channel); - flush_preferences(); - } - } - - public static int channel(int serial) { - if (channels.containsKey(serial)) - return channels.get(serial); - int channel = preferences.getInt(String.format(channelPreferenceFormat, serial), 0); - channels.put(serial, channel); - return channel; - } - - public static void set_voice(boolean new_voice) { - voice = new_voice; - synchronized (preferences) { - preferences.putBoolean(voicePreference, voice); - flush_preferences(); - } - } - - public static boolean voice() { - return voice; - } - - public static void set_callsign(String new_callsign) { - callsign = new_callsign; - synchronized(preferences) { - preferences.put(callsignPreference, callsign); - flush_preferences(); - } - } - - public static String callsign() { - return callsign; - } - - public static void set_firmwaredir(File new_firmwaredir) { - firmwaredir = new_firmwaredir; - synchronized (preferences) { - preferences.put(firmwaredirPreference, firmwaredir.getPath()); - flush_preferences(); - } - } - - public static File firmwaredir() { - return firmwaredir; - } -} diff --git a/ao-tools/altosui/AltosReader.java b/ao-tools/altosui/AltosReader.java deleted file mode 100644 index b9280a0c..00000000 --- a/ao-tools/altosui/AltosReader.java +++ /dev/null @@ -1,28 +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.io.*; -import java.util.*; -import java.text.*; - -public class AltosReader { - public AltosRecord read() throws IOException, ParseException { return null; } - public void close() { } - public void write_comments(PrintStream out) { } -} diff --git a/ao-tools/altosui/AltosRecord.java b/ao-tools/altosui/AltosRecord.java deleted file mode 100644 index 1160a273..00000000 --- a/ao-tools/altosui/AltosRecord.java +++ /dev/null @@ -1,219 +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.lang.*; -import java.text.*; -import java.util.HashMap; -import java.io.*; - -public class AltosRecord { - int version; - String callsign; - int serial; - int flight; - int rssi; - int status; - int state; - int tick; - int accel; - int pres; - int temp; - int batt; - int drogue; - int main; - int flight_accel; - int ground_accel; - int flight_vel; - int flight_pres; - int ground_pres; - int accel_plus_g; - int accel_minus_g; - AltosGPS gps; - - double time; /* seconds since boost */ - - /* - * Values for our MP3H6115A pressure sensor - * - * From the data sheet: - * - * Pressure range: 15-115 kPa - * Voltage at 115kPa: 2.82 - * Output scale: 27mV/kPa - * - * - * 27 mV/kPa * 2047 / 3300 counts/mV = 16.75 counts/kPa - * 2.82V * 2047 / 3.3 counts/V = 1749 counts/115 kPa - */ - - static final double counts_per_kPa = 27 * 2047 / 3300; - static final double counts_at_101_3kPa = 1674.0; - - static double - barometer_to_pressure(double count) - { - return ((count / 16.0) / 2047.0 + 0.095) / 0.009 * 1000.0; - } - - public double raw_pressure() { - return barometer_to_pressure(pres); - } - - public double filtered_pressure() { - return barometer_to_pressure(flight_pres); - } - - public double ground_pressure() { - return barometer_to_pressure(ground_pres); - } - - public double filtered_altitude() { - return AltosConvert.pressure_to_altitude(filtered_pressure()); - } - - public double raw_altitude() { - return AltosConvert.pressure_to_altitude(raw_pressure()); - } - - public double ground_altitude() { - return AltosConvert.pressure_to_altitude(ground_pressure()); - } - - public double filtered_height() { - return filtered_altitude() - ground_altitude(); - } - - public double raw_height() { - return raw_altitude() - ground_altitude(); - } - - public double battery_voltage() { - return AltosConvert.cc_battery_to_voltage(batt); - } - - public double main_voltage() { - return AltosConvert.cc_ignitor_to_voltage(main); - } - - public double drogue_voltage() { - return AltosConvert.cc_ignitor_to_voltage(drogue); - } - - /* Value for the CC1111 built-in temperature sensor - * Output voltage at 0°C = 0.755V - * Coefficient = 0.00247V/°C - * Reference voltage = 1.25V - * - * temp = ((value / 32767) * 1.25 - 0.755) / 0.00247 - * = (value - 19791.268) / 32768 * 1.25 / 0.00247 - */ - - static double - thermometer_to_temperature(double thermo) - { - return (thermo - 19791.268) / 32728.0 * 1.25 / 0.00247; - } - - public double temperature() { - return thermometer_to_temperature(temp); - } - - double accel_counts_per_mss() { - double counts_per_g = Math.abs(accel_minus_g - accel_plus_g) / 2; - - return counts_per_g / 9.80665; - } - public double acceleration() { - return (ground_accel - accel) / accel_counts_per_mss(); - } - - public double accel_speed() { - double speed = flight_vel / (accel_counts_per_mss() * 100.0); - return speed; - } - - public String state() { - return Altos.state_name(state); - } - - public static String gets(FileInputStream s) throws IOException { - int c; - String line = ""; - - while ((c = s.read()) != -1) { - if (c == '\r') - continue; - if (c == '\n') { - return line; - } - line = line + (char) c; - } - return null; - } - - public AltosRecord(AltosRecord old) { - version = old.version; - callsign = old.callsign; - serial = old.serial; - flight = old.flight; - rssi = old.rssi; - status = old.status; - state = old.state; - tick = old.tick; - accel = old.accel; - pres = old.pres; - temp = old.temp; - batt = old.batt; - drogue = old.drogue; - main = old.main; - flight_accel = old.flight_accel; - ground_accel = old.ground_accel; - flight_vel = old.flight_vel; - flight_pres = old.flight_pres; - ground_pres = old.ground_pres; - accel_plus_g = old.accel_plus_g; - accel_minus_g = old.accel_minus_g; - gps = new AltosGPS(old.gps); - } - - public AltosRecord() { - version = 0; - callsign = "N0CALL"; - serial = 0; - flight = 0; - rssi = 0; - status = 0; - state = Altos.ao_flight_startup; - tick = 0; - accel = 0; - pres = 0; - temp = 0; - batt = 0; - drogue = 0; - main = 0; - flight_accel = 0; - ground_accel = 0; - flight_vel = 0; - flight_pres = 0; - ground_pres = 0; - accel_plus_g = 0; - accel_minus_g = 0; - gps = new AltosGPS(); - } -} diff --git a/ao-tools/altosui/AltosRecordIterable.java b/ao-tools/altosui/AltosRecordIterable.java deleted file mode 100644 index a7df92d1..00000000 --- a/ao-tools/altosui/AltosRecordIterable.java +++ /dev/null @@ -1,34 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -public abstract class AltosRecordIterable implements Iterable { - public abstract Iterator iterator(); - public void write_comments(PrintStream out) { } -} diff --git a/ao-tools/altosui/AltosReplayReader.java b/ao-tools/altosui/AltosReplayReader.java deleted file mode 100644 index 4e5e1d93..00000000 --- a/ao-tools/altosui/AltosReplayReader.java +++ /dev/null @@ -1,57 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -/* - * Open an existing telemetry file and replay it in realtime - */ - -public class AltosReplayReader extends AltosFlightReader { - Iterator iterator; - - public AltosRecord read() { - if (iterator.hasNext()) - return iterator.next(); - return null; - } - - public void close (boolean interrupted) { - } - - void update(AltosState state) throws InterruptedException { - /* Make it run in realtime after the rocket leaves the pad */ - if (state.state > Altos.ao_flight_pad) - Thread.sleep((int) (Math.min(state.time_change,10) * 1000)); - } - - public AltosReplayReader(Iterator in_iterator, String in_name) { - iterator = in_iterator; - name = in_name; - } -} diff --git a/ao-tools/altosui/AltosRomconfig.java b/ao-tools/altosui/AltosRomconfig.java deleted file mode 100644 index 55056b5e..00000000 --- a/ao-tools/altosui/AltosRomconfig.java +++ /dev/null @@ -1,147 +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.io.*; - -public class AltosRomconfig { - public boolean valid; - public int version; - public int check; - public int serial_number; - public int radio_calibration; - - static int get_int(byte[] bytes, int start, int len) { - int v = 0; - int o = 0; - while (len > 0) { - v = v | ((((int) bytes[start]) & 0xff) << o); - start++; - len--; - o += 8; - } - return v; - } - - static void put_int(int value, byte[] bytes, int start, int len) { - while (len > 0) { - bytes[start] = (byte) (value & 0xff); - start++; - len--; - value >>= 8; - } - } - - static void put_string(String value, byte[] bytes, int start) { - for (int i = 0; i < value.length(); i++) - bytes[start + i] = (byte) value.charAt(i); - } - - static final int AO_USB_DESC_STRING = 3; - - static void put_usb_serial(int value, byte[] bytes, int start) { - int offset = start + 0xa; - int string_num = 0; - - while (offset < bytes.length && bytes[offset] != 0) { - if (bytes[offset + 1] == AO_USB_DESC_STRING) { - ++string_num; - if (string_num == 4) - break; - } - offset += ((int) bytes[offset]) & 0xff; - } - if (offset >= bytes.length || bytes[offset] == 0) - return; - int len = ((((int) bytes[offset]) & 0xff) - 2) / 2; - String fmt = String.format("%%0%dd", len); - - String s = String.format(fmt, value); - if (s.length() != len) { - System.out.printf("weird usb length issue %s isn't %d\n", - s, len); - return; - } - for (int i = 0; i < len; i++) { - bytes[offset + 2 + i*2] = (byte) s.charAt(i); - bytes[offset + 2 + i*2+1] = 0; - } - } - - public AltosRomconfig(byte[] bytes, int offset) { - version = get_int(bytes, offset + 0, 2); - check = get_int(bytes, offset + 2, 2); - if (check == (~version & 0xffff)) { - switch (version) { - case 2: - case 1: - serial_number = get_int(bytes, offset + 4, 2); - radio_calibration = get_int(bytes, offset + 6, 4); - valid = true; - break; - } - } - } - - public AltosRomconfig(AltosHexfile hexfile) { - this(hexfile.data, 0xa0 - hexfile.address); - } - - public void write(byte[] bytes, int offset) throws IOException { - if (!valid) - throw new IOException("rom configuration invalid"); - - if (offset < 0 || bytes.length < offset + 10) - throw new IOException("image cannot contain rom config"); - - AltosRomconfig existing = new AltosRomconfig(bytes, offset); - if (!existing.valid) - throw new IOException("image does not contain existing rom config"); - - switch (existing.version) { - case 2: - put_usb_serial(serial_number, bytes, offset); - case 1: - put_int(serial_number, bytes, offset + 4, 2); - put_int(radio_calibration, bytes, offset + 6, 4); - break; - } - } - - public void write (AltosHexfile hexfile) throws IOException { - write(hexfile.data, 0xa0 - hexfile.address); - AltosRomconfig check = new AltosRomconfig(hexfile); - if (!check.valid()) - throw new IOException("writing new rom config failed\n"); - } - - public AltosRomconfig(int in_serial_number, int in_radio_calibration) { - valid = true; - version = 1; - check = (~version & 0xffff); - serial_number = in_serial_number; - radio_calibration = in_radio_calibration; - } - - public boolean valid() { - return valid && serial_number != 0; - } - - public AltosRomconfig() { - valid = false; - } -} diff --git a/ao-tools/altosui/AltosRomconfigUI.java b/ao-tools/altosui/AltosRomconfigUI.java deleted file mode 100644 index e1dc974e..00000000 --- a/ao-tools/altosui/AltosRomconfigUI.java +++ /dev/null @@ -1,186 +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 javax.swing.event.*; -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; - -public class AltosRomconfigUI - extends JDialog - implements ActionListener -{ - Container pane; - Box box; - JLabel serial_label; - JLabel radio_calibration_label; - - JFrame owner; - JTextField serial_value; - JTextField radio_calibration_value; - - JButton ok; - JButton cancel; - - /* Build the UI using a grid bag */ - public AltosRomconfigUI(JFrame in_owner) { - super (in_owner, "Configure TeleMetrum Rom Values", true); - - owner = in_owner; - GridBagConstraints c; - - Insets il = new Insets(4,4,4,4); - Insets ir = new Insets(4,4,4,4); - - pane = getContentPane(); - pane.setLayout(new GridBagLayout()); - - /* Serial */ - c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 0; - c.gridwidth = 3; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - serial_label = new JLabel("Serial:"); - pane.add(serial_label, c); - - c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 0; - c.gridwidth = 3; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - serial_value = new JTextField("0"); - pane.add(serial_value, c); - - /* Radio calibration value */ - c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 1; - c.gridwidth = 3; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.LINE_START; - c.insets = il; - c.ipady = 5; - radio_calibration_label = new JLabel("Radio Calibration:"); - pane.add(radio_calibration_label, c); - - c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 1; - c.gridwidth = 3; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.anchor = GridBagConstraints.LINE_START; - c.insets = ir; - c.ipady = 5; - radio_calibration_value = new JTextField("1186611"); - pane.add(radio_calibration_value, c); - - /* Buttons */ - c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 2; - c.gridwidth = 3; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.CENTER; - c.insets = il; - ok = new JButton("OK"); - pane.add(ok, c); - ok.addActionListener(this); - ok.setActionCommand("ok"); - - c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 2; - c.gridwidth = 3; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.CENTER; - c.insets = il; - cancel = new JButton("Cancel"); - pane.add(cancel, c); - cancel.addActionListener(this); - cancel.setActionCommand("cancel"); - - pack(); - setLocationRelativeTo(owner); - } - - boolean selected; - - /* Listen for events from our buttons */ - public void actionPerformed(ActionEvent e) { - String cmd = e.getActionCommand(); - - if (cmd.equals("ok")) { - AltosRomconfig romconfig = romconfig(); - if (romconfig == null || !romconfig.valid()) { - JOptionPane.showMessageDialog(this, - "Invalid serial number or radio calibration value", - "Invalid rom configuration", - JOptionPane.ERROR_MESSAGE); - return; - } - selected = true; - } - setVisible(false); - } - - int serial() { - return Integer.parseInt(serial_value.getText()); - } - - void set_serial(int serial) { - serial_value.setText(String.format("%d", serial)); - } - - int radio_calibration() { - return Integer.parseInt(radio_calibration_value.getText()); - } - - void set_radio_calibration(int calibration) { - radio_calibration_value.setText(String.format("%d", calibration)); - } - - public void set(AltosRomconfig config) { - if (config != null && config.valid()) { - set_serial(config.serial_number); - set_radio_calibration(config.radio_calibration); - } - } - - AltosRomconfig romconfig() { - try { - return new AltosRomconfig(serial(), radio_calibration()); - } catch (NumberFormatException ne) { - return null; - } - } - - public AltosRomconfig showDialog() { - setVisible(true); - if (selected) - return romconfig(); - return null; - } -} diff --git a/ao-tools/altosui/AltosSerial.java b/ao-tools/altosui/AltosSerial.java deleted file mode 100644 index b19143e5..00000000 --- a/ao-tools/altosui/AltosSerial.java +++ /dev/null @@ -1,253 +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. - */ - -/* - * Deal with TeleDongle on a serial port - */ - -package altosui; - -import java.lang.*; -import java.io.*; -import java.util.concurrent.*; -import java.util.*; - -import libaltosJNI.*; - -/* - * This class reads from the serial port and places each received - * line in a queue. Dealing with that queue is left up to other - * threads. - */ - -public class AltosSerial implements Runnable { - - static List devices_opened = Collections.synchronizedList(new LinkedList()); - - AltosDevice device; - SWIGTYPE_p_altos_file altos; - LinkedList> monitors; - LinkedBlockingQueue reply_queue; - Thread input_thread; - String line; - byte[] line_bytes; - int line_count; - boolean monitor_mode; - - public void run () { - int c; - - try { - for (;;) { - c = libaltos.altos_getchar(altos, 0); - if (Thread.interrupted()) - break; - if (c == libaltosConstants.LIBALTOS_ERROR) { - for (int e = 0; e < monitors.size(); e++) { - LinkedBlockingQueue q = monitors.get(e); - q.put(new AltosLine()); - } - reply_queue.put (new AltosLine()); - break; - } - if (c == libaltosConstants.LIBALTOS_TIMEOUT) - continue; - if (c == '\r') - continue; - synchronized(this) { - if (c == '\n') { - if (line_count != 0) { - try { - line = new String(line_bytes, 0, line_count, "UTF-8"); - } catch (UnsupportedEncodingException ue) { - line = ""; - for (int i = 0; i < line_count; i++) - line = line + line_bytes[i]; - } - if (line.startsWith("VERSION") || line.startsWith("CRC")) { - for (int e = 0; e < monitors.size(); e++) { - LinkedBlockingQueue q = monitors.get(e); - q.put(new AltosLine (line)); - } - } else { -// System.out.printf("GOT: %s\n", line); - reply_queue.put(new AltosLine (line)); - } - line_count = 0; - line = ""; - } - } else { - if (line_bytes == null) { - line_bytes = new byte[256]; - } else if (line_count == line_bytes.length) { - byte[] new_line_bytes = new byte[line_count * 2]; - System.arraycopy(line_bytes, 0, new_line_bytes, 0, line_count); - line_bytes = new_line_bytes; - } - line_bytes[line_count] = (byte) c; - line_count++; - } - } - } - } catch (InterruptedException e) { - } - } - - public void flush_output() { - if (altos != null) - libaltos.altos_flush(altos); - } - - public void flush_input() { - flush_output(); - boolean got_some; - do { - try { - Thread.sleep(100); - } catch (InterruptedException ie) { - } - got_some = !reply_queue.isEmpty(); - synchronized(this) { - if (!"VERSION".startsWith(line) && - !line.startsWith("VERSION")) - line = ""; - reply_queue.clear(); - } - } while (got_some); - } - - public String get_reply() throws InterruptedException { - flush_output(); - AltosLine line = reply_queue.take(); - 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); - } - - public void remove_monitor(LinkedBlockingQueue q) { - monitors.remove(q); - if (monitors.isEmpty()) - set_monitor(false); - } - - public void close() { - if (altos != null) { - libaltos.altos_close(altos); - } - if (input_thread != null) { - try { - input_thread.interrupt(); - input_thread.join(); - } catch (InterruptedException e) { - } - input_thread = null; - } - if (altos != null) { - libaltos.altos_free(altos); - altos = null; - } - synchronized (devices_opened) { - devices_opened.remove(device.getPath()); - } - } - - public void putc(char c) { - if (altos != null) - libaltos.altos_putchar(altos, c); - } - - public void print(String data) { -// System.out.printf("\"%s\" ", data); - for (int i = 0; i < data.length(); i++) - putc(data.charAt(i)); - } - - public void printf(String format, Object ... arguments) { - print(String.format(format, arguments)); - } - - private void open() throws FileNotFoundException, AltosSerialInUseException { - synchronized (devices_opened) { - if (devices_opened.contains(device.getPath())) - throw new AltosSerialInUseException(device); - devices_opened.add(device.getPath()); - } - altos = libaltos.altos_open(device); - if (altos == null) { - close(); - throw new FileNotFoundException(device.toShortString()); - } - input_thread = new Thread(this); - input_thread.start(); - print("~\nE 0\n"); - set_monitor(false); - flush_output(); - } - - public void set_radio() { - set_channel(AltosPreferences.channel(device.getSerial())); - set_callsign(AltosPreferences.callsign()); - } - - public void set_channel(int channel) { - if (altos != null) { - if (monitor_mode) - printf("m 0\nc r %d\nm 1\n", channel); - else - printf("c r %d\n", channel); - flush_output(); - } - } - - void set_monitor(boolean monitor) { - monitor_mode = monitor; - if (altos != null) { - if (monitor) - printf("m 1\n"); - else - printf("m 0\n"); - flush_output(); - } - } - - public void set_callsign(String callsign) { - if (altos != null) { - printf ("c c %s\n", callsign); - flush_output(); - } - } - - public AltosSerial(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException { - device = in_device; - line = ""; - monitor_mode = false; - monitors = new LinkedList> (); - reply_queue = new LinkedBlockingQueue (); - open(); - } -} diff --git a/ao-tools/altosui/AltosSerialInUseException.java b/ao-tools/altosui/AltosSerialInUseException.java deleted file mode 100644 index 4b108c7c..00000000 --- a/ao-tools/altosui/AltosSerialInUseException.java +++ /dev/null @@ -1,28 +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 libaltosJNI.*; - -public class AltosSerialInUseException extends Exception { - public altos_device device; - - public AltosSerialInUseException (altos_device in_device) { - device = in_device; - } -} diff --git a/ao-tools/altosui/AltosSerialMonitor.java b/ao-tools/altosui/AltosSerialMonitor.java deleted file mode 100644 index ad0e9295..00000000 --- a/ao-tools/altosui/AltosSerialMonitor.java +++ /dev/null @@ -1,22 +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; - -public interface AltosSerialMonitor { - void data(String data); -} diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java deleted file mode 100644 index 80970605..00000000 --- a/ao-tools/altosui/AltosSiteMap.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright © 2010 Anthony Towns - * - * 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.image.*; -import java.awt.event.*; -import javax.swing.*; -import javax.swing.event.MouseInputAdapter; -import javax.imageio.ImageIO; -import javax.swing.table.*; -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.lang.Math; -import java.awt.geom.Point2D; -import java.awt.geom.Line2D; - -public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { - // preferred vertical step in a tile in naut. miles - // will actually choose a step size between x and 2x, where this - // is 1.5x - static final double tile_size_nmi = 0.75; - - static final int px_size = 512; - - static final int MAX_TILE_DELTA = 100; - - private static Point2D.Double translatePoint(Point2D.Double p, - Point2D.Double d) - { - return new Point2D.Double(p.x + d.x, p.y + d.y); - } - - static class LatLng { - public double lat, lng; - public LatLng(double lat, double lng) { - this.lat = lat; - this.lng = lng; - } - } - - // based on google js - // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js - // search for fromLatLngToPoint and fromPointToLatLng - private static Point2D.Double pt(LatLng latlng, int zoom) { - double scale_x = 256/360.0 * Math.pow(2, zoom); - double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - return pt(latlng, scale_x, scale_y); - } - - private static Point2D.Double pt(LatLng latlng, - double scale_x, double scale_y) - { - Point2D.Double res = new Point2D.Double(); - double e; - - res.x = latlng.lng * scale_x; - - e = Math.sin(Math.toRadians(latlng.lat)); - e = Math.max(e,-(1-1.0E-15)); - e = Math.min(e, 1-1.0E-15 ); - - res.y = 0.5*Math.log((1+e)/(1-e))*-scale_y; - return res; - } - - static private LatLng latlng(Point2D.Double pt, - double scale_x, double scale_y) - { - double lat, lng; - double rads; - - lng = pt.x/scale_x; - rads = 2 * Math.atan(Math.exp(-pt.y/scale_y)); - lat = Math.toDegrees(rads - Math.PI/2); - - return new LatLng(lat,lng); - } - - int zoom; - double scale_x, scale_y; - - private Point2D.Double pt(double lat, double lng) { - return pt(new LatLng(lat, lng), scale_x, scale_y); - } - - private LatLng latlng(double x, double y) { - return latlng(new Point2D.Double(x,y), scale_x, scale_y); - } - private LatLng latlng(Point2D.Double pt) { - return latlng(pt, scale_x, scale_y); - } - - HashMap mapTiles = new HashMap(); - Point2D.Double centre; - - private Point2D.Double tileCoordOffset(Point p) { - return new Point2D.Double(centre.x - p.x*px_size, - centre.y - p.y * px_size); - } - - private Point tileOffset(Point2D.Double p) { - return new Point((int)Math.floor((centre.x+p.x)/px_size), - (int)Math.floor((centre.y+p.y)/px_size)); - } - - private Point2D.Double getBaseLocation(double lat, double lng) { - Point2D.Double locn, north_step; - - zoom = 2; - // stupid loop structure to please Java's control flow analysis - do { - zoom++; - scale_x = 256/360.0 * Math.pow(2, zoom); - scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - locn = pt(lat, lng); - north_step = pt(lat+tile_size_nmi*4/3/60.0, lng); - if (locn.y - north_step.y > px_size) - break; - } while (zoom < 22); - locn.x = -px_size * Math.floor(locn.x/px_size); - locn.y = -px_size * Math.floor(locn.y/px_size); - return locn; - } - - public void reset() { - // nothing - } - - private void bgLoadMap(final AltosSiteMapTile tile, - final File pngfile, final String pngurl) - { - //System.out.printf("Loading/fetching map %s\n", pngfile); - Thread thread = new Thread() { - public void run() { - ImageIcon res; - res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl); - if (res != null) { - tile.loadMap(res); - } else { - System.out.printf("# Failed to fetch file %s\n", pngfile); - System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); - } - } - }; - thread.start(); - } - - public static void prefetchMaps(double lat, double lng, int w, int h) { - AltosPreferences.init(null); - - AltosSiteMap asm = new AltosSiteMap(true); - asm.centre = asm.getBaseLocation(lat, lng); - - Point2D.Double p = new Point2D.Double(); - Point2D.Double p2; - int dx = -w/2, dy = -h/2; - for (int y = dy; y < h+dy; y++) { - for (int x = dx; x < w+dx; x++) { - LatLng map_latlng = asm.latlng( - -asm.centre.x + x*px_size + px_size/2, - -asm.centre.y + y*px_size + px_size/2); - File pngfile = asm.MapFile(map_latlng.lat, map_latlng.lng); - String pngurl = asm.MapURL(map_latlng.lat, map_latlng.lng); - if (pngfile.exists()) { - System.out.printf("Already have %s\n", pngfile); - } else if (AltosSiteMapCache.fetchMap(pngfile, pngurl)) { - System.out.printf("Fetched map %s\n", pngfile); - } else { - System.out.printf("# Failed to fetch file %s\n", pngfile); - System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); - } - } - } - } - - private void initMap(AltosSiteMapTile tile, Point offset) { - Point2D.Double coord = tileCoordOffset(offset); - - LatLng map_latlng = latlng(px_size/2-coord.x, px_size/2-coord.y); - - File pngfile = MapFile(map_latlng.lat, map_latlng.lng); - String pngurl = MapURL(map_latlng.lat, map_latlng.lng); - bgLoadMap(tile, pngfile, pngurl); - } - - private void initMaps(double lat, double lng) { - centre = getBaseLocation(lat, lng); - - for (Point k : mapTiles.keySet()) { - initMap(mapTiles.get(k), k); - } - } - - private File MapFile(double lat, double lng) { - char chlat = lat < 0 ? 'S' : 'N'; - char chlng = lng < 0 ? 'E' : 'W'; - if (lat < 0) lat = -lat; - if (lng < 0) lng = -lng; - return new File(AltosPreferences.logdir(), - String.format("map-%c%.6f,%c%.6f-%d.png", - chlat, lat, chlng, lng, zoom)); - } - - private String MapURL(double lat, double lng) { - return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size); - } - - boolean initialised = false; - Point2D.Double last_pt = null; - int last_state = -1; - public void show(final AltosState state, final int crc_errors) { - // if insufficient gps data, nothing to update - if (state.gps == null) - return; - if (state.pad_lat == 0 && state.pad_lon == 0) - return; - if (!state.gps.locked) { - if (state.gps.nsat < 4) - return; - } - - if (!initialised) { - initMaps(state.pad_lat, state.pad_lon); - initialised = true; - } - - final Point2D.Double pt = pt(state.gps.lat, state.gps.lon); - if (last_pt == pt && last_state == state.state) - return; - - if (last_pt == null) { - last_pt = pt; - } - boolean in_any = false; - for (Point offset : mapTiles.keySet()) { - AltosSiteMapTile tile = mapTiles.get(offset); - Point2D.Double ref, lref; - ref = translatePoint(pt, tileCoordOffset(offset)); - lref = translatePoint(last_pt, tileCoordOffset(offset)); - tile.show(state, crc_errors, lref, ref); - if (0 <= ref.x && ref.x < px_size) - if (0 <= ref.y && ref.y < px_size) - in_any = true; - } - - Point offset = tileOffset(pt); - if (!in_any) { - Point2D.Double ref, lref; - ref = translatePoint(pt, tileCoordOffset(offset)); - lref = translatePoint(last_pt, tileCoordOffset(offset)); - - AltosSiteMapTile tile = createTile(offset); - tile.show(state, crc_errors, lref, ref); - initMap(tile, offset); - finishTileLater(tile, offset); - } - - scrollRocketToVisible(pt); - - if (offset != tileOffset(last_pt)) { - ensureTilesAround(offset); - } - - last_pt = pt; - last_state = state.state; - } - - private AltosSiteMapTile createTile(Point offset) { - AltosSiteMapTile tile = new AltosSiteMapTile(px_size); - mapTiles.put(offset, tile); - return tile; - } - private void finishTileLater(final AltosSiteMapTile tile, - final Point offset) - { - SwingUtilities.invokeLater( new Runnable() { - public void run() { - addTileAt(tile, offset); - } - } ); - } - - private void ensureTilesAround(Point base_offset) { - for (int x = -1; x <= 1; x++) { - for (int y = -1; y <= 1; y++) { - Point offset = new Point(base_offset.x + x, base_offset.y + y); - if (mapTiles.containsKey(offset)) - continue; - AltosSiteMapTile tile = createTile(offset); - initMap(tile, offset); - finishTileLater(tile, offset); - } - } - } - - private Point topleft = new Point(0,0); - private void scrollRocketToVisible(Point2D.Double pt) { - Rectangle r = comp.getVisibleRect(); - Point2D.Double copt = translatePoint(pt, tileCoordOffset(topleft)); - int dx = (int)copt.x - r.width/2 - r.x; - int dy = (int)copt.y - r.height/2 - r.y; - if (Math.abs(dx) > r.width/4 || Math.abs(dy) > r.height/4) { - r.x += dx; - r.y += dy; - comp.scrollRectToVisible(r); - } - } - - private void addTileAt(AltosSiteMapTile tile, Point offset) { - if (Math.abs(offset.x) >= MAX_TILE_DELTA || - Math.abs(offset.y) >= MAX_TILE_DELTA) - { - System.out.printf("Rocket too far away from pad (tile %d,%d)\n", - offset.x, offset.y); - return; - } - - boolean review = false; - Rectangle r = comp.getVisibleRect(); - if (offset.x < topleft.x) { - r.x += (topleft.x - offset.x) * px_size; - topleft.x = offset.x; - review = true; - } - if (offset.y < topleft.y) { - r.y += (topleft.y - offset.y) * px_size; - topleft.y = offset.y; - review = true; - } - GridBagConstraints c = new GridBagConstraints(); - c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.BOTH; - // put some space between the map tiles, debugging only - // c.insets = new Insets(5, 5, 5, 5); - - c.gridx = offset.x + MAX_TILE_DELTA; - c.gridy = offset.y + MAX_TILE_DELTA; - layout.setConstraints(tile, c); - - comp.add(tile); - if (review) { - comp.scrollRectToVisible(r); - } - } - - private AltosSiteMap(boolean knowWhatYouAreDoing) { - if (!knowWhatYouAreDoing) { - throw new RuntimeException("Arggh."); - } - } - - JComponent comp = new JComponent() { }; - private GridBagLayout layout = new GridBagLayout(); - - public AltosSiteMap() { - GrabNDrag scroller = new GrabNDrag(comp); - - comp.setLayout(layout); - - for (int x = -1; x <= 1; x++) { - for (int y = -1; y <= 1; y++) { - Point offset = new Point(x, y); - AltosSiteMapTile t = createTile(offset); - addTileAt(t, offset); - } - } - setViewportView(comp); - setPreferredSize(new Dimension(500,200)); - } -} diff --git a/ao-tools/altosui/AltosSiteMapCache.java b/ao-tools/altosui/AltosSiteMapCache.java deleted file mode 100644 index 2e62cc45..00000000 --- a/ao-tools/altosui/AltosSiteMapCache.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright © 2010 Anthony Towns - * - * 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.image.*; -import java.awt.event.*; -import javax.swing.*; -import javax.imageio.ImageIO; -import javax.swing.table.*; -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.net.URL; -import java.net.URLConnection; - -public class AltosSiteMapCache extends JLabel { - public static boolean fetchMap(File file, String url) { - URL u; - - try { - u = new URL(url); - } catch (java.net.MalformedURLException e) { - return false; - } - - byte[] data; - try { - URLConnection uc = u.openConnection(); - int contentLength = uc.getContentLength(); - InputStream in = new BufferedInputStream(uc.getInputStream()); - int bytesRead = 0; - int offset = 0; - data = new byte[contentLength]; - while (offset < contentLength) { - bytesRead = in.read(data, offset, data.length - offset); - if (bytesRead == -1) - break; - offset += bytesRead; - } - in.close(); - - if (offset != contentLength) { - return false; - } - } catch (IOException e) { - return false; - } - - try { - FileOutputStream out = new FileOutputStream(file); - out.write(data); - out.flush(); - out.close(); - } catch (FileNotFoundException e) { - return false; - } catch (IOException e) { - if (file.exists()) { - file.delete(); - } - return false; - } - return true; - } - - public static ImageIcon fetchAndLoadMap(File pngfile, String url) { - if (!pngfile.exists()) { - if (!fetchMap(pngfile, url)) { - return null; - } - } - return loadMap(pngfile, url); - } - - public static ImageIcon loadMap(File pngfile, String url) { - if (!pngfile.exists()) { - return null; - } - - try { - return new ImageIcon(ImageIO.read(pngfile)); - } catch (IOException e) { - System.out.printf("# IO error trying to load %s\n", pngfile); - return null; - } - } -} diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java deleted file mode 100644 index 8301f42b..00000000 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright © 2010 Anthony Towns - * - * 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.image.*; -import java.awt.event.*; -import javax.swing.*; -import javax.imageio.ImageIO; -import javax.swing.table.*; -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.lang.Math; -import java.awt.geom.Point2D; -import java.awt.geom.Line2D; - -public class AltosSiteMapTile extends JLayeredPane { - JLabel mapLabel; - JLabel draw; - Graphics2D g2d; - - public void loadMap(ImageIcon icn) { - mapLabel.setIcon(icn); - } - - static Color stateColors[] = { - Color.WHITE, // startup - Color.WHITE, // idle - Color.WHITE, // pad - Color.RED, // boost - Color.PINK, // fast - Color.YELLOW, // coast - Color.CYAN, // drogue - Color.BLUE, // main - Color.BLACK // landed - }; - - private boolean drawn_landed_circle = false; - private boolean drawn_boost_circle = false; - public synchronized void show(AltosState state, int crc_errors, - Point2D.Double last_pt, Point2D.Double pt) - { - if (0 <= state.state && state.state < stateColors.length) { - g2d.setColor(stateColors[state.state]); - } - g2d.draw(new Line2D.Double(last_pt, pt)); - - if (state.state == 3 && !drawn_boost_circle) { - drawn_boost_circle = true; - g2d.setColor(Color.RED); - g2d.drawOval((int)last_pt.x-5, (int)last_pt.y-5, 10, 10); - g2d.drawOval((int)last_pt.x-20, (int)last_pt.y-20, 40, 40); - g2d.drawOval((int)last_pt.x-35, (int)last_pt.y-35, 70, 70); - } - if (state.state == 8 && !drawn_landed_circle) { - drawn_landed_circle = true; - g2d.setColor(Color.BLACK); - g2d.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10); - g2d.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40); - g2d.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); - } - - repaint(); - } - - public static Graphics2D fillLabel(JLabel l, Color c, int px_size) { - BufferedImage img = new BufferedImage(px_size, px_size, - BufferedImage.TYPE_INT_ARGB); - Graphics2D g = img.createGraphics(); - g.setColor(c); - g.fillRect(0, 0, px_size, px_size); - l.setIcon(new ImageIcon(img)); - return g; - } - - public AltosSiteMapTile(int px_size) { - setPreferredSize(new Dimension(px_size, px_size)); - - mapLabel = new JLabel(); - fillLabel(mapLabel, Color.GRAY, px_size); - mapLabel.setOpaque(true); - mapLabel.setBounds(0, 0, px_size, px_size); - add(mapLabel, new Integer(0)); - - draw = new JLabel(); - g2d = fillLabel(draw, new Color(127, 127, 127, 0), px_size); - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); - draw.setBounds(0, 0, px_size, px_size); - draw.setOpaque(false); - - add(draw, new Integer(1)); - } -} diff --git a/ao-tools/altosui/AltosState.java b/ao-tools/altosui/AltosState.java deleted file mode 100644 index ec499d5a..00000000 --- a/ao-tools/altosui/AltosState.java +++ /dev/null @@ -1,197 +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. - */ - -/* - * Track flight state from telemetry or eeprom data stream - */ - -package altosui; - -public class AltosState { - AltosRecord data; - - /* derived data */ - - long report_time; - - double time_change; - int tick; - - int state; - boolean landed; - boolean ascent; /* going up? */ - - double ground_altitude; - double height; - double speed; - double acceleration; - double battery; - double temperature; - double main_sense; - double drogue_sense; - double baro_speed; - - double max_height; - double max_acceleration; - double max_speed; - - AltosGPS gps; - - double pad_lat; - double pad_lon; - double pad_alt; - - static final int MIN_PAD_SAMPLES = 10; - - int npad; - int ngps; - int gps_waiting; - boolean gps_ready; - - AltosGreatCircle from_pad; - double elevation; /* from pad */ - double range; /* total distance */ - - double gps_height; - - int speak_tick; - double speak_altitude; - - - void init (AltosRecord cur, AltosState prev_state) { - int i; - AltosRecord prev; - - data = cur; - - ground_altitude = data.ground_altitude(); - height = data.filtered_altitude() - ground_altitude; - - report_time = System.currentTimeMillis(); - - acceleration = data.acceleration(); - speed = data.accel_speed(); - temperature = data.temperature(); - drogue_sense = data.drogue_voltage(); - main_sense = data.main_voltage(); - battery = data.battery_voltage(); - tick = data.tick; - state = data.state; - - if (prev_state != null) { - - /* Preserve any existing gps data */ - npad = prev_state.npad; - ngps = prev_state.ngps; - gps = prev_state.gps; - pad_lat = prev_state.pad_lat; - pad_lon = prev_state.pad_lon; - pad_alt = prev_state.pad_alt; - max_height = prev_state.max_height; - max_acceleration = prev_state.max_acceleration; - max_speed = prev_state.max_speed; - - /* make sure the clock is monotonic */ - while (tick < prev_state.tick) - tick += 65536; - - time_change = (tick - prev_state.tick) / 100.0; - - /* compute barometric speed */ - - double height_change = height - prev_state.height; - if (time_change > 0) - baro_speed = (prev_state.baro_speed * 3 + (height_change / time_change)) / 4.0; - else - baro_speed = prev_state.baro_speed; - } else { - npad = 0; - ngps = 0; - gps = null; - baro_speed = 0; - time_change = 0; - } - - if (state == Altos.ao_flight_pad) { - - /* Track consecutive 'good' gps reports, waiting for 10 of them */ - if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) - npad++; - else - npad = 0; - - /* Average GPS data while on the pad */ - if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) { - if (ngps > 1) { - /* filter pad position */ - pad_lat = (pad_lat * 31.0 + data.gps.lat) / 32.0; - pad_lon = (pad_lon * 31.0 + data.gps.lon) / 32.0; - pad_alt = (pad_alt * 31.0 + data.gps.alt) / 32.0; - } else { - pad_lat = data.gps.lat; - pad_lon = data.gps.lon; - pad_alt = data.gps.alt; - } - ngps++; - } - } - - gps_waiting = MIN_PAD_SAMPLES - npad; - if (gps_waiting < 0) - gps_waiting = 0; - - gps_ready = gps_waiting == 0; - - ascent = (Altos.ao_flight_boost <= state && - state <= Altos.ao_flight_coast); - - /* Only look at accelerometer data on the way up */ - if (ascent && acceleration > max_acceleration) - max_acceleration = acceleration; - if (ascent && speed > max_speed) - max_speed = speed; - - if (height > max_height) - max_height = height; - if (data.gps != null) { - if (gps == null || !gps.locked || data.gps.locked) - gps = data.gps; - if (ngps > 0 && gps.locked) { - from_pad = new AltosGreatCircle(pad_lat, pad_lon, gps.lat, gps.lon); - } - } - elevation = 0; - range = -1; - if (ngps > 0) { - gps_height = gps.alt - pad_alt; - if (from_pad != null) { - elevation = Math.atan2(height, from_pad.distance) * 180 / Math.PI; - range = Math.sqrt(height * height + from_pad.distance * from_pad.distance); - } - } else { - gps_height = 0; - } - } - - public AltosState(AltosRecord cur) { - init(cur, null); - } - - public AltosState (AltosRecord cur, AltosState prev) { - init(cur, prev); - } -} diff --git a/ao-tools/altosui/AltosTelemetry.java b/ao-tools/altosui/AltosTelemetry.java deleted file mode 100644 index bdb6466a..00000000 --- a/ao-tools/altosui/AltosTelemetry.java +++ /dev/null @@ -1,143 +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.lang.*; -import java.text.*; -import java.util.HashMap; - -/* - * Telemetry data contents - */ - - -/* - * The telemetry data stream is a bit of a mess at present, with no consistent - * formatting. In particular, the GPS data is formatted for viewing instead of parsing. - * However, the key feature is that every telemetry line contains all of the information - * necessary to describe the current rocket state, including the calibration values - * for accelerometer and barometer. - * - * GPS unlocked: - * - * VERSION 2 CALL KB0G SERIAL 51 FLIGHT 2 RSSI -68 STATUS ff STATE pad 1001 \ - * a: 16032 p: 21232 t: 20284 v: 25160 d: 204 m: 204 fa: 16038 ga: 16032 fv: 0 \ - * fp: 21232 gp: 21230 a+: 16049 a-: 16304 GPS 0 sat unlocked SAT 1 15 30 - * - * GPS locked: - * - * VERSION 2 CALL KB0G SERIAL 51 FLIGHT 2 RSSI -71 STATUS ff STATE pad 2504 \ - * a: 16028 p: 21220 t: 20360 v: 25004 d: 208 m: 200 fa: 16031 ga: 16032 fv: 330 \ - * fp: 21231 gp: 21230 a+: 16049 a-: 16304 \ - * GPS 9 sat 2010-02-13 17:16:51 35°20.0803'N 106°45.2235'W 1790m \ - * 0.00m/s(H) 0° 0.00m/s(V) 1.0(hdop) 0(herr) 0(verr) \ - * SAT 10 29 30 24 28 5 25 21 20 15 33 1 23 30 24 18 26 10 29 2 26 - */ - -public class AltosTelemetry extends AltosRecord { - public AltosTelemetry(String line) throws ParseException, AltosCRCException { - String[] words = line.split("\\s+"); - int i = 0; - - if (words[i].equals("CRC") && words[i+1].equals("INVALID")) { - i += 2; - AltosParse.word(words[i++], "RSSI"); - rssi = AltosParse.parse_int(words[i++]); - throw new AltosCRCException(rssi); - } - if (words[i].equals("CALL")) { - version = 0; - } else { - AltosParse.word (words[i++], "VERSION"); - version = AltosParse.parse_int(words[i++]); - } - - AltosParse.word (words[i++], "CALL"); - callsign = words[i++]; - - AltosParse.word (words[i++], "SERIAL"); - serial = AltosParse.parse_int(words[i++]); - - if (version >= 2) { - AltosParse.word (words[i++], "FLIGHT"); - flight = AltosParse.parse_int(words[i++]); - } else - flight = 0; - - AltosParse.word(words[i++], "RSSI"); - rssi = AltosParse.parse_int(words[i++]); - - /* Older telemetry data had mis-computed RSSI value */ - if (version <= 2) - rssi = (rssi + 74) / 2 - 74; - - AltosParse.word(words[i++], "STATUS"); - status = AltosParse.parse_hex(words[i++]); - - AltosParse.word(words[i++], "STATE"); - state = Altos.state(words[i++]); - - tick = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "a:"); - accel = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "p:"); - pres = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "t:"); - temp = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "v:"); - batt = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "d:"); - drogue = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "m:"); - main = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "fa:"); - flight_accel = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "ga:"); - ground_accel = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "fv:"); - flight_vel = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "fp:"); - flight_pres = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "gp:"); - ground_pres = AltosParse.parse_int(words[i++]); - - if (version >= 1) { - AltosParse.word(words[i++], "a+:"); - accel_plus_g = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "a-:"); - accel_minus_g = AltosParse.parse_int(words[i++]); - } else { - accel_plus_g = ground_accel; - accel_minus_g = ground_accel + 530; - } - - gps = new AltosGPS(words, i, version); - } -} diff --git a/ao-tools/altosui/AltosTelemetryIterable.java b/ao-tools/altosui/AltosTelemetryIterable.java deleted file mode 100644 index a71ab872..00000000 --- a/ao-tools/altosui/AltosTelemetryIterable.java +++ /dev/null @@ -1,82 +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.io.*; -import java.util.*; -import java.text.*; - -public class AltosTelemetryIterable extends AltosRecordIterable { - LinkedList records; - - public Iterator iterator () { - return records.iterator(); - } - - public AltosTelemetryIterable (FileInputStream input) { - boolean saw_boost = false; - int current_tick = 0; - int boost_tick = 0; - - records = new LinkedList (); - - try { - for (;;) { - String line = AltosRecord.gets(input); - if (line == null) { - break; - } - try { - AltosTelemetry record = new AltosTelemetry(line); - if (record == null) - break; - if (records.isEmpty()) { - current_tick = record.tick; - } else { - int tick = record.tick | (current_tick & ~ 0xffff); - if (tick < current_tick - 0x1000) - tick += 0x10000; - current_tick = tick; - record.tick = current_tick; - } - if (!saw_boost && record.state >= Altos.ao_flight_boost) - { - saw_boost = true; - boost_tick = record.tick; - } - records.add(record); - } catch (ParseException pe) { - System.out.printf("parse exception %s\n", pe.getMessage()); - } catch (AltosCRCException ce) { - System.out.printf("crc error\n"); - } - } - } catch (IOException io) { - System.out.printf("io exception\n"); - } - - /* adjust all tick counts to be relative to boost time */ - for (AltosRecord r : this) - r.time = (r.tick - boost_tick) / 100.0; - - try { - input.close(); - } catch (IOException ie) { - } - } -} diff --git a/ao-tools/altosui/AltosTelemetryReader.java b/ao-tools/altosui/AltosTelemetryReader.java deleted file mode 100644 index 6c5a9397..00000000 --- a/ao-tools/altosui/AltosTelemetryReader.java +++ /dev/null @@ -1,61 +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.lang.*; -import java.text.*; -import java.io.*; -import java.util.concurrent.*; - -class AltosTelemetryReader extends AltosFlightReader { - AltosDevice device; - AltosSerial serial; - AltosLog log; - - LinkedBlockingQueue telem; - - AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { - AltosLine l = telem.take(); - if (l.line == null) - throw new IOException("IO error"); - return new AltosTelemetry(l.line); - } - - void close(boolean interrupted) { - serial.remove_monitor(telem); - log.close(); - serial.close(); - } - - void set_channel(int channel) { - serial.set_channel(channel); - AltosPreferences.set_channel(device.getSerial(), channel); - } - - public AltosTelemetryReader (AltosDevice in_device) - throws FileNotFoundException, AltosSerialInUseException, IOException { - device = in_device; - serial = new AltosSerial(device); - log = new AltosLog(serial); - name = device.toShortString(); - - telem = new LinkedBlockingQueue(); - serial.set_radio(); - serial.add_monitor(telem); - } -} diff --git a/ao-tools/altosui/AltosUI.app/Contents/Info.plist b/ao-tools/altosui/AltosUI.app/Contents/Info.plist deleted file mode 100644 index 97b1b59c..00000000 --- a/ao-tools/altosui/AltosUI.app/Contents/Info.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - CFBundleName - altosui - CFBundleVersion - 100.0 - CFBundleAllowMixedLocalizations - true - CFBundleExecutable - JavaApplicationStub - CFBundleDevelopmentRegion - English - CFBundlePackageType - APPL - CFBundleSignature - ???? - CFBundleGetInfoString - AltOS UI version 0.7 - CFBundleInfoDictionaryVersion - 6.0 - CFBundleIconFile - AltosUIIcon.icns - Java - - MainClass - altosui.AltosUI - JVMVersion - 1.5+ - ClassPath - - $JAVAROOT/altosui.jar - $JAVAROOT/freetts.jar - - - - diff --git a/ao-tools/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub b/ao-tools/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub deleted file mode 100755 index c661d3e1..00000000 Binary files a/ao-tools/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub and /dev/null differ diff --git a/ao-tools/altosui/AltosUI.app/Contents/PkgInfo b/ao-tools/altosui/AltosUI.app/Contents/PkgInfo deleted file mode 100644 index 8a43480f..00000000 --- a/ao-tools/altosui/AltosUI.app/Contents/PkgInfo +++ /dev/null @@ -1 +0,0 @@ -APPLAM.O diff --git a/ao-tools/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns b/ao-tools/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns deleted file mode 100644 index fe49f362..00000000 Binary files a/ao-tools/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns and /dev/null differ diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java deleted file mode 100644 index 94c4dd2a..00000000 --- a/ao-tools/altosui/AltosUI.java +++ /dev/null @@ -1,405 +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.*; -import java.util.concurrent.LinkedBlockingQueue; - -import libaltosJNI.*; - -public class AltosUI extends JFrame { - public AltosVoice voice = new AltosVoice(); - - public static boolean load_library(Frame frame) { - if (!AltosDevice.load_library()) { - JOptionPane.showMessageDialog(frame, - String.format("No AltOS library in \"%s\"", - System.getProperty("java.library.path","")), - "Cannot load device access library", - JOptionPane.ERROR_MESSAGE); - return false; - } - return true; - } - - void telemetry_window(AltosDevice device) { - try { - AltosFlightReader reader = new AltosTelemetryReader(device); - if (reader != null) - new AltosFlightUI(voice, reader, device.getSerial()); - } catch (FileNotFoundException ee) { - JOptionPane.showMessageDialog(AltosUI.this, - String.format("Cannot open device \"%s\"", - device.toShortString()), - "Cannot open target device", - JOptionPane.ERROR_MESSAGE); - } catch (AltosSerialInUseException si) { - JOptionPane.showMessageDialog(AltosUI.this, - String.format("Device \"%s\" already in use", - device.toShortString()), - "Device in use", - JOptionPane.ERROR_MESSAGE); - } catch (IOException ee) { - JOptionPane.showMessageDialog(AltosUI.this, - device.toShortString(), - "Unkonwn I/O error", - JOptionPane.ERROR_MESSAGE); - } - } - - Container pane; - GridBagLayout gridbag; - - JButton addButton(int x, int y, String label) { - GridBagConstraints c; - JButton b; - - c = new GridBagConstraints(); - c.gridx = x; c.gridy = y; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - c.weighty = 1; - b = new JButton(label); - - Dimension ps = b.getPreferredSize(); - - gridbag.setConstraints(b, c); - add(b, c); - return b; - } - - public AltosUI() { - - load_library(null); - - java.net.URL imgURL = AltosUI.class.getResource("/altus-metrum-16x16.jpg"); - if (imgURL != null) - setIconImage(new ImageIcon(imgURL).getImage()); - - AltosPreferences.init(this); - - pane = getContentPane(); - gridbag = new GridBagLayout(); - pane.setLayout(gridbag); - - JButton b; - - b = addButton(0, 0, "Monitor Flight"); - b.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ConnectToDevice(); - } - }); - b = addButton(1, 0, "Save Flight Data"); - b.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - SaveFlightData(); - } - }); - b = addButton(2, 0, "Replay Flight"); - b.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Replay(); - } - }); - b = addButton(3, 0, "Graph Data"); - b.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - GraphData(); - } - }); - b = addButton(4, 0, "Export Data"); - b.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ExportData(); - } - }); - b = addButton(0, 1, "Configure TeleMetrum"); - b.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ConfigureTeleMetrum(); - } - }); - - b = addButton(1, 1, "Configure AltosUI"); - b.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ConfigureAltosUI(); - } - }); - - b = addButton(2, 1, "Flash Image"); - b.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - FlashImage(); - } - }); - - 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); - } - }); - - setTitle("AltOS"); - - pane.doLayout(); - pane.validate(); - - doLayout(); - validate(); - - setVisible(true); - - Insets i = getInsets(); - Dimension ps = rootPane.getPreferredSize(); - ps.width += i.left + i.right; - ps.height += i.top + i.bottom; - setPreferredSize(ps); - setSize(ps); - setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - System.exit(0); - } - }); - } - - private void ConnectToDevice() { - AltosDevice device = AltosDeviceDialog.show(AltosUI.this, - AltosDevice.product_basestation); - - if (device != null) - telemetry_window(device); - } - - void ConfigureCallsign() { - String result; - result = JOptionPane.showInputDialog(AltosUI.this, - "Configure Callsign", - AltosPreferences.callsign()); - if (result != null) - AltosPreferences.set_callsign(result); - } - - void ConfigureTeleMetrum() { - new AltosConfig(AltosUI.this); - } - - void FlashImage() { - new AltosFlashUI(AltosUI.this); - } - - void FireIgniter() { - new AltosIgniteUI(AltosUI.this); - } - - /* - * Replay a flight from telemetry data - */ - private void Replay() { - AltosDataChooser chooser = new AltosDataChooser( - AltosUI.this); - - AltosRecordIterable iterable = chooser.runDialog(); - if (iterable != null) { - AltosFlightReader reader = new AltosReplayReader(iterable.iterator(), - chooser.filename()); - new AltosFlightUI(voice, reader); - } - } - - /* Connect to TeleMetrum, either directly or through - * a TeleDongle over the packet link - */ - private void SaveFlightData() { - new AltosEepromDownload(AltosUI.this); - } - - /* Load a flight log file and write out a CSV file containing - * all of the data in standard units - */ - - private void ExportData() { - 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() { - AltosDataChooser chooser; - chooser = new AltosDataChooser(this); - AltosRecordIterable record_reader = chooser.runDialog(); - if (record_reader == null) - return; - new AltosGraphUI(record_reader); - } - - private void ConfigureAltosUI() { - new AltosConfigureUI(AltosUI.this, voice); - } - - static AltosRecordIterable open_logfile(String filename) { - File file = new File (filename); - try { - FileInputStream in; - - in = new FileInputStream(file); - if (filename.endsWith("eeprom")) - return new AltosEepromIterable(in); - else - return new AltosTelemetryIterable(in); - } catch (FileNotFoundException fe) { - System.out.printf("Cannot open '%s'\n", filename); - return null; - } - } - - static AltosWriter open_csv(String filename) { - File file = new File (filename); - try { - return new AltosCSV(file); - } catch (FileNotFoundException fe) { - System.out.printf("Cannot open '%s'\n", filename); - return null; - } - } - - static AltosWriter open_kml(String filename) { - File file = new File (filename); - try { - return new AltosKML(file); - } catch (FileNotFoundException fe) { - System.out.printf("Cannot open '%s'\n", filename); - return null; - } - } - - static final int process_csv = 1; - static final int process_kml = 2; - - static void process_file(String input, int process) { - AltosRecordIterable iterable = open_logfile(input); - if (iterable == null) - return; - if (process == 0) - process = process_csv; - if ((process & process_csv) != 0) { - String output = Altos.replace_extension(input,".csv"); - System.out.printf("Processing \"%s\" to \"%s\"\n", input, output); - if (input.equals(output)) { - System.out.printf("Not processing '%s'\n", input); - } else { - AltosWriter writer = open_csv(output); - if (writer != null) { - writer.write(iterable); - writer.close(); - } - } - } - if ((process & process_kml) != 0) { - String output = Altos.replace_extension(input,".kml"); - System.out.printf("Processing \"%s\" to \"%s\"\n", input, output); - if (input.equals(output)) { - System.out.printf("Not processing '%s'\n", input); - } else { - AltosWriter writer = open_kml(output); - if (writer == null) - return; - writer.write(iterable); - writer.close(); - } - } - } - - public static void main(final String[] args) { - int process = 0; - /* Handle batch-mode */ - if (args.length == 1 && args[0].equals("--help")) { - System.out.printf("Usage: altosui [OPTION]... [FILE]...\n"); - System.out.printf(" Options:\n"); - System.out.printf(" --fetchmaps \tpre-fetch maps for site map view\n"); - System.out.printf(" --replay \t\trelive the glory of past flights \n"); - System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n"); - System.out.printf(" --kml\tgenerate KML output for use with Google Earth\n"); - } else if (args.length == 3 && args[0].equals("--fetchmaps")) { - double lat = Double.parseDouble(args[1]); - double lon = Double.parseDouble(args[2]); - AltosSiteMap.prefetchMaps(lat, lon, 5, 5); - } else if (args.length == 2 && args[0].equals("--replay")) { - String filename = args[1]; - FileInputStream in; - try { - in = new FileInputStream(filename); - } catch (Exception e) { - System.out.printf("Failed to open file '%s'\n", filename); - return; - } - AltosRecordIterable recs; - AltosReplayReader reader; - if (filename.endsWith("eeprom")) { - recs = new AltosEepromIterable(in); - } else { - recs = new AltosTelemetryIterable(in); - } - reader = new AltosReplayReader(recs.iterator(), filename); - AltosFlightUI flight_ui = new AltosFlightUI(new AltosVoice(), reader); - flight_ui.set_exit_on_close(); - return; - } else if (args.length > 0) { - for (int i = 0; i < args.length; i++) { - if (args[i].equals("--kml")) - process |= process_kml; - else if (args[i].equals("--csv")) - process |= process_csv; - else - process_file(args[i], process); - } - } else { - AltosUI altosui = new AltosUI(); - altosui.setVisible(true); - - AltosDevice[] devices = AltosDevice.list(AltosDevice.product_basestation); - for (int i = 0; i < devices.length; i++) - altosui.telemetry_window(devices[i]); - } - } -} diff --git a/ao-tools/altosui/AltosVoice.java b/ao-tools/altosui/AltosVoice.java deleted file mode 100644 index ac13ee14..00000000 --- a/ao-tools/altosui/AltosVoice.java +++ /dev/null @@ -1,95 +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 com.sun.speech.freetts.Voice; -import com.sun.speech.freetts.VoiceManager; -import com.sun.speech.freetts.audio.JavaClipAudioPlayer; -import java.util.concurrent.LinkedBlockingQueue; - -public class AltosVoice implements Runnable { - VoiceManager voice_manager; - Voice voice; - LinkedBlockingQueue phrases; - Thread thread; - boolean busy; - - final static String voice_name = "kevin16"; - - public void run() { - try { - for (;;) { - String s = phrases.take(); - voice.speak(s); - synchronized(this) { - if (phrases.isEmpty()) { - busy = false; - notifyAll(); - } - } - } - } catch (InterruptedException e) { - } - } - - public synchronized void drain() throws InterruptedException { - while (busy) - wait(); - } - - public void speak_always(String s) { - try { - if (voice != null) { - synchronized(this) { - busy = true; - phrases.put(s); - } - } - } catch (InterruptedException e) { - } - } - - public void speak(String s) { - if (AltosPreferences.voice()) - speak_always(s); - } - - public void speak(String format, Object... parameters) { - speak(String.format(format, parameters)); - } - - public AltosVoice () { - busy = false; - voice_manager = VoiceManager.getInstance(); - voice = voice_manager.getVoice(voice_name); - if (voice != null) { - voice.allocate(); - phrases = new LinkedBlockingQueue (); - thread = new Thread(this); - thread.start(); - } else { - System.out.printf("Voice manager failed to open %s\n", voice_name); - Voice[] voices = voice_manager.getVoices(); - System.out.printf("Available voices:\n"); - for (int i = 0; i < voices.length; i++) { - System.out.println(" " + voices[i].getName() - + " (" + voices[i].getDomain() + " domain)"); - } - } - } -} diff --git a/ao-tools/altosui/AltosWriter.java b/ao-tools/altosui/AltosWriter.java deleted file mode 100644 index a172dff0..00000000 --- a/ao-tools/altosui/AltosWriter.java +++ /dev/null @@ -1,32 +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.lang.*; -import java.io.*; -import java.text.*; -import java.util.*; - -public interface AltosWriter { - - public void write(AltosRecord record); - - public void write(AltosRecordIterable iterable); - - public void close(); -} diff --git a/ao-tools/altosui/GrabNDrag.java b/ao-tools/altosui/GrabNDrag.java deleted file mode 100644 index e6b87b58..00000000 --- a/ao-tools/altosui/GrabNDrag.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright © 2010 Anthony Towns - * - * 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.image.*; -import java.awt.event.*; -import javax.swing.*; -import javax.swing.event.MouseInputAdapter; -import javax.imageio.ImageIO; -import javax.swing.table.*; -import java.io.*; -import java.util.*; -import java.text.*; - -class GrabNDrag extends MouseInputAdapter { - private JComponent scroll; - private Point startPt = new Point(); - - public GrabNDrag(JComponent scroll) { - this.scroll = scroll; - scroll.addMouseMotionListener(this); - scroll.addMouseListener(this); - scroll.setAutoscrolls(true); - } - - public void mousePressed(MouseEvent e) { - startPt.setLocation(e.getPoint()); - } - public void mouseDragged(MouseEvent e) { - int xd = e.getX() - startPt.x; - int yd = e.getY() - startPt.y; - - Rectangle r = scroll.getVisibleRect(); - r.x -= xd; - r.y -= yd; - scroll.scrollRectToVisible(r); - } -} diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi b/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi deleted file mode 100644 index 3ed821eb..00000000 --- a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi +++ /dev/null @@ -1,84 +0,0 @@ -# -# InstDrv Example, (c) 2003 Jan Kiszka (Jan Kiszka@web.de) -# - -Name "InstDrv.dll test" - -OutFile "InstDrv-Test.exe" - -ShowInstDetails show - -ComponentText "InstDrv Plugin Usage Example" - -Page components -Page instfiles - -Section "Install a Driver" InstDriver - InstDrv::InitDriverSetup /NOUNLOAD "{4d36e978-e325-11ce-bfc1-08002be10318}" "IrCOMM2k" - Pop $0 - DetailPrint "InitDriverSetup: $0" - - InstDrv::DeleteOemInfFiles /NOUNLOAD - Pop $0 - DetailPrint "DeleteOemInfFiles: $0" - StrCmp $0 "00000000" PrintInfNames ContInst1 - - PrintInfNames: - Pop $0 - DetailPrint "Deleted $0" - Pop $0 - DetailPrint "Deleted $0" - - ContInst1: - InstDrv::CreateDevice /NOUNLOAD - Pop $0 - DetailPrint "CreateDevice: $0" - - SetOutPath $TEMP - File "ircomm2k.inf" - File "ircomm2k.sys" - - InstDrv::InstallDriver /NOUNLOAD "$TEMP\ircomm2k.inf" - Pop $0 - DetailPrint "InstallDriver: $0" - StrCmp $0 "00000000" PrintReboot ContInst2 - - PrintReboot: - Pop $0 - DetailPrint "Reboot: $0" - - ContInst2: - InstDrv::CountDevices - Pop $0 - DetailPrint "CountDevices: $0" -SectionEnd - -Section "Uninstall the driver again" UninstDriver - InstDrv::InitDriverSetup /NOUNLOAD "{4d36e978-e325-11ce-bfc1-08002be10318}" "IrCOMM2k" - Pop $0 - DetailPrint "InitDriverSetup: $0" - - InstDrv::DeleteOemInfFiles /NOUNLOAD - Pop $0 - DetailPrint "DeleteOemInfFiles: $0" - StrCmp $0 "00000000" PrintInfNames ContUninst1 - - PrintInfNames: - Pop $0 - DetailPrint "Deleted $0" - Pop $0 - DetailPrint "Deleted $0" - - ContUninst1: - InstDrv::RemoveAllDevices - Pop $0 - DetailPrint "RemoveAllDevices: $0" - StrCmp $0 "00000000" PrintReboot ContUninst2 - - PrintReboot: - Pop $0 - DetailPrint "Reboot: $0" - - ContUninst2: - Delete "$SYSDIR\system32\ircomm2k.sys" -SectionEnd \ No newline at end of file diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe b/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe deleted file mode 100644 index 615bae15..00000000 Binary files a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe and /dev/null differ diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c b/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c deleted file mode 100644 index efe866e9..00000000 --- a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c +++ /dev/null @@ -1,704 +0,0 @@ -/* - -InstDrv.dll - Installs or Removes Device Drivers - -Copyright © 2003 Jan Kiszka (Jan.Kiszka@web.de) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute -it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; - you must not claim that you wrote the original software. - If you use this software in a product, an acknowledgment in the - product documentation would be appreciated but is not required. -2. Altered versions must be plainly marked as such, - and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any distribution. - -*/ - - -#include -#include -#include -#include "../exdll/exdll.h" - - -char paramBuf[1024]; -GUID devClass; -char hwIdBuf[1024]; -int initialized = 0; - - - -void* memset(void* dst, int val, unsigned int len) -{ - while (len-- > 0) - *((char *)dst)++ = val; - - return NULL; -} - - - -void* memcpy(void* dst, const void* src, unsigned int len) -{ - while (len-- > 0) - *((char *)dst)++ = *((char *)src)++; - - return NULL; -} - - - -int HexCharToInt(char c) -{ - if ((c >= '0') && (c <= '9')) - return c - '0'; - else if ((c >= 'a') && (c <= 'f')) - return c - 'a' + 10; - else if ((c >= 'A') && (c <= 'F')) - return c - 'A' + 10; - else - return -1; -} - - - -BOOLEAN HexStringToUInt(char* str, int width, void* valBuf) -{ - int i, val; - - - for (i = width - 4; i >= 0; i -= 4) - { - val = HexCharToInt(*str++); - if (val < 0) - return FALSE; - *(unsigned int *)valBuf += val << i; - } - - return TRUE; -} - - - -BOOLEAN StringToGUID(char* guidStr, GUID* pGuid) -{ - int i; - - - memset(pGuid, 0, sizeof(GUID)); - - if (*guidStr++ != '{') - return FALSE; - - if (!HexStringToUInt(guidStr, 32, &pGuid->Data1)) - return FALSE; - guidStr += 8; - - if (*guidStr++ != '-') - return FALSE; - - if (!HexStringToUInt(guidStr, 16, &pGuid->Data2)) - return FALSE; - guidStr += 4; - - if (*guidStr++ != '-') - return FALSE; - - if (!HexStringToUInt(guidStr, 16, &pGuid->Data3)) - return FALSE; - guidStr += 4; - - if (*guidStr++ != '-') - return FALSE; - - for (i = 0; i < 2; i++) - { - if (!HexStringToUInt(guidStr, 8, &pGuid->Data4[i])) - return FALSE; - guidStr += 2; - } - - if (*guidStr++ != '-') - return FALSE; - - for (i = 2; i < 8; i++) - { - if (!HexStringToUInt(guidStr, 8, &pGuid->Data4[i])) - return FALSE; - guidStr += 2; - } - - if (*guidStr++ != '}') - return FALSE; - - return TRUE; -} - - - -DWORD FindNextDevice(HDEVINFO devInfoSet, SP_DEVINFO_DATA* pDevInfoData, DWORD* pIndex) -{ - DWORD buffersize = 0; - LPTSTR buffer = NULL; - DWORD dataType; - DWORD result; - - - while (1) - { - if (!SetupDiEnumDeviceInfo(devInfoSet, (*pIndex)++, pDevInfoData)) - { - result = GetLastError(); - break; - } - - GetDeviceRegistryProperty: - if (!SetupDiGetDeviceRegistryProperty(devInfoSet, pDevInfoData, SPDRP_HARDWAREID, - &dataType, (PBYTE)buffer, buffersize, - &buffersize)) - { - result = GetLastError(); - - if (result == ERROR_INSUFFICIENT_BUFFER) - { - if (buffer != NULL) - LocalFree(buffer); - - buffer = (LPTSTR)LocalAlloc(LPTR, buffersize); - - if (buffer == NULL) - break; - - goto GetDeviceRegistryProperty; - } - else if (result == ERROR_INVALID_DATA) - continue; // ignore invalid entries - else - break; // break on other errors - } - - if (lstrcmpi(buffer, hwIdBuf) == 0) - { - result = 0; - break; - } - } - - if (buffer != NULL) - LocalFree(buffer); - - return result; -} - - - -DWORD FindFirstDevice(HWND hwndParent, const GUID* pDevClass, const LPTSTR hwId, - HDEVINFO* pDevInfoSet, SP_DEVINFO_DATA* pDevInfoData, - DWORD *pIndex, DWORD flags) -{ - DWORD result; - - - *pDevInfoSet = SetupDiGetClassDevs((GUID*)pDevClass, NULL, hwndParent, flags); - if (*pDevInfoSet == INVALID_HANDLE_VALUE) - return GetLastError(); - - pDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA); - *pIndex = 0; - - result = FindNextDevice(*pDevInfoSet, pDevInfoData, pIndex); - - if (result != 0) - SetupDiDestroyDeviceInfoList(*pDevInfoSet); - - return result; -} - - - -/* - * InstDrv::InitDriverSetup devClass drvHWID - * - * devClass - GUID of the driver's device setup class - * drvHWID - Hardware ID of the supported device - * - * Return: - * result - error message, empty on success - */ -void __declspec(dllexport) InitDriverSetup(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) -{ - EXDLL_INIT(); - - /* convert class GUID */ - popstring(paramBuf); - - if (!StringToGUID(paramBuf, &devClass)) - { - popstring(paramBuf); - pushstring("Invalid GUID!"); - return; - } - - /* get hardware ID */ - memset(hwIdBuf, 0, sizeof(hwIdBuf)); - popstring(hwIdBuf); - - initialized = 1; - pushstring(""); -} - - - -/* - * InstDrv::CountDevices - * - * Return: - * result - Number of installed devices the driver supports - */ -void __declspec(dllexport) CountDevices(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) -{ - HDEVINFO devInfoSet; - SP_DEVINFO_DATA devInfoData; - int count = 0; - char countBuf[16]; - DWORD index; - DWORD result; - - - EXDLL_INIT(); - - if (!initialized) - { - pushstring("Fatal error!"); - return; - } - - result = FindFirstDevice(hwndParent, &devClass, hwIdBuf, &devInfoSet, &devInfoData, - &index, DIGCF_PRESENT); - if (result != 0) - { - pushstring("0"); - return; - } - - do - { - count++; - } while (FindNextDevice(devInfoSet, &devInfoData, &index) == 0); - - SetupDiDestroyDeviceInfoList(devInfoSet); - - wsprintf(countBuf, "%d", count); - pushstring(countBuf); -} - - - -/* - * InstDrv::CreateDevice - * - * Return: - * result - Windows error code - */ -void __declspec(dllexport) CreateDevice(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) -{ - HDEVINFO devInfoSet; - SP_DEVINFO_DATA devInfoData; - DWORD result = 0; - char resultBuf[16]; - - - EXDLL_INIT(); - - if (!initialized) - { - pushstring("Fatal error!"); - return; - } - - devInfoSet = SetupDiCreateDeviceInfoList(&devClass, hwndParent); - if (devInfoSet == INVALID_HANDLE_VALUE) - { - wsprintf(resultBuf, "%08X", GetLastError()); - pushstring(resultBuf); - return; - } - - devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); - if (!SetupDiCreateDeviceInfo(devInfoSet, hwIdBuf, &devClass, NULL, - hwndParent, DICD_GENERATE_ID, &devInfoData)) - { - result = GetLastError(); - goto InstallCleanup; - } - - if (!SetupDiSetDeviceRegistryProperty(devInfoSet, &devInfoData, SPDRP_HARDWAREID, - hwIdBuf, (lstrlen(hwIdBuf)+2)*sizeof(TCHAR))) - { - result = GetLastError(); - goto InstallCleanup; - } - - if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, devInfoSet, &devInfoData)) - result = GetLastError(); - - InstallCleanup: - SetupDiDestroyDeviceInfoList(devInfoSet); - - wsprintf(resultBuf, "%08X", result); - pushstring(resultBuf); -} - - - -/* - * InstDrv::InstallDriver infPath - * - * Return: - * result - Windows error code - * reboot - non-zero if reboot is required - */ -void __declspec(dllexport) InstallDriver(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) -{ - char resultBuf[16]; - BOOL reboot; - - - EXDLL_INIT(); - popstring(paramBuf); - - if (!initialized) - { - pushstring("Fatal error!"); - return; - } - - if (!UpdateDriverForPlugAndPlayDevices(hwndParent, hwIdBuf, paramBuf, - INSTALLFLAG_FORCE, &reboot)) - { - wsprintf(resultBuf, "%08X", GetLastError()); - pushstring(resultBuf); - } - else - { - wsprintf(resultBuf, "%d", reboot); - pushstring(resultBuf); - pushstring("00000000"); - } -} - - - -/* - * InstDrv::DeleteOemInfFiles - * - * Return: - * result - Windows error code - * oeminf - Path of the deleted devices setup file (oemXX.inf) - * oempnf - Path of the deleted devices setup file (oemXX.pnf) - */ -void __declspec(dllexport) DeleteOemInfFiles(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) -{ - HDEVINFO devInfo; - SP_DEVINFO_DATA devInfoData; - SP_DRVINFO_DATA drvInfoData; - SP_DRVINFO_DETAIL_DATA drvInfoDetail; - DWORD index; - DWORD result; - char resultBuf[16]; - - - if (!initialized) - { - pushstring("Fatal error!"); - return; - } - - result = FindFirstDevice(NULL, &devClass, hwIdBuf, &devInfo, &devInfoData, &index, 0); - if (result != 0) - goto Cleanup1; - - if (!SetupDiBuildDriverInfoList(devInfo, &devInfoData, SPDIT_COMPATDRIVER)) - { - result = GetLastError(); - goto Cleanup2; - } - - drvInfoData.cbSize = sizeof(SP_DRVINFO_DATA); - drvInfoDetail.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA); - - if (!SetupDiEnumDriverInfo(devInfo, &devInfoData, SPDIT_COMPATDRIVER, 0, &drvInfoData)) - { - result = GetLastError(); - goto Cleanup3; - } - - if (!SetupDiGetDriverInfoDetail(devInfo, &devInfoData, &drvInfoData, - &drvInfoDetail, sizeof(drvInfoDetail), NULL)) - { - result = GetLastError(); - - if (result != ERROR_INSUFFICIENT_BUFFER) - goto Cleanup3; - - result = 0; - } - - pushstring(drvInfoDetail.InfFileName); - if (!DeleteFile(drvInfoDetail.InfFileName)) - result = GetLastError(); - else - { - index = lstrlen(drvInfoDetail.InfFileName); - if (index > 3) - { - lstrcpy(drvInfoDetail.InfFileName+index-3, "pnf"); - pushstring(drvInfoDetail.InfFileName); - if (!DeleteFile(drvInfoDetail.InfFileName)) - result = GetLastError(); - } - } - - Cleanup3: - SetupDiDestroyDriverInfoList(devInfo, &devInfoData, SPDIT_COMPATDRIVER); - - Cleanup2: - SetupDiDestroyDeviceInfoList(devInfo); - - Cleanup1: - wsprintf(resultBuf, "%08X", result); - pushstring(resultBuf); -} - - - -/* - * InstDrv::RemoveAllDevices - * - * Return: - * result - Windows error code - * reboot - non-zero if reboot is required - */ -void __declspec(dllexport) RemoveAllDevices(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) -{ - HDEVINFO devInfo; - SP_DEVINFO_DATA devInfoData; - DWORD index; - DWORD result; - char resultBuf[16]; - BOOL reboot = FALSE; - SP_DEVINSTALL_PARAMS instParams; - - - EXDLL_INIT(); - - if (!initialized) - { - pushstring("Fatal error!"); - return; - } - - result = FindFirstDevice(NULL, &devClass, hwIdBuf, &devInfo, &devInfoData, &index, 0); - if (result != 0) - goto Cleanup1; - - do - { - if (!SetupDiCallClassInstaller(DIF_REMOVE, devInfo, &devInfoData)) - { - result = GetLastError(); - break; - } - - instParams.cbSize = sizeof(instParams); - if (!reboot && - SetupDiGetDeviceInstallParams(devInfo, &devInfoData, &instParams) && - ((instParams.Flags & (DI_NEEDRESTART|DI_NEEDREBOOT)) != 0)) - { - reboot = TRUE; - } - - result = FindNextDevice(devInfo, &devInfoData, &index); - } while (result == 0); - - SetupDiDestroyDeviceInfoList(devInfo); - - Cleanup1: - if ((result == 0) || (result == ERROR_NO_MORE_ITEMS)) - { - wsprintf(resultBuf, "%d", reboot); - pushstring(resultBuf); - pushstring("00000000"); - } - else - { - wsprintf(resultBuf, "%08X", result); - pushstring(resultBuf); - } -} - - - -/* - * InstDrv::StartSystemService serviceName - * - * Return: - * result - Windows error code - */ -void __declspec(dllexport) StartSystemService(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) -{ - SC_HANDLE managerHndl; - SC_HANDLE svcHndl; - SERVICE_STATUS svcStatus; - DWORD oldCheckPoint; - DWORD result; - char resultBuf[16]; - - - EXDLL_INIT(); - popstring(paramBuf); - - managerHndl = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); - if (managerHndl == NULL) - { - result = GetLastError(); - goto Cleanup1; - } - - svcHndl = OpenService(managerHndl, paramBuf, SERVICE_START | SERVICE_QUERY_STATUS); - if (svcHndl == NULL) - { - result = GetLastError(); - goto Cleanup2; - } - - if (!StartService(svcHndl, 0, NULL) || !QueryServiceStatus(svcHndl, &svcStatus)) - { - result = GetLastError(); - goto Cleanup3; - } - - while (svcStatus.dwCurrentState == SERVICE_START_PENDING) - { - oldCheckPoint = svcStatus.dwCheckPoint; - - Sleep(svcStatus.dwWaitHint); - - if (!QueryServiceStatus(svcHndl, &svcStatus)) - { - result = GetLastError(); - break; - } - - if (oldCheckPoint >= svcStatus.dwCheckPoint) - { - if ((svcStatus.dwCurrentState == SERVICE_STOPPED) && - (svcStatus.dwWin32ExitCode != 0)) - result = svcStatus.dwWin32ExitCode; - else - result = ERROR_SERVICE_REQUEST_TIMEOUT; - } - } - - if (svcStatus.dwCurrentState == SERVICE_RUNNING) - result = 0; - - Cleanup3: - CloseServiceHandle(svcHndl); - - Cleanup2: - CloseServiceHandle(managerHndl); - - Cleanup1: - wsprintf(resultBuf, "%08X", result); - pushstring(resultBuf); -} - - - -/* - * InstDrv::StopSystemService serviceName - * - * Return: - * result - Windows error code - */ -void __declspec(dllexport) StopSystemService(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) -{ - SC_HANDLE managerHndl; - SC_HANDLE svcHndl; - SERVICE_STATUS svcStatus; - DWORD oldCheckPoint; - DWORD result; - char resultBuf[16]; - - - EXDLL_INIT(); - popstring(paramBuf); - - managerHndl = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); - if (managerHndl == NULL) - { - result = GetLastError(); - goto Cleanup1; - } - - svcHndl = OpenService(managerHndl, paramBuf, SERVICE_STOP | SERVICE_QUERY_STATUS); - if (svcHndl == NULL) - { - result = GetLastError(); - goto Cleanup2; - } - - if (!ControlService(svcHndl, SERVICE_CONTROL_STOP, &svcStatus)) - { - result = GetLastError(); - goto Cleanup3; - } - - while (svcStatus.dwCurrentState == SERVICE_STOP_PENDING) - { - oldCheckPoint = svcStatus.dwCheckPoint; - - Sleep(svcStatus.dwWaitHint); - - if (!QueryServiceStatus(svcHndl, &svcStatus)) - { - result = GetLastError(); - break; - } - - if (oldCheckPoint >= svcStatus.dwCheckPoint) - { - result = ERROR_SERVICE_REQUEST_TIMEOUT; - break; - } - } - - if (svcStatus.dwCurrentState == SERVICE_STOPPED) - result = 0; - - Cleanup3: - CloseServiceHandle(svcHndl); - - Cleanup2: - CloseServiceHandle(managerHndl); - - Cleanup1: - wsprintf(resultBuf, "%08X", result); - pushstring(resultBuf); -} - - - -BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) -{ - return TRUE; -} diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp b/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp deleted file mode 100644 index 874e66c7..00000000 --- a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp +++ /dev/null @@ -1,110 +0,0 @@ -# Microsoft Developer Studio Project File - Name="InstDrv" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** NICHT BEARBEITEN ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=InstDrv - Win32 Debug -!MESSAGE Dies ist kein gültiges Makefile. Zum Erstellen dieses Projekts mit NMAKE -!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und führen Sie den Befehl -!MESSAGE -!MESSAGE NMAKE /f "InstDrv.mak". -!MESSAGE -!MESSAGE Sie können beim Ausführen von NMAKE eine Konfiguration angeben -!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel: -!MESSAGE -!MESSAGE NMAKE /f "InstDrv.mak" CFG="InstDrv - Win32 Debug" -!MESSAGE -!MESSAGE Für die Konfiguration stehen zur Auswahl: -!MESSAGE -!MESSAGE "InstDrv - Win32 Release" (basierend auf "Win32 (x86) Dynamic-Link Library") -!MESSAGE "InstDrv - Win32 Debug" (basierend auf "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "InstDrv - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 1 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INSTDRV_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O1 /I "C:\Programme\WINDDK\3790\inc\ddk\w2k" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INSTDRV_EXPORTS" /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x407 /d "NDEBUG" -# ADD RSC /l 0x407 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib setupapi.lib newdev.lib /nologo /entry:"_DllMainCRTStartup" /dll /machine:I386 /nodefaultlib /out:"../../Plugins/InstDrv.dll" /libpath:"C:\Programme\WINDDK\3790\lib\w2k\i386" /opt:nowin98 -# SUBTRACT LINK32 /pdb:none - -!ELSEIF "$(CFG)" == "InstDrv - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INSTDRV_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INSTDRV_EXPORTS" /YX /FD /GZ /c -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x407 /d "_DEBUG" -# ADD RSC /l 0x407 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"_DllMainCRTStartup" /dll /debug /machine:I386 /pdbtype:sept -# SUBTRACT LINK32 /nodefaultlib - -!ENDIF - -# Begin Target - -# Name "InstDrv - Win32 Release" -# Name "InstDrv - Win32 Debug" -# Begin Group "Quellcodedateien" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\InstDrv.c -# End Source File -# End Group -# Begin Group "Header-Dateien" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# Begin Group "Ressourcendateien" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw b/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw deleted file mode 100644 index b3d02f0e..00000000 --- a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw +++ /dev/null @@ -1,29 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELÖSCHT WERDEN! - -############################################################################### - -Project: "InstDrv"=.\InstDrv.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt b/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt deleted file mode 100644 index e5877aa6..00000000 --- a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt +++ /dev/null @@ -1,141 +0,0 @@ -InstDrv.dll version 0.2 - Installs or Removes Device Drivers ------------------------------------------------------------- - - -The plugin helps you to create NSIS scripts for installing device drivers or -removing them again. It can count installed device instances, create new ones -or delete all supported device. InstDrv works on Windows 2000 or later. - - - -InstDrv::InitDriverSetup devClass drvHWID -Return: result - -To start processing a driver, first call this function. devClass is the GUID -of the device class the driver supports, drvHWID is the device hardware ID. If -you don't know what these terms mean, you may want to take a look at the -Windows DDK. This function returns an empty string on success, otherwise an -error message. - -InitDriverSetup has to be called every time after the plugin dll has been -(re-)loaded, or if you want to switch to a different driver. - - - -InstDrv::CountDevices -Return: number - -This call returns the number of installed and supported devices of the driver. - - - -InstDrv::CreateDevice -Return: result - -To create a new deviced node which the driver has to support, use this -function. You may even call it multiple times for more than one instance. The -return value is the Windows error code (in hex). Use CreateDevice before -installing or updating the driver itself. - - - -InstDrv::InstallDriver infPath -Return: result - reboot - -InstallDriver installs or updates a device driver as specified in the .inf -setup script. It returns a Windows error code (in hex) and, on success, a flag -signalling if a system reboot is required. - - - -InstDrv::DeleteOemInfFiles -Return: result - oeminf - oempnf - -DeleteOemInfFiles tries to clean up the Windows inf directory by deleting the -oemXX.inf and oemXX.pnf files associated with the drivers. It returns a -Windows error code (in hex) and, on success, the names of the deleted files. -This functions requires that at least one device instance is still present. -So, call it before you remove the devices itself. You should also call it -before updating a driver. This avoids that the inf directory gets slowly -messed up with useless old setup scripts (which does NOT really accelerate -Windows). The error code which comes up when no device is installed is -"00000103". - - - -InstDrv::RemoveAllDevices -Return: result - reboot - -This functions deletes all devices instances the driver supported. It returns -a Windows error code (in hex) and, on success, a flag signalling if the system -needs to be rebooted. You additionally have to remove the driver binaries from -the system paths. - - - -InstDrv::StartSystemService serviceName -Return: result - -Call this function to start the provided system service. The function blocks -until the service is started or the system reported a timeout. The return value -is the Windows error code (in hex). - - - -InstDrv::StopSystemService serviceName -Return: result - -This function tries to stop the provided system service. It blocks until the -service has been shut down or the system reported a timeout. The return value -is the Windows error code (in hex). - - - -Example.nsi - -The example script installs or removes the virtual COM port driver of IrCOMM2k -(2.0.0-alpha8, see www.ircomm2k.de/english). The driver and its setup script -are only included for demonstration purposes, they do not work without the -rest of IrCOMM2k (but they also do not cause any harm). - - - -Building the Source Code - -To build the plugin from the source code, some include files and libraries -which come with the Windows DDK are required. - - - -History - - 0.2 - fixed bug when calling InitDriverSetup the second time - - added StartSystemService and StopSystemService - - 0.1 - first release - - - -License - -Copyright © 2003 Jan Kiszka (Jan.Kiszka@web.de) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute -it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; - you must not claim that you wrote the original software. - If you use this software in a product, an acknowledgment in the - product documentation would be appreciated but is not required. -2. Altered versions must be plainly marked as such, - and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any distribution. diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf b/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf deleted file mode 100644 index ccda1d87..00000000 --- a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf +++ /dev/null @@ -1,137 +0,0 @@ -; IrCOMM2k.inf -; -; Installation file for the Virtual Infrared-COM-Port -; -; (c) Copyright 2001, 2002 Jan Kiszka -; - -[Version] -Signature="$Windows NT$" -Provider=%JK% -Class=Ports -ClassGUID={4d36e978-e325-11ce-bfc1-08002be10318} -;DriverVer=03/26/2002,1.2.1.0 - -[DestinationDirs] -IrCOMM2k.Copy2Drivers = 12 -IrCOMM2k.Copy2Winnt = 10 -IrCOMM2k.Copy2System32 = 11 -IrCOMM2k.Copy2Help = 18 - - -; -; Driver information -; - -[Manufacturer] -%JK% = JK.Mfg - -[JK.Mfg] -%JK.DeviceDescIrCOMM% = IrCOMM2k_inst,IrCOMM2k - - -; -; General installation section -; - -[IrCOMM2k_inst] -CopyFiles = IrCOMM2k.Copy2Drivers ;,IrCOMM2k.Copy2System32,IrCOMM2k.Copy2Help,IrCOMM2k.Copy2Winnt -;AddReg = IrCOMM2k_inst_AddReg - - -; -; File sections -; - -[IrCOMM2k.Copy2Drivers] -ircomm2k.sys,,,2 - -;[IrCOMM2k.Copy2System32] -;ircomm2k.exe,,,2 -;ircomm2k.dll,,,2 - -;[IrCOMM2k.Copy2Help] -;ircomm2k.hlp,,,2 - -;[IrCOMM2k.Copy2Winnt] -;IrCOMM2k-Setup.exe,Setup.exe,,2 - - -; -; Service Installation -; - -[IrCOMM2k_inst.Services] -AddService = IrCOMM2k,0x00000002,IrCOMM2k_DriverService_Inst,IrCOMM2k_DriverEventLog_Inst -;AddService = IrCOMM2kSvc,,IrCOMM2k_Service_Inst - -[IrCOMM2k_DriverService_Inst] -DisplayName = %IrCOMM2k.DrvName% -ServiceType = 1 ; SERVICE_KERNEL_DRIVER -StartType = 3 ; SERVICE_DEMAND_START -ErrorControl = 0 ; SERVICE_ERROR_IGNORE -ServiceBinary = %12%\ircomm2k.sys - -;[IrCOMM2k_Service_Inst] -;DisplayName = %IrCOMM2k.SvcName% -;Description = %IrCOMM2k.SvcDesc% -;ServiceType = 0x00000120 ; SERVICE_WIN32_SHARE_PROCESS, SERVICE_INTERACTIVE_PROCESS -;StartType = 2 ; SERVICE_AUTO_START -;ErrorControl = 0 ; SERVICE_ERROR_IGNORE -;ServiceBinary = %11%\ircomm2k.exe -;Dependencies = IrCOMM2k -;AddReg = IrCOMM2kSvcAddReg - - -[IrCOMM2k_inst.nt.HW] -AddReg=IrCOMM2kHwAddReg - -[IrCOMM2kHwAddReg] -HKR,,PortSubClass,REG_BINARY,0x00000001 -;HKR,,TimeoutScaling,REG_DWORD,0x00000001 -;HKR,,StatusLines,REG_DWORD,0x00000000 - -;[IrCOMM2k_inst_AddReg] -;HKR,,EnumPropPages32,,"ircomm2k.dll,IrCOMM2kPropPageProvider" -;HKLM,%UNINSTALL_KEY%,DisplayIcon,0x00020000,"%windir%\IrCOMM2k-Setup.exe" -;HKLM,%UNINSTALL_KEY%,DisplayName,,"IrCOMM2k 1.2.1 " -;HKLM,%UNINSTALL_KEY%,DisplayVersion,,"1.2.1" -;HKLM,%UNINSTALL_KEY%,HelpLink,,"http://www.ircomm2k.de" -;HKLM,%UNINSTALL_KEY%,Publisher,,%JK% -;HKLM,%UNINSTALL_KEY%,UninstallString,0x00020000,"%windir%\IrCOMM2k-Setup.exe" - -;[IrCOMM2kSvcAddReg] -;HKR,Parameters,ActiveConnectOnly,REG_DWORD,0x00000000 - - -[IrCOMM2k_DriverEventLog_Inst] -AddReg = IrCOMM2k_DriverEventLog_AddReg - -[IrCOMM2k_DriverEventLog_AddReg] -HKR,,EventMessageFile,REG_EXPAND_SZ,"%SystemRoot%\System32\IoLogMsg.dll;%SystemRoot%\System32\drivers\ircomm2k.sys" -HKR,,TypesSupported,REG_DWORD,7 - - -[Strings] - -; -; Non-Localizable Strings -; - -REG_SZ = 0x00000000 -REG_MULTI_SZ = 0x00010000 -REG_EXPAND_SZ = 0x00020000 -REG_BINARY = 0x00000001 -REG_DWORD = 0x00010001 -SERVICEROOT = "System\CurrentControlSet\Services" -UNINSTALL_KEY = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\IrCOMM2k" - -; -; Localizable Strings -; - -JK = "Jan Kiszka" -JK.DeviceDescIrCOMM = "Virtueller Infrarot-Kommunikationsanschluss" -IrCOMM2k.DrvName = "Virtueller Infrarot-Kommunikationsanschluss" -;IrCOMM2k.SvcName = "Virtueller Infrarot-Kommunikationsanschluß, Dienstprogramm" -;IrCOMM2k.SvcDesc = "Bildet über Infarot einen Kommunikationsanschluß nach." diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys b/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys deleted file mode 100644 index 7882583b..00000000 Binary files a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys and /dev/null differ diff --git a/ao-tools/altosui/Instdrv/NSIS/Plugins/InstDrv.dll b/ao-tools/altosui/Instdrv/NSIS/Plugins/InstDrv.dll deleted file mode 100644 index 482e955e..00000000 Binary files a/ao-tools/altosui/Instdrv/NSIS/Plugins/InstDrv.dll and /dev/null differ diff --git a/ao-tools/altosui/Makefile-standalone b/ao-tools/altosui/Makefile-standalone deleted file mode 100644 index a95a5aa8..00000000 --- a/ao-tools/altosui/Makefile-standalone +++ /dev/null @@ -1,184 +0,0 @@ -.SUFFIXES: .java .class - -CLASSPATH=classes:./*:/usr/share/java/* -CLASSFILES=\ - Altos.class \ - AltosChannelMenu.class \ - AltosConfig.class \ - AltosConfigUI.class \ - AltosConvert.class \ - AltosCRCException.class \ - AltosCSV.class \ - AltosCSVUI.class \ - AltosDebug.class \ - AltosEepromDownload.class \ - AltosEepromMonitor.class \ - AltosEepromReader.class \ - AltosEepromRecord.class \ - AltosFile.class \ - AltosFlash.class \ - AltosFlashUI.class \ - AltosFlightInfoTableModel.class \ - AltosFlightStatusTableModel.class \ - AltosGPS.class \ - AltosGreatCircle.class \ - AltosHexfile.class \ - AltosLine.class \ - AltosInfoTable.class \ - AltosLog.class \ - AltosLogfileChooser.class \ - AltosParse.class \ - AltosPreferences.class \ - AltosReader.class \ - AltosRecord.class \ - AltosSerialMonitor.class \ - AltosSerial.class \ - AltosState.class \ - AltosStatusTable.class \ - AltosTelemetry.class \ - AltosTelemetryReader.class \ - AltosUI.class \ - AltosDevice.class \ - AltosDeviceDialog.class \ - AltosRomconfig.class \ - AltosRomconfigUI.class \ - AltosVoice.class - -JAVA_ICON=../../icon/altus-metrum-16x16.jpg -WINDOWS_ICON=../../icon/altus-metrum.ico - -# where altosui.jar gets installed -ALTOSLIB=/usr/share/java - -# where freetts.jar is to be found -FREETTSLIB=/usr/share/java - -# all of the freetts files -FREETTSJAR= \ - $(FREETTSLIB)/cmudict04.jar \ - $(FREETTSLIB)/cmulex.jar \ - $(FREETTSLIB)/cmu_time_awb.jar \ - $(FREETTSLIB)/cmutimelex.jar \ - $(FREETTSLIB)/cmu_us_kal.jar \ - $(FREETTSLIB)/en_us.jar \ - $(FREETTSLIB)/freetts.jar - -# The current hex files -HEXLIB=../../src -HEXFILES = \ - $(HEXLIB)/telemetrum-v1.0.ihx \ - $(HEXLIB)/teledongle-v0.2.ihx - -JAVAFLAGS=-Xlint:unchecked -Xlint:deprecation - -ALTOSUIJAR = altosui.jar -FATJAR = fat/altosui.jar - -OS:=$(shell uname) - -LINUX_APP=altosui - -DARWIN_ZIP=Altos-Mac.zip - -WINDOWS_EXE=Altos-Windows.exe - -LINUX_TGZ=Altos-Linux.tgz - -all: altosui.jar $(LINUX_APP) -fat: altosui.jar $(LINUX_APP) $(DARWIN_ZIP) $(WINDOWS_EXE) $(LINUX_TGZ) - -$(CLASSFILES): - -.java.class: - javac -encoding UTF8 -classpath "$(CLASSPATH)" $(JAVAFLAGS) $*.java - -altosui.jar: classes/images classes/altosui classes/libaltosJNI $(CLASSFILES) Manifest.txt - cd ./classes && jar cfm ../$@ altosui/Manifest.txt images/* altosui/*.class libaltosJNI/*.class - -Manifest.txt: Makefile $(CLASSFILES) - echo 'Main-Class: altosui.AltosUI' > $@ - echo "Class-Path: $(FREETTSLIB)/freetts.jar" >> $@ - -classes/altosui: - mkdir -p classes - ln -sf .. classes/altosui - -classes/libaltosJNI: - mkdir -p classes - ln -sf ../../libaltos/libaltosJNI classes/libaltosJNI - -classes/images: - mkdir -p classes/images - ln -sf ../../$(JAVA_ICON) classes/images - -altosui: - echo "#!/bin/sh" > $@ - echo "exec java -Djava.library.path=/usr/lib/altos -jar /usr/share/java/altosui.jar" >> $@ - chmod +x ./altosui - -fat/altosui: - echo "#!/bin/sh" > $@ - echo 'ME=`which "$0"`' >> $@ - echo 'DIR=`dirname "$ME"`' >> $@ - echo 'exec java -Djava.library.path="$$DIR" -jar "$$DIR"/altosui.jar' >> $@ - chmod +x $@ - -fat/altosui.jar: $(CLASSFILES) $(JAVA_ICON) fat/classes/Manifest.txt - mkdir -p fat/classes - test -L fat/classes/altosui || ln -sf ../.. fat/classes/altosui - mkdir -p fat/classes/images - cp $(JAVA_ICON) fat/classes/images - test -L fat/classes/libaltosJNI || ln -sf ../../../libaltos/libaltosJNI fat/classes/libaltosJNI - cd ./fat/classes && jar cfm ../../$@ Manifest.txt images/* altosui/*.class libaltosJNI/*.class - -fat/classes/Manifest.txt: $(CLASSFILES) Makefile - mkdir -p fat/classes - echo 'Main-Class: altosui.AltosUI' > $@ - echo "Class-Path: freetts.jar" >> $@ - -install: altosui.jar altosui - install -m 0644 altosui.jar $(DESTDIR)/usr/share/java/altosui.jar - install -m 0644 altosui.1 $(DESTDIR)/usr/share/man/man1/altosui.1 - install altosui $(DESTDIR)/usr/bin/altosui - -clean: - rm -f *.class altosui.jar - rm -f AltosUI.app/Contents/Resources/Java/* - rm -rf classes - rm -rf windows linux - -distclean: clean - rm -f $(DARWIN_ZIP) $(WINDOWS_EXE) $(LINUX_TGZ) - rm -rf darwin fat - -FAT_FILES=$(FATJAR) $(FREETTSJAR) $(HEXFILES) - -LINUX_FILES=$(FAT_FILES) ../libaltos/libaltos.so fat/altosui -$(LINUX_TGZ): $(LINUX_FILES) - rm -f $@ - mkdir -p linux/AltOS - rm -f linux/AltOS/* - cp $(LINUX_FILES) linux/AltOS - cd linux && tar czf ../$@ AltOS - -DARWIN_RESOURCES=$(FATJAR) $(FREETTSJAR) ../libaltos/libaltos.dylib -DARWIN_EXTRA=$(HEXFILES) -DARWIN_FILES=$(DARWIN_RESOURCES) $(DARWIN_EXTRA) - -$(DARWIN_ZIP): $(DARWIN_FILES) - rm -f $@ - cp -a AltosUI.app darwin/ - mkdir -p darwin/AltosUI.app/Contents/Resources/Java - cp $(DARWIN_RESOURCES) darwin/AltosUI.app/Contents/Resources/Java - mkdir -p darwin/AltOS - cp $(DARWIN_EXTRA) darwin/AltOS - cd darwin && zip -r ../$@ AltosUI.app AltOS - -WINDOWS_FILES = $(FAT_FILES) ../libaltos/altos.dll ../../telemetrum.inf $(WINDOWS_ICON) - -$(WINDOWS_EXE): $(WINDOWS_FILES) altos-windows.nsi - rm -f $@ - mkdir -p windows/AltOS - rm -f windows/AltOS/* - cp $(WINDOWS_FILES) windows/AltOS - makensis altos-windows.nsi diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am deleted file mode 100644 index 93a43b12..00000000 --- a/ao-tools/altosui/Makefile.am +++ /dev/null @@ -1,271 +0,0 @@ -JAVAROOT=classes -AM_JAVACFLAGS=-encoding UTF-8 - -man_MANS=altosui.1 - -altoslibdir=$(libdir)/altos - -CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:../libaltos:$(FREETTS)/*:/usr/share/java/*" - -bin_SCRIPTS=altosui - -altosui_JAVA = \ - GrabNDrag.java \ - AltosAscent.java \ - AltosChannelMenu.java \ - AltosConfig.java \ - AltosConfigUI.java \ - AltosConfigureUI.java \ - AltosConvert.java \ - AltosCRCException.java \ - AltosCSV.java \ - AltosCSVUI.java \ - AltosDebug.java \ - AltosDescent.java \ - AltosDeviceDialog.java \ - AltosDevice.java \ - AltosDisplayThread.java \ - AltosEepromDownload.java \ - AltosEepromMonitor.java \ - AltosEepromIterable.java \ - AltosEepromRecord.java \ - AltosFile.java \ - AltosFlash.java \ - AltosFlashUI.java \ - AltosFlightDisplay.java \ - AltosFlightInfoTableModel.java \ - AltosFlightReader.java \ - AltosFlightStatus.java \ - AltosFlightUI.java \ - AltosGPS.java \ - AltosGreatCircle.java \ - AltosHexfile.java \ - Altos.java \ - AltosIgnite.java \ - AltosIgniteUI.java \ - AltosInfoTable.java \ - AltosKML.java \ - AltosLanded.java \ - AltosLed.java \ - AltosLights.java \ - AltosLine.java \ - AltosLog.java \ - AltosPad.java \ - AltosParse.java \ - AltosPreferences.java \ - AltosReader.java \ - AltosRecord.java \ - AltosRecordIterable.java \ - AltosTelemetryReader.java \ - AltosReplayReader.java \ - AltosRomconfig.java \ - AltosRomconfigUI.java \ - AltosSerial.java \ - AltosSerialInUseException.java \ - AltosSerialMonitor.java \ - AltosSiteMap.java \ - AltosSiteMapCache.java \ - AltosSiteMapTile.java \ - AltosState.java \ - AltosTelemetry.java \ - AltosTelemetryIterable.java \ - AltosUI.java \ - AltosWriter.java \ - AltosDataPointReader.java \ - AltosDataPoint.java \ - AltosGraph.java \ - AltosGraphTime.java \ - AltosGraphUI.java \ - AltosDataChooser.java \ - AltosVoice.java - -JFREECHART_CLASS= \ - jfreechart.jar - -JCOMMON_CLASS=\ - jcommon.jar - -FREETTS_CLASS= \ - cmudict04.jar \ - cmulex.jar \ - cmu_time_awb.jar \ - cmutimelex.jar \ - cmu_us_kal.jar \ - en_us.jar \ - freetts.jar - -LIBALTOS= \ - libaltos.so \ - libaltos.dylib \ - altos.dll - -JAR=altosui.jar - -FATJAR=altosui-fat.jar - -# Icons -ICONDIR=$(top_srcdir)/icon - -JAVA_ICON=$(ICONDIR)/altus-metrum-16x16.jpg - -ICONS= $(ICONDIR)/redled.png $(ICONDIR)/redoff.png \ - $(ICONDIR)/greenled.png $(ICONDIR)/greenoff.png \ - $(ICONDIR)/grayled.png $(ICONDIR)/grayoff.png - -# icon base names for jar -ICONJAR= -C $(ICONDIR) altus-metrum-16x16.jpg \ - -C $(ICONDIR) redled.png -C $(ICONDIR) redoff.png \ - -C $(ICONDIR) greenled.png -C $(ICONDIR) greenoff.png \ - -C $(ICONDIR) grayon.png -C $(ICONDIR) grayled.png - -WINDOWS_ICON=$(ICONDIR)/altus-metrum.ico - -# Firmware -FIRMWARE_TD=$(top_srcdir)/src/teledongle-v0.2-$(VERSION).ihx -FIRMWARE_TM=$(top_srcdir)/src/telemetrum-v1.0-$(VERSION).ihx -FIRMWARE=$(FIRMWARE_TM) $(FIRMWARE_TD) - -# Distribution targets -LINUX_DIST=Altos-Linux-$(VERSION).tar.bz2 -MACOSX_DIST=Altos-Mac-$(VERSION).zip -WINDOWS_DIST=Altos-Windows-$(VERSION_DASH).exe - -FAT_FILES=$(FATJAR) $(FREETTS_CLASS) $(JFREECHART_CLASS) $(JCOMMON_CLASS) - -LINUX_FILES=$(FAT_FILES) libaltos.so $(FIRMWARE) -LINUX_EXTRA=altosui-fat - -MACOSX_FILES=$(FAT_FILES) libaltos.dylib -MACOSX_EXTRA=$(FIRMWARE) - -WINDOWS_FILES=$(FAT_FILES) altos.dll $(top_srcdir)/telemetrum.inf $(WINDOWS_ICON) - -all-local: classes/altosui $(JAR) altosui altosui-test altosui-jdb - -clean-local: - -rm -rf classes $(JAR) $(FATJAR) \ - $(LINUX_DIST) $(MACOSX_DIST) windows $(WINDOWS_DIST) $(FREETTS_CLASS) \ - $(JFREECHART_CLASS) $(JCOMMON_CLASS) $(LIBALTOS) Manifest.txt Manifest-fat.txt altos-windows.log \ - altosui altosui-test altosui-jdb macosx linux - -if FATINSTALL - -FATTARGET=$(FATDIR)/$(VERSION) - -LINUX_DIST_TARGET=$(FATTARGET)/$(LINUX_DIST) -MACOSX_DIST_TARGET=$(FATTARGET)/$(MACOSX_DIST) -WINDOWS_DIST_TARGET=$(FATTARGET)/$(WINDOWS_DIST) - -fat: $(LINUX_DIST_TARGET) $(MACOSX_DIST_TARGET) $(WINDOWS_DIST_TARGET) - -$(LINUX_DIST_TARGET): $(LINUX_DIST) - mkdir -p $(FATTARGET) - cp -p $< $@ - -$(MACOSX_DIST_TARGET): $(MACOSX_DIST) - mkdir -p $(FATTARGET) - cp -p $< $@ - -$(WINDOWS_DIST_TARGET): $(WINDOWS_DIST) - mkdir -p $(FATTARGET) - cp -p $< $@ - -else -fat: $(LINUX_DIST) $(MACOSX_DIST) $(WINDOWS_DIST) -endif - - -altosuidir=$(datadir)/java - -install-altosuiJAVA: altosui.jar - @$(NORMAL_INSTALL) - test -z "$(altosuidir)" || $(MKDIR_P) "$(DESTDIR)$(altosuidir)" - echo " $(INSTALL_DATA)" "$<" "'$(DESTDIR)$(altosuidir)/altosui.jar'"; \ - $(INSTALL_DATA) "$<" "$(DESTDIR)$(altosuidir)" - -classes/altosui: - mkdir -p classes/altosui - -$(JAR): classaltosui.stamp Manifest.txt $(JAVA_ICON) - jar cfm $@ Manifest.txt \ - $(ICONJAR) \ - -C classes altosui \ - -C ../libaltos libaltosJNI - -$(FATJAR): classaltosui.stamp Manifest-fat.txt $(FREETTS_CLASS) $(JFREECHART_CLASS) $(JCOMMON_CLASS) $(LIBALTOS) $(JAVA_ICON) - jar cfm $@ Manifest-fat.txt \ - $(ICONJAR) \ - -C classes altosui \ - -C ../libaltos libaltosJNI - -Manifest.txt: Makefile - echo 'Main-Class: altosui.AltosUI' > $@ - echo "Class-Path: $(FREETTS)/freetts.jar $(JFREECHART)/jfreechart.jar $(JCOMMON)/jcommon.jar" >> $@ - -Manifest-fat.txt: - echo 'Main-Class: altosui.AltosUI' > $@ - echo "Class-Path: freetts.jar jfreechart.jar jcommon.jar" >> $@ - -altosui: Makefile - echo "#!/bin/sh" > $@ - echo 'exec java -cp "$(FREETTS)/*:$(JFREECHART)/*:$(JCOMMON)/*" -Djava.library.path="$(altoslibdir)" -jar "$(altosuidir)/altosui.jar" "$$@"' >> $@ - chmod +x $@ - -altosui-test: Makefile - echo "#!/bin/sh" > $@ - echo 'exec java -cp "$(FREETTS)/*:$(JFREECHART)/*:$(JCOMMON)/*" -Djava.library.path="../libaltos/.libs" -jar altosui.jar "$$@"' >> $@ - chmod +x $@ - -altosui-jdb: Makefile - echo "#!/bin/sh" > $@ - echo 'exec jdb -classpath "classes:../libaltos:$(FREETTS)/*:$(JFREECHART)/*:$(JCOMMON)/*" -Djava.library.path="../libaltos/.libs" altosui/AltosUI "$$@"' >> $@ - chmod +x $@ - -libaltos.so: - -rm -f "$@" - $(LN_S) ../libaltos/.libs/"$@" . - -libaltos.dylib: - -rm -f "$@" - $(LN_S) ../libaltos/"$@" . - -altos.dll: - -rm -f "$@" - $(LN_S) ../libaltos/"$@" . - -$(FREETTS_CLASS): - -rm -f "$@" - $(LN_S) "$(FREETTS)"/"$@" . - -$(JFREECHART_CLASS): - -rm -f "$@" - $(LN_S) "$(JFREECHART)"/"$@" . - -$(JCOMMON_CLASS): - -rm -f "$@" - $(LN_S) "$(JCOMMON)"/"$@" . - -$(LINUX_DIST): $(LINUX_FILES) $(LINUX_EXTRA) - -rm -f $@ - -rm -rf linux - mkdir -p linux/AltOS - cp -p $(LINUX_FILES) linux/AltOS - cp -p altosui-fat linux/AltOS/altosui - chmod +x linux/AltOS/altosui - tar cjf $@ -C linux AltOS - -$(MACOSX_DIST): $(MACOSX_FILES) $(MACOSX_EXTRA) - -rm -f $@ - -rm -rf macosx - mkdir macosx - cp -a AltosUI.app macosx/ - mkdir -p macosx/AltOS macosx/AltosUI.app/Contents/Resources/Java - cp -p $(FATJAR) macosx/AltosUI.app/Contents/Resources/Java/altosui.jar - cp -p $(FREETTS_CLASS) libaltos.dylib macosx/AltosUI.app/Contents/Resources/Java - cp -p $(JFREECHART_CLASS) libaltos.dylib macosx/AltosUI.app/Contents/Resources/Java - cp -p $(MACOSX_EXTRA) macosx/AltOS - cd macosx && zip -r ../$@ AltosUI.app AltOS - -$(WINDOWS_DIST): $(WINDOWS_FILES) altos-windows.nsi - -rm -f $@ - makensis -Oaltos-windows.log "-XOutFile $@" "-DVERSION=$(VERSION)" altos-windows.nsi diff --git a/ao-tools/altosui/altos-windows.nsi b/ao-tools/altosui/altos-windows.nsi deleted file mode 100644 index 37777fd6..00000000 --- a/ao-tools/altosui/altos-windows.nsi +++ /dev/null @@ -1,113 +0,0 @@ -!addplugindir Instdrv/NSIS/Plugins - -Name "Altus Metrum Installer" - -; Default install directory -InstallDir "$PROGRAMFILES\AltusMetrum" - -; Tell the installer where to re-install a new version -InstallDirRegKey HKLM "Software\AltusMetrum" "Install_Dir" - -LicenseText "GNU General Public License Version 2" -LicenseData "../../COPYING" - -; Need admin privs for Vista or Win7 -RequestExecutionLevel admin - -ShowInstDetails Show - -ComponentText "Altus Metrum Software and Driver Installer" - -; Pages to present - -Page license -Page components -Page directory -Page instfiles - -UninstPage uninstConfirm -UninstPage instfiles - -; And the stuff to install - -Section "Install Driver" InstDriver - InstDrv::InitDriverSetup /NOUNLOAD {4D36E96D-E325-11CE-BFC1-08002BE10318} "Altus Metrum" - Pop $0 - DetailPrint "InitDriverSetup: $0" - - InstDrv::DeleteOemInfFiles /NOUNLOAD - InstDrv::CreateDevice /NOUNLOAD - SetOutPath $TEMP - File "../../telemetrum.inf" - InstDrv::InstallDriver /NOUNLOAD "$TEMP\telemetrum.inf" - - SetOutPath $INSTDIR - File "../../telemetrum.inf" -SectionEnd - -Section "AltosUI Application" - SetOutPath $INSTDIR - - File "altosui-fat.jar" - File "cmudict04.jar" - File "cmulex.jar" - File "cmu_time_awb.jar" - File "cmutimelex.jar" - File "cmu_us_kal.jar" - File "en_us.jar" - File "freetts.jar" - - File "*.dll" - - File "../../icon/*.ico" - - CreateShortCut "$SMPROGRAMS\AltusMetrum.lnk" "$INSTDIR\altosui-fat.jar" "" "$INSTDIR\altus-metrum.ico" -SectionEnd - -Section "AltosUI Desktop Shortcut" - CreateShortCut "$DESKTOP\AltusMetrum.lnk" "$INSTDIR\altosui-fat.jar" "" "$INSTDIR\altus-metrum.ico" -SectionEnd - -Section "TeleMetrum and TeleDongle Firmware" - - SetOutPath $INSTDIR - - File "../../src/telemetrum-v1.0/telemetrum-v1.0-${VERSION}.ihx" - File "../../src/teledongle-v0.2/teledongle-v0.2-${VERSION}.ihx" - -SectionEnd - -Section "Uninstaller" - - ; Deal with the uninstaller - - SetOutPath $INSTDIR - - ; Write the install path to the registry - WriteRegStr HKLM SOFTWARE\AltusMetrum "Install_Dir" "$INSTDIR" - - ; Write the uninstall keys for windows - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum" "DisplayName" "Altus Metrum" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum" "UninstallString" '"$INSTDIR\uninstall.exe"' - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum" "NoModify" "1" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum" "NoRepair" "1" - - WriteUninstaller "uninstall.exe" -SectionEnd - -Section "Uninstall" - DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum" - DeleteRegKey HKLM "Software\AltusMetrum" - - Delete "$INSTDIR\*.*" - RMDir "$INSTDIR" - - ; Remove devices - InstDrv::InitDriverSetup /NOUNLOAD {4D36E96D-E325-11CE-BFC1-08002BE10318} "Altus Metrum" - InstDrv::DeleteOemInfFiles /NOUNLOAD - InstDrv::RemoveAllDevices - - ; Remove shortcuts, if any - Delete "$SMPROGRAMS\AltusMetrum.lnk" - Delete "$DESKTOP\AltusMetrum.lnk" -SectionEnd diff --git a/ao-tools/altosui/altosui-fat b/ao-tools/altosui/altosui-fat deleted file mode 100755 index 95b1c051..00000000 --- a/ao-tools/altosui/altosui-fat +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -me=`which "$0"` -dir=`dirname "$me"` -exec java -cp "$dir/*" -Djava.library.path="$dir" -jar "$dir"/altosui-fat.jar "$@" diff --git a/ao-tools/altosui/altosui.1 b/ao-tools/altosui/altosui.1 deleted file mode 100644 index 57fa4489..00000000 --- a/ao-tools/altosui/altosui.1 +++ /dev/null @@ -1,46 +0,0 @@ -.\" -.\" Copyright © 2010 Bdale Garbee -.\" -.\" This program is free software; you can redistribute it and/or modify -.\" it under the terms of the GNU General Public License as published by -.\" the Free Software Foundation; either version 2 of the License, or -.\" (at your option) any later version. -.\" -.\" This program is distributed in the hope that it will be useful, but -.\" WITHOUT ANY WARRANTY; without even the implied warranty of -.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -.\" General Public License for more details. -.\" -.\" You should have received a copy of the GNU General Public License along -.\" with this program; if not, write to the Free Software Foundation, Inc., -.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -.\" -.\" -.TH ALTOSUI 1 "altosui" "" -.SH NAME -altosui \- Rocket flight monitor -.SH SYNOPSIS -.B "altosui" -.SH DESCRIPTION -.I altosui -connects to a TeleDongle or TeleMetrum device through a USB serial device. -It provides a menu-oriented -user interface to monitor, record and review rocket flight data. -.SH USAGE -When connected to a TeleDongle device, altosui turns on the radio -receiver and listens for telemetry packets. It displays the received -telemetry data, and reports flight status via voice synthesis. All -received telemetry information is recorded to a file. -.P -When connected to a TeleMetrum device, altosui can be used to configure the -TeleMetrum, and to downloads the eeprom data and store it in a file. -.P -A number of other menu options exist, including the ability to export flight -data in different formats. -.SH FILES -All data log files are recorded into a user-specified directory -(default ~/TeleMetrum). Files are named using the current date, the serial -number of the reporting device, the flight number recorded in the data -and either '.telem' for telemetry data or '.eeprom' for eeprom data. -.SH AUTHOR -Keith Packard diff --git a/ao-tools/altosui/altusmetrum.jpg b/ao-tools/altosui/altusmetrum.jpg deleted file mode 100644 index 04027921..00000000 Binary files a/ao-tools/altosui/altusmetrum.jpg and /dev/null differ diff --git a/ao-tools/libaltos/.gitignore b/ao-tools/libaltos/.gitignore deleted file mode 100644 index c490e6f8..00000000 --- a/ao-tools/libaltos/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -*.so -*.lo -*.la -*.java -*.class -.libs/ -classlibaltos.stamp -libaltos_wrap.c -libaltosJNI -cjnitest -libaltos.swig -swig_bindings/ diff --git a/ao-tools/libaltos/Makefile-standalone b/ao-tools/libaltos/Makefile-standalone deleted file mode 100644 index 4e438050..00000000 --- a/ao-tools/libaltos/Makefile-standalone +++ /dev/null @@ -1,126 +0,0 @@ -OS:=$(shell uname) - -# -# Linux -# -ifeq ($(OS),Linux) - -JAVA_CFLAGS=-I/usr/lib/jvm/java-6-openjdk/include - -OS_LIB_CFLAGS=-DLINUX -DPOSIX_TTY $(JAVA_CFLAGS) - -OS_APP_CFLAGS=$(OS_LIB_CFLAGS) - -OS_LDFLAGS= - -LIBNAME=libaltos.so -EXEEXT= -endif - -# -# Darwin (Mac OS X) -# -ifeq ($(OS),Darwin) - -OS_LIB_CFLAGS=\ - -DDARWIN -DPOSIX_TTY -arch i386 -arch x86_64 \ - --sysroot=/Developer/SDKs/MacOSX10.5.sdk -mmacosx-version-min=10.5 \ - -iwithsysroot /System/Library/Frameworks/JavaVM.framework/Headers \ - -iwithsysroot /System/Library/Frameworks/IOKit.framework/Headers \ - -iwithsysroot /System/Library/Frameworks/CoreFoundation.framework/Headers -OS_APP_CFLAGS=$(OS_LIB_CFLAGS) - -OS_LDFLAGS =\ - -framework IOKit -framework CoreFoundation - -LIBNAME=libaltos.dylib -EXEEXT= - -endif - -# -# Windows -# -ifneq (,$(findstring MINGW,$(OS))) - -CC=gcc - -OS_LIB_CFLAGS = -DWINDOWS -mconsole -DBUILD_DLL -OS_APP_CFLAGS = -DWINDOWS -mconsole - -OS_LDFLAGS = -lgdi32 -luser32 -lcfgmgr32 -lsetupapi -lole32 \ - -ladvapi32 -lcomctl32 -mconsole -Wl,--add-stdcall-alias - -LIBNAME=altos.dll - -EXEEXT=.exe - -endif - -.SUFFIXES: .java .class - -CLASSPATH=".:jnitest/*:libaltosJNI:/usr/share/java/*" - -SWIG_DIR=swig_bindings/java -SWIG_FILE=$(SWIG_DIR)/libaltos.swig -SWIG_WRAP=$(SWIG_DIR)/libaltos_wrap.c - -JNI_DIR=libaltosJNI -JNI_FILE=$(JNI_DIR)/libaltosJNI.java -JNI_SRCS=$(JNI_FILE) \ - $(JNI_DIR)/SWIGTYPE_p_altos_file.java \ - $(JNI_DIR)/SWIGTYPE_p_altos_list.java \ - $(JNI_DIR)/altos_device.java \ - $(JNI_DIR)/libaltos.java - -JAVAFILES=\ - $(JNI_SRCS) - -CLASSFILES = $(JAVAFILES:%.java=%.class) - -JAVAFLAGS=-Xlint:unchecked - -CJNITEST=cjnitest$(EXEEXT) - -all: $(LIBNAME) $(CJNITEST) $(CLASSFILES) - -.java.class: - javac -encoding UTF8 -classpath "$(CLASSPATH)" $(JAVAFLAGS) $*.java - -CFLAGS=$(OS_LIB_CFLAGS) -O -I. - -LDFLAGS=$(OS_LDFLAGS) - -HEADERS=libaltos.h -SRCS = libaltos.c $(SWIG_WRAP) -OBJS = $(SRCS:%.c=%.o) -LIBS = $(DARWIN_LIBS) - -$(CJNITEST): cjnitest.c $(LIBNAME) - $(CC) -o $@ $(OS_APP_CFLAGS) cjnitest.c $(LIBNAME) $(LIBS) $(LDFLAGS) - -$(LIBNAME): $(OBJS) - $(CC) -shared $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(LDFLAGS) - -clean: - rm -f $(CLASSFILES) $(OBJS) $(LIBNAME) $(CJNITEST) cjnitest.o - rm -rf swig_bindings libaltosJNI - -distclean: clean - -$(JNI_FILE): libaltos.i0 $(HEADERS) - mkdir -p $(SWIG_DIR) - mkdir -p libaltosJNI - sed 's;//%;%;' libaltos.i0 $(HEADERS) > $(SWIG_FILE) - swig -java -package libaltosJNI $(SWIG_FILE) - cp swig_bindings/java/*.java libaltosJNI - -$(SWIG_WRAP): $(JNI_FILE) - -ifeq ($(OS),Linux) -install: $(LIBNAME) - install -c $(LIBNAME) $(DESTDIR)/usr/lib/altos/$(LIBNAME) - -endif - -.NOTPARALLEL: diff --git a/ao-tools/libaltos/Makefile.am b/ao-tools/libaltos/Makefile.am deleted file mode 100644 index 388d2104..00000000 --- a/ao-tools/libaltos/Makefile.am +++ /dev/null @@ -1,41 +0,0 @@ -JAVAC=javac -AM_CFLAGS=-DLINUX -DPOSIX_TTY -I$(JVM_INCLUDE) -AM_JAVACFLAGS=-encoding UTF-8 - -altoslibdir=$(libdir)/altos - -altoslib_LTLIBRARIES=libaltos.la - -libaltos_la_LDFLAGS = -version-info 1:0:1 - -libaltos_la_SOURCES=\ - libaltos.c \ - libaltos_wrap.c - -noinst_PROGRAMS=cjnitest - -cjnitest_LDADD=libaltos.la - -LIBS= - -HFILES=libaltos.h - -SWIG_FILE=libaltos.swig - -CLASSDIR=libaltosJNI - -$(SWIG_FILE): libaltos.i0 $(HFILES) - sed 's;//%;%;' libaltos.i0 $(HFILES) > $(SWIG_FILE) - -all-local: classlibaltos.stamp - -libaltos_wrap.c: classlibaltos.stamp - -classlibaltos.stamp: $(SWIG_FILE) - swig -java -package libaltosJNI $(SWIG_FILE) - mkdir -p libaltosJNI - $(JAVAC) -d . $(AM_JAVACFLAGS) $(JAVACFLAGS) *.java && \ - touch classlibaltos.stamp - -clean-local: - -rm -rf libaltosJNI *.class *.java classlibaltos.stamp $(SWIG_FILE) libaltos_wrap.c diff --git a/ao-tools/libaltos/altos.dll b/ao-tools/libaltos/altos.dll deleted file mode 100755 index 28e9b4ad..00000000 Binary files a/ao-tools/libaltos/altos.dll and /dev/null differ diff --git a/ao-tools/libaltos/cjnitest.c b/ao-tools/libaltos/cjnitest.c deleted file mode 100644 index c6d6e069..00000000 --- a/ao-tools/libaltos/cjnitest.c +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include "libaltos.h" - -static void -altos_puts(struct altos_file *file, char *string) -{ - char c; - - while ((c = *string++)) - altos_putchar(file, c); -} - -main () -{ - struct altos_device device; - struct altos_list *list; - - altos_init(); - list = altos_list_start(); - while (altos_list_next(list, &device)) { - struct altos_file *file; - int c; - - printf ("%04x:%04x %-20s %4d %s\n", device.vendor, device.product, - device.name, device.serial, device.path); - - file = altos_open(&device); - if (!file) { - printf("altos_open failed\n"); - continue; - } - altos_puts(file,"v\nc s\n"); - altos_flush(file); - while ((c = altos_getchar(file, 100)) >= 0) { - putchar (c); - } - if (c != LIBALTOS_TIMEOUT) - printf ("getchar returns %d\n", c); - altos_close(file); - } - altos_list_finish(list); - altos_fini(); -} diff --git a/ao-tools/libaltos/libaltos.c b/ao-tools/libaltos/libaltos.c deleted file mode 100644 index 465f0ac8..00000000 --- a/ao-tools/libaltos/libaltos.c +++ /dev/null @@ -1,1028 +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. - */ - -#include "libaltos.h" -#include -#include -#include - -#define USE_POLL - -PUBLIC int -altos_init(void) -{ - return LIBALTOS_SUCCESS; -} - -PUBLIC void -altos_fini(void) -{ -} - -#ifdef DARWIN - -#undef USE_POLL - -/* Mac OS X don't have strndup even if _GNU_SOURCE is defined */ -static char * -altos_strndup (const char *s, size_t n) -{ - size_t len = strlen (s); - char *ret; - - if (len <= n) - return strdup (s); - ret = malloc(n + 1); - strncpy(ret, s, n); - ret[n] = '\0'; - return ret; -} - -#else -#define altos_strndup strndup -#endif - -/* - * Scan for Altus Metrum devices by looking through /sys - */ - -#ifdef LINUX - -#define _GNU_SOURCE -#include -#include -#include -#include -#include - -static char * -cc_fullname (char *dir, char *file) -{ - char *new; - int dlen = strlen (dir); - int flen = strlen (file); - int slen = 0; - - if (dir[dlen-1] != '/') - slen = 1; - new = malloc (dlen + slen + flen + 1); - if (!new) - return 0; - strcpy(new, dir); - if (slen) - strcat (new, "/"); - strcat(new, file); - return new; -} - -static char * -cc_basename(char *file) -{ - char *b; - - b = strrchr(file, '/'); - if (!b) - return file; - return b + 1; -} - -static char * -load_string(char *dir, char *file) -{ - char *full = cc_fullname(dir, file); - char line[4096]; - char *r; - FILE *f; - int rlen; - - f = fopen(full, "r"); - free(full); - if (!f) - return NULL; - r = fgets(line, sizeof (line), f); - fclose(f); - if (!r) - return NULL; - rlen = strlen(r); - if (r[rlen-1] == '\n') - r[rlen-1] = '\0'; - return strdup(r); -} - -static int -load_hex(char *dir, char *file) -{ - char *line; - char *end; - long i; - - line = load_string(dir, file); - if (!line) - return -1; - i = strtol(line, &end, 16); - free(line); - if (end == line) - return -1; - return i; -} - -static int -load_dec(char *dir, char *file) -{ - char *line; - char *end; - long i; - - line = load_string(dir, file); - if (!line) - return -1; - i = strtol(line, &end, 10); - free(line); - if (end == line) - return -1; - return i; -} - -static int -dir_filter_tty_colon(const struct dirent *d) -{ - return strncmp(d->d_name, "tty:", 4) == 0; -} - -static int -dir_filter_tty(const struct dirent *d) -{ - return strncmp(d->d_name, "tty", 3) == 0; -} - -struct altos_usbdev { - char *sys; - char *tty; - char *manufacturer; - char *product_name; - int serial; /* AltOS always uses simple integer serial numbers */ - int idProduct; - int idVendor; -}; - -static char * -usb_tty(char *sys) -{ - char *base; - int num_configs; - int config; - struct dirent **namelist; - int interface; - int num_interfaces; - char endpoint_base[20]; - char *endpoint_full; - char *tty_dir; - int ntty; - char *tty; - - base = cc_basename(sys); - num_configs = load_hex(sys, "bNumConfigurations"); - num_interfaces = load_hex(sys, "bNumInterfaces"); - for (config = 1; config <= num_configs; config++) { - for (interface = 0; interface < num_interfaces; interface++) { - sprintf(endpoint_base, "%s:%d.%d", - base, config, interface); - endpoint_full = cc_fullname(sys, endpoint_base); - - /* Check for tty:ttyACMx style names - */ - ntty = scandir(endpoint_full, &namelist, - dir_filter_tty_colon, - alphasort); - if (ntty > 0) { - free(endpoint_full); - tty = cc_fullname("/dev", namelist[0]->d_name + 4); - free(namelist); - return tty; - } - - /* Check for tty/ttyACMx style names - */ - tty_dir = cc_fullname(endpoint_full, "tty"); - free(endpoint_full); - ntty = scandir(tty_dir, &namelist, - dir_filter_tty, - alphasort); - free (tty_dir); - if (ntty > 0) { - tty = cc_fullname("/dev", namelist[0]->d_name); - free(namelist); - return tty; - } - } - } - return NULL; -} - -static struct altos_usbdev * -usb_scan_device(char *sys) -{ - struct altos_usbdev *usbdev; - - usbdev = calloc(1, sizeof (struct altos_usbdev)); - if (!usbdev) - return NULL; - usbdev->sys = strdup(sys); - usbdev->manufacturer = load_string(sys, "manufacturer"); - usbdev->product_name = load_string(sys, "product"); - usbdev->serial = load_dec(sys, "serial"); - usbdev->idProduct = load_hex(sys, "idProduct"); - usbdev->idVendor = load_hex(sys, "idVendor"); - usbdev->tty = usb_tty(sys); - return usbdev; -} - -static void -usbdev_free(struct altos_usbdev *usbdev) -{ - free(usbdev->sys); - free(usbdev->manufacturer); - free(usbdev->product_name); - /* this can get used as a return value */ - if (usbdev->tty) - free(usbdev->tty); - free(usbdev); -} - -#define USB_DEVICES "/sys/bus/usb/devices" - -static int -dir_filter_dev(const struct dirent *d) -{ - const char *n = d->d_name; - char c; - - while ((c = *n++)) { - if (isdigit(c)) - continue; - if (c == '-') - continue; - if (c == '.' && n != d->d_name + 1) - continue; - return 0; - } - return 1; -} - -struct altos_list { - struct altos_usbdev **dev; - int current; - int ndev; -}; - -struct altos_list * -altos_list_start(void) -{ - int e; - struct dirent **ents; - char *dir; - struct altos_usbdev *dev; - struct altos_list *devs; - int n; - - devs = calloc(1, sizeof (struct altos_list)); - if (!devs) - return NULL; - - n = scandir (USB_DEVICES, &ents, - dir_filter_dev, - alphasort); - if (!n) - return 0; - for (e = 0; e < n; e++) { - dir = cc_fullname(USB_DEVICES, ents[e]->d_name); - dev = usb_scan_device(dir); - free(dir); - if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) { - if (devs->dev) - devs->dev = realloc(devs->dev, - devs->ndev + 1 * sizeof (struct usbdev *)); - else - devs->dev = malloc (sizeof (struct usbdev *)); - devs->dev[devs->ndev++] = dev; - } - } - free(ents); - devs->current = 0; - return devs; -} - -int -altos_list_next(struct altos_list *list, struct altos_device *device) -{ - struct altos_usbdev *dev; - if (list->current >= list->ndev) - return 0; - dev = list->dev[list->current]; - strcpy(device->name, dev->product_name); - device->vendor = dev->idVendor; - device->product = dev->idProduct; - strcpy(device->path, dev->tty); - device->serial = dev->serial; - list->current++; - return 1; -} - -void -altos_list_finish(struct altos_list *usbdevs) -{ - int i; - - if (!usbdevs) - return; - for (i = 0; i < usbdevs->ndev; i++) - usbdev_free(usbdevs->dev[i]); - free(usbdevs); -} - -#endif - -#ifdef DARWIN - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct altos_list { - io_iterator_t iterator; -}; - -static int -get_string(io_object_t object, CFStringRef entry, char *result, int result_len) -{ - CFTypeRef entry_as_string; - Boolean got_string; - - entry_as_string = IORegistryEntrySearchCFProperty (object, - kIOServicePlane, - entry, - kCFAllocatorDefault, - kIORegistryIterateRecursively); - if (entry_as_string) { - got_string = CFStringGetCString(entry_as_string, - result, result_len, - kCFStringEncodingASCII); - - CFRelease(entry_as_string); - if (got_string) - return 1; - } - return 0; -} - -static int -get_number(io_object_t object, CFStringRef entry, int *result) -{ - CFTypeRef entry_as_number; - Boolean got_number; - - entry_as_number = IORegistryEntrySearchCFProperty (object, - kIOServicePlane, - entry, - kCFAllocatorDefault, - kIORegistryIterateRecursively); - if (entry_as_number) { - got_number = CFNumberGetValue(entry_as_number, - kCFNumberIntType, - result); - if (got_number) - return 1; - } - return 0; -} - -struct altos_list * -altos_list_start(void) -{ - struct altos_list *list = calloc (sizeof (struct altos_list), 1); - CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice"); - io_iterator_t tdIterator; - io_object_t tdObject; - kern_return_t ret; - int i; - - ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator); - if (ret != kIOReturnSuccess) - return NULL; - return list; -} - -int -altos_list_next(struct altos_list *list, struct altos_device *device) -{ - io_object_t object; - char serial_string[128]; - - for (;;) { - object = IOIteratorNext(list->iterator); - if (!object) - return 0; - - if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) || - !get_number (object, CFSTR(kUSBProductID), &device->product)) - continue; - if (device->vendor != 0xfffe) - continue; - if (device->product < 0x000a || 0x0013 < device->product) - continue; - if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) && - get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) && - get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) { - device->serial = atoi(serial_string); - return 1; - } - } -} - -void -altos_list_finish(struct altos_list *list) -{ - IOObjectRelease (list->iterator); - free(list); -} - -#endif - -#ifdef POSIX_TTY - -#include -#include -#include -#include -#include - -#define USB_BUF_SIZE 64 - -struct altos_file { - int fd; -#ifdef USE_POLL - int pipe[2]; -#else - int out_fd; -#endif - unsigned char out_data[USB_BUF_SIZE]; - int out_used; - unsigned char in_data[USB_BUF_SIZE]; - int in_used; - int in_read; -}; - -PUBLIC struct altos_file * -altos_open(struct altos_device *device) -{ - struct altos_file *file = calloc (sizeof (struct altos_file), 1); - int ret; - struct termios term; - - if (!file) - return NULL; - - file->fd = open(device->path, O_RDWR | O_NOCTTY); - if (file->fd < 0) { - perror(device->path); - free(file); - return NULL; - } -#ifdef USE_POLL - pipe(file->pipe); -#else - file->out_fd = open(device->path, O_RDWR | O_NOCTTY); - if (file->out_fd < 0) { - perror(device->path); - close(file->fd); - free(file); - return NULL; - } -#endif - ret = tcgetattr(file->fd, &term); - if (ret < 0) { - perror("tcgetattr"); - close(file->fd); -#ifndef USE_POLL - close(file->out_fd); -#endif - free(file); - return NULL; - } - cfmakeraw(&term); -#ifdef USE_POLL - term.c_cc[VMIN] = 1; - term.c_cc[VTIME] = 0; -#else - term.c_cc[VMIN] = 0; - term.c_cc[VTIME] = 1; -#endif - ret = tcsetattr(file->fd, TCSAFLUSH, &term); - if (ret < 0) { - perror("tcsetattr"); - close(file->fd); -#ifndef USE_POLL - close(file->out_fd); -#endif - free(file); - return NULL; - } - return file; -} - -PUBLIC void -altos_close(struct altos_file *file) -{ - if (file->fd != -1) { - int fd = file->fd; - file->fd = -1; -#ifdef USE_POLL - write(file->pipe[1], "\r", 1); -#else - close(file->out_fd); - file->out_fd = -1; -#endif - close(fd); - } -} - -PUBLIC void -altos_free(struct altos_file *file) -{ - altos_close(file); - free(file); -} - -PUBLIC int -altos_flush(struct altos_file *file) -{ - if (file->out_used && 0) { - printf ("flush \""); - fwrite(file->out_data, 1, file->out_used, stdout); - printf ("\"\n"); - } - while (file->out_used) { - int ret; - - if (file->fd < 0) - return -EBADF; -#ifdef USE_POLL - ret = write (file->fd, file->out_data, file->out_used); -#else - ret = write (file->out_fd, file->out_data, file->out_used); -#endif - if (ret < 0) - return -errno; - if (ret) { - memmove(file->out_data, file->out_data + ret, - file->out_used - ret); - file->out_used -= ret; - } - } - return 0; -} - -PUBLIC int -altos_putchar(struct altos_file *file, char c) -{ - int ret; - - if (file->out_used == USB_BUF_SIZE) { - ret = altos_flush(file); - if (ret) { - return ret; - } - } - file->out_data[file->out_used++] = c; - ret = 0; - if (file->out_used == USB_BUF_SIZE) - ret = altos_flush(file); - return 0; -} - -#ifdef USE_POLL -#include -#endif - -static int -altos_fill(struct altos_file *file, int timeout) -{ - int ret; -#ifdef USE_POLL - struct pollfd fd[2]; -#endif - - if (timeout == 0) - timeout = -1; - while (file->in_read == file->in_used) { - if (file->fd < 0) - return LIBALTOS_ERROR; -#ifdef USE_POLL - fd[0].fd = file->fd; - fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; - fd[1].fd = file->pipe[0]; - fd[1].events = POLLIN; - ret = poll(fd, 2, timeout); - if (ret < 0) { - perror("altos_getchar"); - return LIBALTOS_ERROR; - } - if (ret == 0) - return LIBALTOS_TIMEOUT; - - if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL)) - return LIBALTOS_ERROR; - if (fd[0].revents & POLLIN) -#endif - { - ret = read(file->fd, file->in_data, USB_BUF_SIZE); - if (ret < 0) { - perror("altos_getchar"); - return LIBALTOS_ERROR; - } - file->in_read = 0; - file->in_used = ret; -#ifndef USE_POLL - if (ret == 0 && timeout > 0) - return LIBALTOS_TIMEOUT; -#endif - } - } - if (file->in_used && 0) { - printf ("fill \""); - fwrite(file->in_data, 1, file->in_used, stdout); - printf ("\"\n"); - } - return 0; -} - -PUBLIC int -altos_getchar(struct altos_file *file, int timeout) -{ - int ret; - while (file->in_read == file->in_used) { - if (file->fd < 0) - return LIBALTOS_ERROR; - ret = altos_fill(file, timeout); - if (ret) - return ret; - } - return file->in_data[file->in_read++]; -} - -#endif /* POSIX_TTY */ - -#ifdef WINDOWS - -#include -#include -#include - -struct altos_list { - HDEVINFO dev_info; - int index; -}; - -#define USB_BUF_SIZE 64 - -struct altos_file { - HANDLE handle; - unsigned char out_data[USB_BUF_SIZE]; - int out_used; - unsigned char in_data[USB_BUF_SIZE]; - int in_used; - int in_read; - OVERLAPPED ov_read; - BOOL pend_read; - OVERLAPPED ov_write; -}; - -PUBLIC struct altos_list * -altos_list_start(void) -{ - struct altos_list *list = calloc(1, sizeof (struct altos_list)); - - if (!list) - return NULL; - list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL, - DIGCF_ALLCLASSES|DIGCF_PRESENT); - if (list->dev_info == INVALID_HANDLE_VALUE) { - printf("SetupDiGetClassDevs failed %d\n", GetLastError()); - free(list); - return NULL; - } - list->index = 0; - return list; -} - -PUBLIC int -altos_list_next(struct altos_list *list, struct altos_device *device) -{ - SP_DEVINFO_DATA dev_info_data; - char port[128]; - DWORD port_len; - char friendlyname[256]; - char symbolic[256]; - DWORD symbolic_len; - HKEY dev_key; - int vid, pid; - int serial; - HRESULT result; - DWORD friendlyname_type; - DWORD friendlyname_len; - - dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA); - while(SetupDiEnumDeviceInfo(list->dev_info, list->index, - &dev_info_data)) - { - list->index++; - - dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data, - DICS_FLAG_GLOBAL, 0, DIREG_DEV, - KEY_READ); - if (dev_key == INVALID_HANDLE_VALUE) { - printf("cannot open device registry key\n"); - continue; - } - - /* Fetch symbolic name for this device and parse out - * the vid/pid/serial info */ - symbolic_len = sizeof(symbolic); - result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL, - symbolic, &symbolic_len); - if (result != 0) { - printf("cannot find SymbolicName value\n"); - RegCloseKey(dev_key); - continue; - } - vid = pid = serial = 0; - sscanf(symbolic + sizeof("\\??\\USB#VID_") - 1, - "%04X", &vid); - sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1, - "%04X", &pid); - sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1, - "%d", &serial); - if (!USB_IS_ALTUSMETRUM(vid, pid)) { - RegCloseKey(dev_key); - continue; - } - - /* Fetch the com port name */ - port_len = sizeof (port); - result = RegQueryValueEx(dev_key, "PortName", NULL, NULL, - port, &port_len); - RegCloseKey(dev_key); - if (result != 0) { - printf("failed to get PortName\n"); - continue; - } - - /* Fetch the device description which is the device name, - * with firmware that has unique USB ids */ - friendlyname_len = sizeof (friendlyname); - if(!SetupDiGetDeviceRegistryProperty(list->dev_info, - &dev_info_data, - SPDRP_FRIENDLYNAME, - &friendlyname_type, - (BYTE *)friendlyname, - sizeof(friendlyname), - &friendlyname_len)) - { - printf("Failed to get friendlyname\n"); - continue; - } - device->vendor = vid; - device->product = pid; - device->serial = serial; - strcpy(device->name, friendlyname); - - strcpy(device->path, port); - return 1; - } - result = GetLastError(); - if (result != ERROR_NO_MORE_ITEMS) - printf ("SetupDiEnumDeviceInfo failed error %d\n", result); - return 0; -} - -PUBLIC void -altos_list_finish(struct altos_list *list) -{ - SetupDiDestroyDeviceInfoList(list->dev_info); - free(list); -} - -static int -altos_queue_read(struct altos_file *file) -{ - DWORD got; - if (file->pend_read) - return LIBALTOS_SUCCESS; - - if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) { - if (GetLastError() != ERROR_IO_PENDING) - return LIBALTOS_ERROR; - file->pend_read = TRUE; - } else { - file->pend_read = FALSE; - file->in_read = 0; - file->in_used = got; - } - return LIBALTOS_SUCCESS; -} - -static int -altos_wait_read(struct altos_file *file, int timeout) -{ - DWORD ret; - DWORD got; - - if (!file->pend_read) - return LIBALTOS_SUCCESS; - - if (!timeout) - timeout = INFINITE; - - ret = WaitForSingleObject(file->ov_read.hEvent, timeout); - switch (ret) { - case WAIT_OBJECT_0: - if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) - return LIBALTOS_ERROR; - file->pend_read = FALSE; - file->in_read = 0; - file->in_used = got; - break; - case WAIT_TIMEOUT: - return LIBALTOS_TIMEOUT; - break; - default: - return LIBALTOS_ERROR; - } - return LIBALTOS_SUCCESS; -} - -static int -altos_fill(struct altos_file *file, int timeout) -{ - int ret; - - if (file->in_read < file->in_used) - return LIBALTOS_SUCCESS; - - file->in_read = file->in_used = 0; - - ret = altos_queue_read(file); - if (ret) - return ret; - ret = altos_wait_read(file, timeout); - if (ret) - return ret; - - return LIBALTOS_SUCCESS; -} - -PUBLIC int -altos_flush(struct altos_file *file) -{ - DWORD put; - char *data = file->out_data; - char used = file->out_used; - DWORD ret; - - while (used) { - if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) { - if (GetLastError() != ERROR_IO_PENDING) - return LIBALTOS_ERROR; - ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE); - switch (ret) { - case WAIT_OBJECT_0: - if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) - return LIBALTOS_ERROR; - break; - default: - return LIBALTOS_ERROR; - } - } - data += put; - used -= put; - } - file->out_used = 0; - return LIBALTOS_SUCCESS; -} - -PUBLIC struct altos_file * -altos_open(struct altos_device *device) -{ - struct altos_file *file = calloc (1, sizeof (struct altos_file)); - char full_name[64]; - DCB dcbSerialParams = {0}; - COMMTIMEOUTS timeouts; - - if (!file) - return NULL; - - strcpy(full_name, "\\\\.\\"); - strcat(full_name, device->path); - file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE, - 0, NULL, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, NULL); - if (file->handle == INVALID_HANDLE_VALUE) { - free(file); - return NULL; - } - file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - timeouts.ReadIntervalTimeout = MAXDWORD; - timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; - timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */ - timeouts.WriteTotalTimeoutMultiplier = 0; - timeouts.WriteTotalTimeoutConstant = 0; - SetCommTimeouts(file->handle, &timeouts); - - dcbSerialParams.DCBlength = sizeof(dcbSerialParams); - if (!GetCommState(file->handle, &dcbSerialParams)) { - CloseHandle(file->handle); - free(file); - return NULL; - } - dcbSerialParams.BaudRate = CBR_9600; - dcbSerialParams.ByteSize = 8; - dcbSerialParams.StopBits = ONESTOPBIT; - dcbSerialParams.Parity = NOPARITY; - if (!SetCommState(file->handle, &dcbSerialParams)) { - CloseHandle(file->handle); - free(file); - return NULL; - } - - return file; -} - -PUBLIC void -altos_close(struct altos_file *file) -{ - if (file->handle != INVALID_HANDLE_VALUE) { - CloseHandle(file->handle); - file->handle = INVALID_HANDLE_VALUE; - } -} - -PUBLIC void -altos_free(struct altos_file *file) -{ - altos_close(file); - free(file); -} - -int -altos_putchar(struct altos_file *file, char c) -{ - int ret; - - if (file->out_used == USB_BUF_SIZE) { - ret = altos_flush(file); - if (ret) - return ret; - } - file->out_data[file->out_used++] = c; - if (file->out_used == USB_BUF_SIZE) - return altos_flush(file); - return LIBALTOS_SUCCESS; -} - -int -altos_getchar(struct altos_file *file, int timeout) -{ - int ret; - while (file->in_read == file->in_used) { - if (file->handle == INVALID_HANDLE_VALUE) - return LIBALTOS_ERROR; - ret = altos_fill(file, timeout); - if (ret) - return ret; - } - return file->in_data[file->in_read++]; -} - -#endif diff --git a/ao-tools/libaltos/libaltos.dylib b/ao-tools/libaltos/libaltos.dylib deleted file mode 100755 index 89aa12e7..00000000 Binary files a/ao-tools/libaltos/libaltos.dylib and /dev/null differ diff --git a/ao-tools/libaltos/libaltos.h b/ao-tools/libaltos/libaltos.h deleted file mode 100644 index 6e94899e..00000000 --- a/ao-tools/libaltos/libaltos.h +++ /dev/null @@ -1,102 +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. - */ - -#ifndef _LIBALTOS_H_ -#define _LIBALTOS_H_ - -#include - -#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) -# ifndef BUILD_STATIC -# ifdef BUILD_DLL -# define PUBLIC __declspec(dllexport) -# else -# define PUBLIC __declspec(dllimport) -# endif -# endif /* BUILD_STATIC */ -#endif - -#ifndef PUBLIC -# define PUBLIC -#endif - -#define USB_VENDOR_FSF 0xfffe -#define USB_VENDOR_ALTUSMETRUM USB_VENDOR_FSF -#define USB_PRODUCT_ALTUSMETRUM 0x000a -#define USB_PRODUCT_TELEMETRUM 0x000b -#define USB_PRODUCT_TELEDONGLE 0x000c -#define USB_PRODUCT_TELETERRA 0x000d -#define USB_PRODUCT_ALTUSMETRUM_MIN 0x000a -#define USB_PRODUCT_ALTUSMETRUM_MAX 0x0013 - -#define USB_IS_ALTUSMETRUM(v,p) ((v) == USB_VENDOR_ALTUSMETRUM && \ - (USB_PRODUCT_ALTUSMETRUM_MIN <= (p) && \ - (p) <= USB_PRODUCT_ALTUSMETRUM_MAX)) - -struct altos_device { - //%immutable; - int vendor; - int product; - int serial; - char name[256]; - char path[256]; - //%mutable; -}; - -#define LIBALTOS_SUCCESS 0 -#define LIBALTOS_ERROR -1 -#define LIBALTOS_TIMEOUT -2 - -/* Returns 0 for success, < 0 on error */ -PUBLIC int -altos_init(void); - -PUBLIC void -altos_fini(void); - -PUBLIC struct altos_list * -altos_list_start(void); - -/* Returns 1 for success, zero on end of list */ -PUBLIC int -altos_list_next(struct altos_list *list, struct altos_device *device); - -PUBLIC void -altos_list_finish(struct altos_list *list); - -PUBLIC struct altos_file * -altos_open(struct altos_device *device); - -PUBLIC void -altos_close(struct altos_file *file); - -PUBLIC void -altos_free(struct altos_file *file); - -/* Returns < 0 for error */ -PUBLIC int -altos_putchar(struct altos_file *file, char c); - -/* Returns < 0 for error */ -PUBLIC int -altos_flush(struct altos_file *file); - -/* Returns < 0 for error or timeout. timeout of 0 == wait forever */ -PUBLIC int -altos_getchar(struct altos_file *file, int timeout); - -#endif /* _LIBALTOS_H_ */ diff --git a/ao-tools/libaltos/libaltos.i0 b/ao-tools/libaltos/libaltos.i0 deleted file mode 100644 index d06468f5..00000000 --- a/ao-tools/libaltos/libaltos.i0 +++ /dev/null @@ -1,5 +0,0 @@ -%module libaltos -%{ -#include "libaltos.h" -%} - diff --git a/configure.ac b/configure.ac index 903c5765..22af00ab 100644 --- a/configure.ac +++ b/configure.ac @@ -133,6 +133,8 @@ PKG_CHECK_MODULES([SNDFILE], [sndfile]) AC_OUTPUT([ Makefile +altosui/Makefile +altosui/libaltos/Makefile ao-tools/Makefile ao-tools/lib/Makefile ao-tools/ao-rawload/Makefile @@ -144,8 +146,6 @@ ao-tools/ao-list/Makefile ao-tools/ao-load/Makefile ao-tools/ao-postflight/Makefile ao-tools/ao-view/Makefile -ao-tools/libaltos/Makefile -ao-tools/altosui/Makefile ao-utils/Makefile src/Version ]) -- cgit v1.2.3 From 915f881d61294dc6f5a6a3e8d75567e18492a631 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Thu, 25 Nov 2010 09:52:30 +1000 Subject: doc: Document altosui "Site Map" tab --- doc/telemetrum-doc.xsl | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/telemetrum-doc.xsl b/doc/telemetrum-doc.xsl index 8f554d88..f7c8627c 100644 --- a/doc/telemetrum-doc.xsl +++ b/doc/telemetrum-doc.xsl @@ -1044,6 +1044,27 @@ during the flight are displayed for your admiring observers.
+
+ Site Map + + When the rocket gets a GPS fix, the Site Map tab will map + the rocket's position to make it easier for you to locate the + rocket, both while it is in the air, and when it has landed. The + rocket's state is indicated by colour: white for pad, red for + boost, pink for fast, yellow for coast, light blue for drogue, + dark blue for main, and black for landed. + + + The map's scale is approximately 3m (10ft) per pixel. The map + can be dragged using the left mouse button. The map will attempt + to keep the rocket roughly centred while data is being received. + + + Images are fetched automatically via the Google Maps Static API, + and are cached for reuse. If map images cannot be downloaded, + the rocket's path will be traced on a dark grey background + instead. +
Save Flight Data -- cgit v1.2.3 From b372f3c0ee4ec49aabe61c169cb1eb9bb4fb2cfc Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 24 Nov 2010 18:50:46 -0800 Subject: Missing change to top level Makefile to build altosui Signed-off-by: Keith Packard --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 264e577a..2c412bf9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS=src ao-tools ao-utils +SUBDIRS=src altosui ao-tools ao-utils EXTRA_DIST = ChangeLog -- cgit v1.2.3 From 7cd1c7765d137df711caeeb69abaaba1b36e0a65 Mon Sep 17 00:00:00 2001 From: Bdale Garbee Date: Wed, 24 Nov 2010 20:53:36 -0700 Subject: fix missing section close in Site Map content --- doc/telemetrum-doc.xsl | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/telemetrum-doc.xsl b/doc/telemetrum-doc.xsl index f7c8627c..4e71464d 100644 --- a/doc/telemetrum-doc.xsl +++ b/doc/telemetrum-doc.xsl @@ -1065,6 +1065,7 @@ the rocket's path will be traced on a dark grey background instead. +
Save Flight Data -- cgit v1.2.3 From 8a68c1da253c0b29a7cb9c7540c20585ad6e3dec Mon Sep 17 00:00:00 2001 From: Bdale Garbee Date: Wed, 24 Nov 2010 21:21:53 -0700 Subject: tweak rev history --- doc/telemetrum-doc.xsl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/telemetrum-doc.xsl b/doc/telemetrum-doc.xsl index 4e71464d..ef10ee40 100644 --- a/doc/telemetrum-doc.xsl +++ b/doc/telemetrum-doc.xsl @@ -29,8 +29,9 @@ 0.3 - 23 November 2010 - New section on AltosUI mostly by Keith + 24 November 2010 + New section on AltosUI mostly by Keith with contributions + from Anthony Towns. Many other updates. 0.2 -- cgit v1.2.3 From 4e47c44d335276cf0dc5ed3a0756e50c98c1b9b9 Mon Sep 17 00:00:00 2001 From: Bdale Garbee Date: Wed, 24 Nov 2010 21:44:53 -0700 Subject: manually fold in documentation work from the master branch --- doc/telemetrum-doc.xsl | 256 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 208 insertions(+), 48 deletions(-) diff --git a/doc/telemetrum-doc.xsl b/doc/telemetrum-doc.xsl index ef10ee40..e75e10b5 100644 --- a/doc/telemetrum-doc.xsl +++ b/doc/telemetrum-doc.xsl @@ -28,20 +28,9 @@ - 0.3 + 0.8 24 November 2010 - New section on AltosUI mostly by Keith with contributions - from Anthony Towns. Many other updates. - - - 0.2 - 18 July 2010 - Significant update - - - 0.1 - 30 March 2010 - Initial content + Updated for software version 0.8 @@ -87,52 +76,40 @@ The first thing to do after you check the inventory of parts in your "starter kit" is to charge the battery by plugging it into the - corresponding socket of the TeleMetrum and then using the USB A to B + corresponding socket of the TeleMetrum and then using the USB A to +mini B cable to plug the Telemetrum into your computer's USB socket. The TeleMetrum circuitry will charge the battery whenever it is plugged - into the usb socket. The TeleMetrum's on-off switch does NOT control - the charging circuitry. When the GPS chip is initially searching for - satellites, the unit will pull more current than it can pull from the - usb port, so the battery must be plugged in order to get a good - satellite lock. Once GPS is locked the current consumption goes back + in, because the TeleMetrum's on-off switch does NOT control the + charging circuitry. When the GPS chip is initially searching for + satellites, TeleMetrum will consume more current than it can pull + from the usb port, so the battery must be attached in order to get + satellite lock. Once GPS is locked, the current consumption goes back down enough to enable charging while running. So it's a good idea to fully charge the battery as your first item of business so there is no issue getting and maintaining satellite lock. The yellow charge indicator led will go out when the - battery is nearly full and the charger goes to trickle charge. + battery is nearly full and the charger goes to trickle charge. It + can takeseveral hours to fully recharge a deeply discharged battery. - The other active device in the starter kit is the half-duplex TeleDongle - rf link. If you plug it in to your computer it should "just work", - showing up as a serial port device. If you are using Linux and are + The other active device in the starter kit is the TeleDongle USB to + RF interface. If you plug it in to your Mac or Linux computer it should + "just work", showing up as a serial port device. Windows systems need + driver information that is part of the AltOS download to know that the + existing USB modem driver will work. If you are using Linux and are having problems, try moving to a fresher kernel (2.6.33 or newer), as - there were some ugly USB serial driver bugs in earlier versions. - - - Next you should obtain and install the AltOS utilities. The first - generation sofware was written for Linux only. New software is coming - soon that will also run on Windows and Mac. For now, we'll concentrate - on Linux. If you are using Debian, an 'altos' package already exists, - see http://altusmetrum.org/AltOS for details on how to install it. - User-contributed directions for building packages on ArchLinux may be - found in the contrib/arch-linux directory as PKGBUILD files. - Between the debian/rules file and the PKGBUILD files in - contrib, you should find enough information to learn how to build the - software for any other version of Linux. + the USB serial driver had ugly bugs in some earlier versions. - When you have successfully installed the software suite (either from - compiled source code or as the pre-built Debian package) you will - have 10 or so executable programs all of which have names beginning - with 'ao-'. - ('ao-view' is the lone GUI-based program, the rest are command-line - oriented.) You will also have man pages, that give you basic info - on each program. - You will also get this documentation in two file types in the doc/ - directory, telemetrum-doc.pdf and telemetrum-doc.html. - Finally you will have a couple control files that allow the ao-view - GUI-based program to appear in your menu of programs (under - the 'Internet' category). + Next you should obtain and install the AltOS utilities. These include + the AltosUI ground station program, current firmware images for + TeleMetrum and TeleDongle, and a number of standalone utilities that + are rarely needed. Pre-built binary packages are available for Debian + Linux, Microsoft Windows, and recent MacOSX versions. Full sourcecode + and build instructions for some other Linux variants are also available. + The latest version may always be downloaded from + http://altusmetrum.org/AltOS. Both Telemetrum and TeleDongle can be directly communicated @@ -764,8 +741,191 @@ sensor... nothing is permanently "lost" or "damaged" if the calibration is poor. + + In the unlikely event an accel cal that goes badly, it is possible + that TeleMetrum may always come up in 'pad mode' and as such not be + listening to either the USB or radio interfaces. If that happens, + there is a special hook in the firmware to force the board back + in to 'idle mode' so you can re-do the cal. To use this hook, you + just need to ground the SPI clock pin at power-on. This pin is + available as pin 2 on the 8-pin companion connector, and pin 1 is + ground. So either carefully install a fine-gauge wire jumper + between the two pins closest to the index hole end of the 8-pin + connector, or plug in the programming cable to the 8-pin connector + and use a small screwdriver or similar to short the two pins closest + to the index post on the 4-pin end of the programming cable, and + power up the board. It should come up in 'idle mode' (two beeps). +
+ + + +
+ Updating Device Firmware + + The big conceptual thing to realize is that you have to use a + TeleDongle as a programmer to update a TeleMetrum, and vice versa. + Due to limited memory resources in the cc1111, we don't support + programming either unit directly over USB. + + + You may wish to begin by ensuring you have current firmware images. + These are distributed as part of the AltOS software bundle that + also includes the AltosUI ground station program. Newer ground + station versions typically work fine with older firmware versions, + so you don't need to update your devices just to try out new + software features. You can always download the most recent + version from http://www.altusmetrum.org/AltOS/. + + + We recommend updating TeleMetrum first, before updating TeleDongle. + +
+ Updating TeleMetrum Firmware + + + Find the 'programming cable' that you got as part of the starter + kit, that has a red 8-pin MicroMaTch connector on one end and a + red 4-pin MicroMaTch connector on the other end. + + + Take the 2 screws out of the TeleDongle case to get access + to the circuit board. + + + Plug the 8-pin end of the programming cable to the + matching connector on the TeleDongle, and the 4-pin end to the + matching connector on the TeleMetrum. + Note that each MicroMaTch connector has an alignment pin that + goes through a hole in the PC board when you have the cable + oriented correctly. + + + Attach a battery to the TeleMetrum board. + + + Plug the TeleDongle into your computer's USB port, and power + up the TeleMetrum. + + + Run AltosUI, and select 'Flash Image' from the File menu. + + + Pick the TeleDongle device from the list, identifying it as the + programming device. + + + Select the image you want put on the TeleMetrum, which should have a + name in the form telemetrum-v1.0-0.7.1.ihx. It should be visible + in the default directory, if not you may have to poke around + your system to find it. + + + Make sure the configuration parameters are reasonable + looking. If the serial number and/or RF configuration + values aren't right, you'll need to change them. + + + Hit the 'OK' button and the software should proceed to flash + the TeleMetrum with new firmware, showing a progress bar. + + + Confirm that the TeleMetrum board seems to have updated ok, which you + can do by plugging in to it over USB and using a terminal program + to connect to the board and issue the 'v' command to check + the version, etc. + + + If something goes wrong, give it another try. + + +
+
+ Updating TeleDongle Firmware + + Updating TeleDongle's firmware is just like updating TeleMetrum + firmware, but you switch which board is the programmer and which + is the programming target. + + + + Find the 'programming cable' that you got as part of the starter + kit, that has a red 8-pin MicroMaTch connector on one end and a + red 4-pin MicroMaTch connector on the other end. + + + Find the USB cable that you got as part of the starter kit, and + plug the "mini" end in to the mating connector on TeleMetrum. + + + Take the 2 screws out of the TeleDongle case to get access + to the circuit board. + + + Plug the 8-pin end of the programming cable to the (latching) + matching connector on the TeleMetrum, and the 4-pin end to the + matching connector on the TeleDongle. + Note that each MicroMaTch connector has an alignment pin that + goes through a hole in the PC board when you have the cable + oriented correctly. + + + Attach a battery to the TeleMetrum board. + + + Plug both TeleMetrum and TeleDongle into your computer's USB + ports, and power up the TeleMetrum. + + + Run AltosUI, and select 'Flash Image' from the File menu. + + + Pick the TeleMetrum device from the list, identifying it as the + programming device. + + + Select the image you want put on the TeleDongle, which should have a + name in the form teledongle-v0.2-0.7.1.ihx. It should be visible + in the default directory, if not you may have to poke around + your system to find it. + + + Make sure the configuration parameters are reasonable + looking. If the serial number and/or RF configuration + values aren't right, you'll need to change them. The TeleDongle + serial number is on the "bottom" of the circuit board, and can + usually be read through the translucent blue plastic case without + needing to remove the board from the case. + + + Hit the 'OK' button and the software should proceed to flash + the TeleDongle with new firmware, showing a progress bar. + + + Confirm that the TeleDongle board seems to have updated ok, which you + can do by plugging in to it over USB and using a terminal program + to connect to the board and issue the 'v' command to check + the version, etc. Once you're happy, remove the programming cable + and put the cover back on the TeleDongle. + + + If something goes wrong, give it another try. + + + + Be careful removing the programming cable from the locking 8-pin + connector on TeleMetrum. You'll need a fingernail or perhaps a thin + screwdriver or knife blade to gently pry the locking ears out + slightly to extract the connector. We used a locking connector on + TeleMetrum to help ensure that the cabling to companion boards + used in a rocket don't ever come loose accidentally in flight. + +
+
+ + + -- cgit v1.2.3