diff options
Diffstat (limited to 'ao-tools/altosui')
24 files changed, 2246 insertions, 396 deletions
diff --git a/ao-tools/altosui/Altos.java b/ao-tools/altosui/Altos.java new file mode 100644 index 00000000..53359e23 --- /dev/null +++ b/ao-tools/altosui/Altos.java @@ -0,0 +1,117 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.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,Integer> string_to_state = new HashMap<String,Integer>(); + + static boolean map_initialized = false; + + 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; +} diff --git a/ao-tools/altosui/AltosCSV.java b/ao-tools/altosui/AltosCSV.java new file mode 100644 index 00000000..24936758 --- /dev/null +++ b/ao-tools/altosui/AltosCSV.java @@ -0,0 +1,115 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.lang.*; +import java.io.*; +import altosui.AltosRecord; + +public class AltosCSV { + File name; + PrintStream out; + boolean header_written; + + static final int ALTOS_CSV_VERSION = 1; + + /* Version 1 format: + * + * General info + * version number + * serial number + * flight number + * callsign + * time (seconds since boost) + * + * Flight status + * state + * state name + * + * Basic sensors + * acceleration (m/s²) + * pressure (mBar) + * altitude (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) + * + * GPS Sat data + * hdop + * C/N0 data for all 32 valid SDIDs + */ + + void write_general_header() { + out.printf("version serial flight call time"); + } + + void write_general(AltosRecord record) { + out.printf("%s,%d,%d,%s,%d", + record.version, record.serial, record.flight, record.callsign, record.tick); + } + + void write_flight_header() { + out.printf("state state_name"); + } + + void write_flight(AltosRecord record) { + out.printf("%d,%s", record.state, record.state()); + } + + void write_header() { + out.printf("# "); write_general_header(); + out.printf(" "); write_flight_header(); + out.printf ("\n"); + } + + public void write(AltosRecord record) { + if (!header_written) { + write_header(); + header_written = true; + } + write_general(record); out.printf(","); + write_flight(record); + out.printf ("\n"); + } + + public PrintStream out() { + return out; + } + + public AltosCSV(File in_name) throws FileNotFoundException { + name = in_name; + out = new PrintStream(name); + } +} diff --git a/ao-tools/altosui/AltosChannelMenu.java b/ao-tools/altosui/AltosChannelMenu.java new file mode 100644 index 00000000..504c13c6 --- /dev/null +++ b/ao-tools/altosui/AltosChannelMenu.java @@ -0,0 +1,70 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosChannelMenu extends JMenu implements ActionListener { + ButtonGroup group; + int channel; + LinkedList<ActionListener> listeners; + + public void addActionListener(ActionListener l) { + listeners.add(l); + } + + public void actionPerformed(ActionEvent e) { + channel = Integer.parseInt(e.getActionCommand()); + + ListIterator<ActionListener> i = listeners.listIterator(); + + ActionEvent newe = new ActionEvent(this, channel, e.getActionCommand()); + while (i.hasNext()) { + ActionListener listener = i.next(); + listener.actionPerformed(newe); + } + } + + public AltosChannelMenu(int current_channel) { + super("Channel", true); + group = new ButtonGroup(); + + channel = current_channel; + + listeners = new LinkedList<ActionListener>(); + 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); + } + } + +} diff --git a/ao-tools/altosui/AltosConfig.java b/ao-tools/altosui/AltosConfig.java new file mode 100644 index 00000000..ac73e7c5 --- /dev/null +++ b/ao-tools/altosui/AltosConfig.java @@ -0,0 +1,264 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.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 { + + 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; + string_ref version; + string_ref product; + string_ref callsign; + AltosConfigUI config_ui; + + + 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 { + if (remote) { + serial_line.printf("m 0\n"); + serial_line.set_channel(AltosPreferences.channel()); + serial_line.set_callsign(AltosPreferences.callsign()); + serial_line.printf("p\n"); + } + } + + void stop_serial() throws InterruptedException { + if (remote) { + serial_line.printf("~\n"); + serial_line.flush(); + } + } + + void get_data() throws InterruptedException { + try { + start_serial(); + serial_line.printf("c s\nv\n"); + for (;;) { + String line = serial_line.get_reply(); + 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_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 () { + 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_callsign(callsign.get()); + config_ui.set_clean(); + } catch (InterruptedException ie) { + } + } + + 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()); + 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 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(); + if (cmd.equals("save")) { + save_data(); + set_ui(); + } else if (cmd.equals("reset")) { + set_ui(); + } else if (cmd.equals("close")) { + if (serial_line != null) + serial_line.close(); + } + } + + public void run () { + try { + init_ui(); + config_ui.make_visible(); +// } catch (InterruptedException ie) { + } finally { + } + } + + 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); + callsign = new string_ref("N0CALL"); + version = new string_ref("unknown"); + product = new string_ref("unknown"); + + device = AltosDeviceDialog.show(owner, AltosDevice.Any); + serial_line = new AltosSerial(); + if (device != null) { + try { + serial_line.open(device); + if (!device.matchProduct(AltosDevice.TeleMetrum)) + remote = true; + config_thread = new Thread(this); + config_thread.start(); + } catch (FileNotFoundException ee) { + JOptionPane.showMessageDialog(owner, + String.format("Cannot open device \"%s\"", + device.getPath()), + "Cannot open target device", + JOptionPane.ERROR_MESSAGE); + } catch (IOException ee) { + JOptionPane.showMessageDialog(owner, + device.getPath(), + 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 new file mode 100644 index 00000000..1d8c579a --- /dev/null +++ b/ao-tools/altosui/AltosConfigUI.java @@ -0,0 +1,423 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import javax.swing.event.*; +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.AltosFlightStatusTableModel; +import altosui.AltosFlightInfoTableModel; + +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 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 callsign_value; + + JButton save; + JButton reset; + 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) { + 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 = 3; + 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 = 3; c.gridy = 0; + c.gridwidth = 3; + 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 = 3; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + version_label = new JLabel("Software version:"); + pane.add(version_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; + version_value = new JLabel(""); + pane.add(version_value, c); + + /* Serial */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 2; + 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 = 2; + c.gridwidth = 3; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + serial_value = new JLabel(""); + pane.add(serial_value, c); + + /* Main deploy */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 3; + c.gridwidth = 3; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + c.ipady = 3; + main_deploy_label = new JLabel("Main Deploy Altitude(m):"); + pane.add(main_deploy_label, c); + + c = new GridBagConstraints(); + c.gridx = 3; c.gridy = 3; + c.gridwidth = 3; + 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 = 3; + 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 = 3; c.gridy = 4; + c.gridwidth = 3; + 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 = 3; + 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 = 3; c.gridy = 5; + c.gridwidth = 3; + 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); + pane.add(radio_channel_value, c); + + /* Callsign */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 6; + 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.gridwidth = 3; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + c.ipady = 5; + callsign_value = new JTextField("N0CALL"); + callsign_value.getDocument().addDocumentListener(this); + pane.add(callsign_value, c); + + /* Buttons */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 7; + 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 = 7; + 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 = 7; + 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() { + if (dirty) { + Object[] options = { "Close anyway", "Keep editing" }; + int i; + i = JOptionPane.showOptionDialog(this, + "Configuration modified, close anyway?", + "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")) + if (!check_dirty()) + return; + listener.actionPerformed(e); + if (cmd.equals("close")) { + 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_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/AltosConvert.java b/ao-tools/altosui/AltosConvert.java index 3be0716c..8cc1df27 100644 --- a/ao-tools/altosui/AltosConvert.java +++ b/ao-tools/altosui/AltosConvert.java @@ -62,7 +62,7 @@ public class AltosConvert { * altitudes are measured with respect to the mean sea level */ static double - cc_altitude_to_pressure(double altitude) + altitude_to_pressure(double altitude) { double base_temperature = LAYER0_BASE_TEMPERATURE; double base_pressure = LAYER0_BASE_PRESSURE; @@ -115,7 +115,7 @@ public class AltosConvert { /* outputs the altitude associated with the given pressure. the altitude returned is measured with respect to the mean sea level */ static double - cc_pressure_to_altitude(double pressure) + pressure_to_altitude(double pressure) { double next_base_temperature = LAYER0_BASE_TEMPERATURE; @@ -178,59 +178,6 @@ public class AltosConvert { return altitude; } - /* - * 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 - cc_barometer_to_pressure(double count) - { - return ((count / 16.0) / 2047.0 + 0.095) / 0.009 * 1000.0; - } - - static double - cc_barometer_to_altitude(double baro) - { - double Pa = cc_barometer_to_pressure(baro); - return cc_pressure_to_altitude(Pa); - } - - static final double count_per_mss = 27.0; - - static double - cc_accelerometer_to_acceleration(double accel, double ground_accel) - { - return (ground_accel - accel) / count_per_mss; - } - - /* 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 - cc_thermometer_to_temperature(double thermo) - { - return (thermo - 19791.268) / 32728.0 * 1.25 / 0.00247; - } - static double cc_battery_to_voltage(double battery) { diff --git a/ao-tools/altosui/AltosDevice.java b/ao-tools/altosui/AltosDevice.java index f488174c..3daf0742 100644 --- a/ao-tools/altosui/AltosDevice.java +++ b/ao-tools/altosui/AltosDevice.java @@ -22,16 +22,76 @@ import libaltosJNI.*; public class AltosDevice extends altos_device { + static boolean initialized = false; + static { + try { + System.loadLibrary("altos"); + libaltos.altos_init(); + initialized = true; + } catch (UnsatisfiedLinkError e) { + System.err.println("Native library failed to load.\n" + e); + } + } + public final static int TeleMetrum = libaltosConstants.USB_PRODUCT_TELEMETRUM; + public final static int TeleDongle = libaltosConstants.USB_PRODUCT_TELEDONGLE; + public final static int TeleTerra = libaltosConstants.USB_PRODUCT_TELETERRA; + public final static int Any = 0x10000; + public final static int BaseStation = 0x10000 + 1; + public String toString() { + String name = getName(); + if (name == null) + name = "Altus Metrum"; return String.format("%-20.20s %4d %s", - getProduct(), getSerial(), getPath()); + getName(), getSerial(), getPath()); } - static { - System.loadLibrary("altos"); - libaltos.altos_init(); + public boolean isAltusMetrum() { + if (getVendor() != libaltosConstants.USB_VENDOR_ALTUSMETRUM) + return false; + if (getProduct() < libaltosConstants.USB_PRODUCT_ALTUSMETRUM_MIN) + return false; + if (getProduct() > libaltosConstants.USB_PRODUCT_ALTUSMETRUM_MAX) + return false; + return true; } - static AltosDevice[] list(String product) { + + public boolean matchProduct(int want_product) { + + if (want_product == Any) + return true; + + if (want_product == BaseStation) + return matchProduct(TeleDongle) || matchProduct(TeleTerra); + + if (!isAltusMetrum()) + return false; + + int have_product = getProduct(); + + if (want_product == have_product) + return true; + + if (have_product != libaltosConstants.USB_PRODUCT_ALTUSMETRUM) + return false; + + String name = getName(); + + if (name == null) + return false; + if (want_product == libaltosConstants.USB_PRODUCT_TELEMETRUM) + return name.startsWith("TeleMetrum"); + if (want_product == libaltosConstants.USB_PRODUCT_TELEDONGLE) + return name.startsWith("TeleDongle"); + if (want_product == libaltosConstants.USB_PRODUCT_TELETERRA) + return name.startsWith("TeleTerra"); + return false; + } + + static AltosDevice[] list(int product) { + if (!initialized) + return null; + SWIGTYPE_p_altos_list list = libaltos.altos_list_start(); ArrayList<AltosDevice> device_list = new ArrayList<AltosDevice>(); @@ -42,7 +102,7 @@ public class AltosDevice extends altos_device { AltosDevice device = new AltosDevice(); if (libaltos.altos_list_next(list, device) == 0) break; - if (product == null || device.getProduct().startsWith(product)) + if (device.matchProduct(product)) device_list.add(device); } libaltos.altos_list_finish(list); diff --git a/ao-tools/altosui/AltosDeviceDialog.java b/ao-tools/altosui/AltosDeviceDialog.java index c60bd7c3..3df4c6eb 100644 --- a/ao-tools/altosui/AltosDeviceDialog.java +++ b/ao-tools/altosui/AltosDeviceDialog.java @@ -31,16 +31,16 @@ import altosui.AltosDevice; public class AltosDeviceDialog extends JDialog implements ActionListener { private static AltosDeviceDialog dialog; - private static altos_device value = null; + private static AltosDevice value = null; private JList list; - public static altos_device show (Component frameComp, String product) { + 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) { + if (devices != null && devices.length > 0) { value = null; dialog = new AltosDeviceDialog(frame, frameComp, devices, @@ -153,7 +153,7 @@ public class AltosDeviceDialog extends JDialog implements ActionListener { //Handle clicks on the Set and Cancel buttons. public void actionPerformed(ActionEvent e) { if ("select".equals(e.getActionCommand())) - AltosDeviceDialog.value = (altos_device)(list.getSelectedValue()); + AltosDeviceDialog.value = (AltosDevice)(list.getSelectedValue()); AltosDeviceDialog.dialog.setVisible(false); } diff --git a/ao-tools/altosui/AltosEeprom.java b/ao-tools/altosui/AltosEepromDownload.java index 4c537a89..02a71118 100644 --- a/ao-tools/altosui/AltosEeprom.java +++ b/ao-tools/altosui/AltosEepromDownload.java @@ -28,8 +28,10 @@ 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; @@ -40,30 +42,7 @@ import altosui.AltosEepromMonitor; import libaltosJNI.*; -public class AltosEeprom implements Runnable { - - 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'; - - 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; +public class AltosEepromDownload implements Runnable { static final String[] state_names = { "startup", @@ -105,7 +84,7 @@ public class AltosEeprom implements Runnable { } JFrame frame; - altos_device device; + AltosDevice device; AltosSerial serial_line; boolean remote; Thread eeprom_thread; @@ -125,7 +104,7 @@ public class AltosEeprom implements Runnable { AltosFile eeprom_name; LinkedList<String> eeprom_pending = new LinkedList<String>(); - serial_line.printf("v\n"); + serial_line.printf("\nc s\nv\n"); /* Pull the serial number out of the version information */ @@ -135,12 +114,13 @@ public class AltosEeprom implements Runnable { if (line.startsWith("serial-number")) { try { serial = Integer.parseInt(line.substring(13).trim()); - eeprom_pending.add(String.format("%s\n", line)); } 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; @@ -175,21 +155,21 @@ public class AltosEeprom implements Runnable { int a = values[5] + (values[6] << 8); int b = values[7] + (values[8] << 8); - if (cmd == AO_LOG_FLIGHT) { + if (cmd == Altos.AO_LOG_FLIGHT) { flight = b; monitor.set_flight(flight); } /* Monitor state transitions to update display */ - if (cmd == AO_LOG_STATE && a <= ao_flight_landed) { - if (a > ao_flight_pad) + 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 == AO_LOG_GPS_DATE) { + if (cmd == Altos.AO_LOG_GPS_DATE) { year = 2000 + (a & 0xff); month = (a >> 8) & 0xff; day = (b & 0xff); @@ -219,7 +199,7 @@ public class AltosEeprom implements Runnable { else eeprom_pending.add(log_line); - if (cmd == AO_LOG_STATE && a == ao_flight_landed) { + if (cmd == Altos.AO_LOG_STATE && a == Altos.ao_flight_landed) { done = true; } } @@ -245,10 +225,11 @@ public class AltosEeprom implements Runnable { if (remote) { serial_line.printf("m 0\n"); serial_line.set_channel(AltosPreferences.channel()); + serial_line.set_callsign(AltosPreferences.callsign()); serial_line.printf("p\n"); } - monitor = new AltosEepromMonitor(frame, ao_flight_boost, ao_flight_landed); + monitor = new AltosEepromMonitor(frame, Altos.ao_flight_boost, Altos.ao_flight_landed); monitor.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { eeprom_thread.interrupt(); @@ -269,9 +250,9 @@ public class AltosEeprom implements Runnable { serial_line.close(); } - public AltosEeprom(JFrame given_frame) { + public AltosEepromDownload(JFrame given_frame) { frame = given_frame; - device = AltosDeviceDialog.show(frame, null); + device = AltosDeviceDialog.show(frame, AltosDevice.Any); serial_line = new AltosSerial(); remote = false; @@ -279,7 +260,7 @@ public class AltosEeprom implements Runnable { if (device != null) { try { serial_line.open(device); - if (!device.getProduct().startsWith("TeleMetrum")) + if (!device.matchProduct(AltosDevice.TeleMetrum)) remote = true; eeprom_thread = new Thread(this); eeprom_thread.start(); diff --git a/ao-tools/altosui/AltosEepromMonitor.java b/ao-tools/altosui/AltosEepromMonitor.java index e110a354..b88fdd29 100644 --- a/ao-tools/altosui/AltosEepromMonitor.java +++ b/ao-tools/altosui/AltosEepromMonitor.java @@ -30,6 +30,7 @@ import java.util.concurrent.LinkedBlockingQueue; import altosui.AltosSerial; import altosui.AltosSerialMonitor; +import altosui.AltosRecord; import altosui.AltosTelemetry; import altosui.AltosState; import altosui.AltosDeviceDialog; diff --git a/ao-tools/altosui/AltosEepromReader.java b/ao-tools/altosui/AltosEepromReader.java new file mode 100644 index 00000000..c29fd90b --- /dev/null +++ b/ao-tools/altosui/AltosEepromReader.java @@ -0,0 +1,315 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.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 + */ +class AltosOrderedRecord extends AltosEepromRecord implements Comparable<AltosOrderedRecord> { + + int index; + + public AltosOrderedRecord(String line, int in_index, int prev_tick) + throws ParseException { + super(line); + int new_tick = tick | (prev_tick & ~0xffff); + if (new_tick < prev_tick) { + if (prev_tick - new_tick > 0x8000) + new_tick += 0x10000; + } + tick = new_tick; + 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 AltosEepromReader extends AltosReader { + + 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; + + AltosRecord state; + AltosOrderedRecord record; + + TreeSet<AltosOrderedRecord> records; + + Iterator<AltosOrderedRecord> record_iterator; + + int seen; + + int index; + + boolean last_reported; + + double ground_pres; + double ground_accel; + + int n_pad_samples; + + int gps_tick; + + boolean saw_boost; + + int boost_tick; + + public AltosRecord read() throws IOException, ParseException { + for (;;) { + if (record == null) { + if (!record_iterator.hasNext()) { + if (last_reported) + return null; + last_reported = true; + return state; + } + record = record_iterator.next(); + + if ((seen & seen_basic) == seen_basic && record.tick != state.tick) { + AltosRecord r = new AltosRecord(state); + r.time = (r.tick - boost_tick) / 100.0; + return r; + } + } + + state.tick = record.tick; + switch (record.cmd) { + case Altos.AO_LOG_FLIGHT: + state.ground_accel = record.a; + state.flight = record.b; + seen |= seen_flight; + break; + case Altos.AO_LOG_SENSOR: + state.accel = record.a; + state.pres = record.b; + if (state.state < Altos.ao_flight_boost) { + n_pad_samples++; + ground_pres += state.pres; + state.ground_pres = (int) (ground_pres / n_pad_samples); + state.flight_pres = state.ground_pres; + System.out.printf("ground pressure %d altitude %f\n", + record.b, state.altitude()); + ground_accel += state.accel; + state.ground_accel = (int) (ground_accel / n_pad_samples); + state.flight_accel = state.ground_accel; + } 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); + } + seen |= seen_sensor; + break; + case Altos.AO_LOG_TEMP_VOLT: + state.temp = record.a; + state.batt = record.b; + seen |= seen_temp_volt; + break; + case Altos.AO_LOG_DEPLOY: + state.drogue = record.a; + state.main = record.b; + seen |= seen_deploy; + break; + case Altos.AO_LOG_STATE: + System.out.printf("state %d\n", record.a); + state.state = record.a; + break; + case Altos.AO_LOG_GPS_TIME: + gps_tick = state.tick; + state.gps = new AltosGPS(); + 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 == 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; + 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: + break; + case Altos.AO_LOG_SOFTWARE_VERSION: + break; + } + record = null; + } + } + + public void write_comments(PrintStream out) { + Iterator<AltosOrderedRecord> iterator = records.iterator(); + 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 %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; + } + } + } + + /* + * 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 AltosEepromReader (FileInputStream input) { + state = new AltosRecord(); + state.state = Altos.ao_flight_pad; + state.accel_plus_g = 15758; + state.accel_minus_g = 16294; + seen = 0; + records = new TreeSet<AltosOrderedRecord>(); + + int index = 0; + int tick = 0; + + try { + for (;;) { + String line = AltosRecord.gets(input); + if (line == null) + break; + AltosOrderedRecord record = new AltosOrderedRecord(line, index++, tick); + if (record == null) + break; + tick = record.tick; + if (!saw_boost && record.cmd == Altos.AO_LOG_STATE && + record.a == Altos.ao_flight_boost) + { + saw_boost = true; + boost_tick = state.tick; + } + records.add(record); + } + } catch (IOException io) { + } catch (ParseException pe) { + } + record_iterator = records.iterator(); + try { + input.close(); + } catch (IOException ie) { + } + } +} diff --git a/ao-tools/altosui/AltosEepromRecord.java b/ao-tools/altosui/AltosEepromRecord.java new file mode 100644 index 00000000..86ac1fd2 --- /dev/null +++ b/ao-tools/altosui/AltosEepromRecord.java @@ -0,0 +1,110 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.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; + public int a; + public int b; + String data; + public boolean tick_valid; + + public AltosEepromRecord (String line) throws ParseException { + tick_valid = false; + tick = 0; + a = 0; + b = 0; + data = null; + if (line == null) { + cmd = Altos.AO_LOG_INVALID; + } else { + String[] tokens = line.split("\\s+"); + + if (tokens[0].length() == 1) { + if (tokens.length != 4) + throw new ParseException(line, 0); + 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; + } + } + } + +} diff --git a/ao-tools/altosui/AltosFlightInfoTableModel.java b/ao-tools/altosui/AltosFlightInfoTableModel.java new file mode 100644 index 00000000..2a22e3e5 --- /dev/null +++ b/ao-tools/altosui/AltosFlightInfoTableModel.java @@ -0,0 +1,81 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosFlightInfoTableModel extends AbstractTableModel { + private String[] columnNames = {"Field", "Value"}; + + class InfoLine { + String name; + String value; + + public InfoLine(String n, String v) { + name = n; + value = v; + } + } + + private ArrayList<InfoLine> rows = new ArrayList<InfoLine>(); + + public int getColumnCount() { return columnNames.length; } + public String getColumnName(int col) { return columnNames[col]; } + + public int getRowCount() { return 20; } + + int current_row = 0; + int prev_num_rows = 0; + + public Object getValueAt(int row, int col) { + if (row >= rows.size()) + return ""; + if (col == 0) + return rows.get(row).name; + else + return rows.get(row).value; + } + + public void resetRow() { + current_row = 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 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; + fireTableDataChanged(); + } +} diff --git a/ao-tools/altosui/AltosFlightStatusTableModel.java b/ao-tools/altosui/AltosFlightStatusTableModel.java new file mode 100644 index 00000000..4c24b6ac --- /dev/null +++ b/ao-tools/altosui/AltosFlightStatusTableModel.java @@ -0,0 +1,61 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.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/AltosGPS.java b/ao-tools/altosui/AltosGPS.java index f8eb5f48..b3ee67e8 100644 --- a/ao-tools/altosui/AltosGPS.java +++ b/ao-tools/altosui/AltosGPS.java @@ -23,49 +23,24 @@ import altosui.AltosParse; public class AltosGPS { - public class AltosGPSTime { - int year; - int month; - int day; - int hour; - int minute; - int second; - - public AltosGPSTime(String date, String time) 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]); - - 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]); - } - - public AltosGPSTime() { - year = month = day = 0; - hour = minute = second = 0; - } - - } - public class AltosGPSSat { int svid; int c_n0; } int nsat; - boolean gps_locked; - boolean gps_connected; - AltosGPSTime gps_time; + 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 */ @@ -77,27 +52,47 @@ public class AltosGPS { AltosGPSSat[] cc_gps_sat; /* tracking data */ + void ParseGPSTime(String date, String time) 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]); + + 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) throws ParseException { AltosParse.word(words[i++], "GPS"); nsat = AltosParse.parse_int(words[i++]); AltosParse.word(words[i++], "sat"); - gps_connected = false; - gps_locked = false; + connected = false; + locked = false; lat = lon = 0; alt = 0; + ClearGPSTime(); if ((words[i]).equals("unlocked")) { - gps_connected = true; - gps_time = new AltosGPSTime(); + connected = true; i++; } else if ((words[i]).equals("not-connected")) { - gps_time = new AltosGPSTime(); i++; } else if (words.length >= 40) { - gps_locked = true; - gps_connected = true; + locked = true; + connected = true; - gps_time = new AltosGPSTime(words[i], words[i+1]); i += 2; + ParseGPSTime(words[i], words[i+1]); i += 2; lat = AltosParse.parse_coord(words[i++]); lon = AltosParse.parse_coord(words[i++]); alt = AltosParse.parse_int(AltosParse.strip_suffix(words[i++], "m")); @@ -108,7 +103,6 @@ public class AltosGPS { h_error = AltosParse.parse_int(AltosParse.strip_suffix(words[i++], "(herr)")); v_error = AltosParse.parse_int(AltosParse.strip_suffix(words[i++], "(verr)")); } else { - gps_time = new AltosGPSTime(); i++; } AltosParse.word(words[i++], "SAT"); @@ -125,4 +119,84 @@ public class AltosGPS { cc_gps_sat[chan].c_n0 = AltosParse.parse_int(words[i++]); } } + + 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; + 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/AltosPreferences.java b/ao-tools/altosui/AltosPreferences.java index 297e1aae..690f8f1e 100644 --- a/ao-tools/altosui/AltosPreferences.java +++ b/ao-tools/altosui/AltosPreferences.java @@ -37,6 +37,9 @@ class AltosPreferences { /* voice preference name */ final static String voicePreference = "VOICE"; + /* callsign preference name */ + final static String callsignPreference = "CALLSIGN"; + /* Default logdir is ~/TeleMetrum */ final static String logdirName = "TeleMetrum"; @@ -52,6 +55,8 @@ class AltosPreferences { /* Voice preference */ static boolean voice; + static String callsign; + public static void init(Component ui) { preferences = Preferences.userRoot().node("/org/altusmetrum/altosui"); @@ -71,6 +76,8 @@ class AltosPreferences { channel = preferences.getInt(channelPreference, 0); voice = preferences.getBoolean(voicePreference, true); + + callsign = preferences.get(callsignPreference,"N0CALL"); } static void flush_preferences() { @@ -154,4 +161,16 @@ class AltosPreferences { 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; + } } diff --git a/ao-tools/altosui/AltosReader.java b/ao-tools/altosui/AltosReader.java new file mode 100644 index 00000000..81779e2b --- /dev/null +++ b/ao-tools/altosui/AltosReader.java @@ -0,0 +1,28 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.io.*; +import java.util.*; +import java.text.*; + +import altosui.AltosRecord; + +public class AltosReader { + public AltosRecord read() throws IOException, ParseException { return null; } +} diff --git a/ao-tools/altosui/AltosRecord.java b/ao-tools/altosui/AltosRecord.java new file mode 100644 index 00000000..b670ee37 --- /dev/null +++ b/ao-tools/altosui/AltosRecord.java @@ -0,0 +1,209 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.lang.*; +import java.text.*; +import java.util.HashMap; +import java.io.*; +import altosui.AltosConvert; +import altosui.AltosGPS; + +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 pressure() { + return barometer_to_pressure(flight_pres); + } + + public double ground_pressure() { + return barometer_to_pressure(ground_pres); + } + + public double altitude() { + return AltosConvert.pressure_to_altitude(pressure()); + } + + public double ground_altitude() { + return AltosConvert.pressure_to_altitude(ground_pressure()); + } + + public double height() { + return 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 (accel_plus_g - 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/AltosSerial.java b/ao-tools/altosui/AltosSerial.java index efa63f68..ba00b55e 100644 --- a/ao-tools/altosui/AltosSerial.java +++ b/ao-tools/altosui/AltosSerial.java @@ -154,6 +154,11 @@ public class AltosSerial implements Runnable { printf("m 0\nc r %d\nm 1\n", channel); } + public void set_callsign(String callsign) { + if (altos != null) + printf ("c c %s\n", callsign); + } + public AltosSerial() { altos = null; input_thread = null; diff --git a/ao-tools/altosui/AltosState.java b/ao-tools/altosui/AltosState.java index 9aa10a08..deeb4c77 100644 --- a/ao-tools/altosui/AltosState.java +++ b/ao-tools/altosui/AltosState.java @@ -16,16 +16,16 @@ */ /* - * Track flight state from telemetry data stream + * Track flight state from telemetry or eeprom data stream */ package altosui; -import altosui.AltosTelemetry; +import altosui.AltosRecord; import altosui.AltosGPS; public class AltosState { - AltosTelemetry data; + AltosRecord data; /* derived data */ @@ -71,27 +71,25 @@ public class AltosState { double speak_altitude; - void init (AltosTelemetry cur, AltosState prev_state) { + void init (AltosRecord cur, AltosState prev_state) { int i; - AltosTelemetry prev; - double accel_counts_per_mss; + AltosRecord prev; data = cur; - ground_altitude = AltosConvert.cc_barometer_to_altitude(data.ground_pres); - height = AltosConvert.cc_barometer_to_altitude(data.flight_pres) - ground_altitude; + ground_altitude = data.ground_altitude(); + height = data.altitude() - ground_altitude; report_time = System.currentTimeMillis(); - accel_counts_per_mss = ((data.accel_minus_g - data.accel_plus_g) / 2.0) / 9.80665; - acceleration = (data.ground_accel - data.flight_accel) / accel_counts_per_mss; - speed = data.flight_vel / (accel_counts_per_mss * 100.0); - temperature = AltosConvert.cc_thermometer_to_temperature(data.temp); - drogue_sense = AltosConvert.cc_ignitor_to_voltage(data.drogue); - main_sense = AltosConvert.cc_ignitor_to_voltage(data.main); - battery = AltosConvert.cc_battery_to_voltage(data.batt); + 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(); + state = data.state; if (prev_state != null) { @@ -125,8 +123,13 @@ public class AltosState { time_change = 0; } - if (state == AltosTelemetry.ao_flight_pad) { - if (data.gps != null && data.gps.gps_locked && data.gps.nsat >= 4) { + if (state == Altos.ao_flight_pad) { + if (data.gps == null) + System.out.printf("on pad, gps null\n"); + else + System.out.printf ("on pad gps lat %f lon %f locked %d nsat %d\n", + data.gps.lat, data.gps.lon, data.gps.locked ? 1 : 0, data.gps.nsat); + if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) { npad++; if (npad > 1) { /* filter pad position */ @@ -149,8 +152,8 @@ public class AltosState { gps_ready = gps_waiting == 0; - ascent = (AltosTelemetry.ao_flight_boost <= state && - state <= AltosTelemetry.ao_flight_coast); + 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) @@ -161,9 +164,9 @@ public class AltosState { if (height > max_height) max_height = height; if (data.gps != null) { - if (gps == null || !gps.gps_locked || data.gps.gps_locked) + if (gps == null || !gps.locked || data.gps.locked) gps = data.gps; - if (npad > 0 && gps.gps_locked) + if (npad > 0 && gps.locked) from_pad = new AltosGreatCircle(pad_lat, pad_lon, gps.lat, gps.lon); } if (npad > 0) { @@ -173,11 +176,11 @@ public class AltosState { } } - public AltosState(AltosTelemetry cur) { + public AltosState(AltosRecord cur) { init(cur, null); } - public AltosState (AltosTelemetry cur, AltosState prev) { + public AltosState (AltosRecord cur, AltosState prev) { init(cur, prev); } } diff --git a/ao-tools/altosui/AltosTelemetry.java b/ao-tools/altosui/AltosTelemetry.java index e13b42e2..af29b8c0 100644 --- a/ao-tools/altosui/AltosTelemetry.java +++ b/ao-tools/altosui/AltosTelemetry.java @@ -21,6 +21,7 @@ import java.lang.*; import java.text.*; import java.util.HashMap; import altosui.AltosConvert; +import altosui.AltosRecord; import altosui.AltosGPS; /* @@ -51,72 +52,9 @@ import altosui.AltosGPS; * 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 { - int version; - String callsign; - int serial; - int flight; - int rssi; - int status; - String 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; - - public static final int ao_flight_startup = 0; - public static final int ao_flight_idle = 1; - public static final int ao_flight_pad = 2; - public static final int ao_flight_boost = 3; - public static final int ao_flight_fast = 4; - public static final int ao_flight_coast = 5; - public static final int ao_flight_drogue = 6; - public static final int ao_flight_main = 7; - public static final int ao_flight_landed = 8; - public static final int ao_flight_invalid = 9; - - static HashMap<String,Integer> states = new HashMap<String,Integer>(); - { - states.put("startup", ao_flight_startup); - states.put("idle", ao_flight_idle); - states.put("pad", ao_flight_pad); - states.put("boost", ao_flight_boost); - states.put("fast", ao_flight_fast); - states.put("coast", ao_flight_coast); - states.put("drogue", ao_flight_drogue); - states.put("main", ao_flight_main); - states.put("landed", ao_flight_landed); - states.put("invalid", ao_flight_invalid); - } - - public int state() { - if (states.containsKey(state)) - return states.get(state); - return ao_flight_invalid; - } - - public double altitude() { - return AltosConvert.cc_pressure_to_altitude(AltosConvert.cc_barometer_to_pressure(pres)); - } - - public double pad_altitude() { - return AltosConvert.cc_pressure_to_altitude(AltosConvert.cc_barometer_to_pressure(ground_pres)); - } - +public class AltosTelemetry extends AltosRecord { public AltosTelemetry(String line) throws ParseException { String[] words = line.split("\\s+"); - int i = 0; AltosParse.word (words[i++], "VERSION"); @@ -138,7 +76,7 @@ public class AltosTelemetry { status = AltosParse.parse_hex(words[i++]); AltosParse.word(words[i++], "STATE"); - state = words[i++]; + state = Altos.state(words[i++]); tick = AltosParse.parse_int(words[i++]); diff --git a/ao-tools/altosui/AltosTelemetryReader.java b/ao-tools/altosui/AltosTelemetryReader.java new file mode 100644 index 00000000..f1f6788c --- /dev/null +++ b/ao-tools/altosui/AltosTelemetryReader.java @@ -0,0 +1,71 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.io.*; +import java.util.*; +import java.text.*; +import altosui.AltosTelemetry; + +public class AltosTelemetryReader extends AltosReader { + LinkedList<AltosRecord> records; + + Iterator<AltosRecord> record_iterator; + + int boost_tick; + + public AltosRecord read() throws IOException, ParseException { + AltosRecord r; + if (!record_iterator.hasNext()) + return null; + + r = record_iterator.next(); + r.time = (r.tick - boost_tick) / 100.0; + return r; + } + + public AltosTelemetryReader (FileInputStream input) { + boolean saw_boost = false; + + records = new LinkedList<AltosRecord> (); + + try { + for (;;) { + String line = AltosRecord.gets(input); + if (line == null) + break; + AltosTelemetry record = new AltosTelemetry(line); + if (record == null) + break; + if (!saw_boost && record.state >= Altos.ao_flight_boost) + { + saw_boost = true; + boost_tick = record.tick; + } + records.add(record); + } + } catch (IOException io) { + } catch (ParseException pe) { + } + record_iterator = records.iterator(); + try { + input.close(); + } catch (IOException ie) { + } + } +} diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index 4994f093..49d1f11a 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -28,100 +28,22 @@ 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.AltosChannelMenu; import libaltosJNI.*; -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); - } -} - -class AltosFlightInfoTableModel extends AbstractTableModel { - private String[] columnNames = {"Field", "Value"}; - - class InfoLine { - String name; - String value; - - public InfoLine(String n, String v) { - name = n; - value = v; - } - } - - private ArrayList<InfoLine> rows = new ArrayList<InfoLine>(); - - public int getColumnCount() { return columnNames.length; } - public String getColumnName(int col) { return columnNames[col]; } - - public int getRowCount() { return 20; } - - public Object getValueAt(int row, int col) { - if (row >= rows.size()) - return ""; - if (col == 0) - return rows.get(row).name; - else - return rows.get(row).value; - } - - int current_row = 0; - int prev_num_rows = 0; - - public void resetRow() { - current_row = 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 finish() { - if (current_row > prev_num_rows) { - fireTableRowsInserted(prev_num_rows, current_row - 1); - prev_num_rows = current_row; - } - fireTableDataChanged(); - } -} - public class AltosUI extends JFrame { private int channel = -1; @@ -252,7 +174,7 @@ public class AltosUI extends JFrame { else info_add_row(0, "Ground state", "wait (%d)", state.gps_waiting); - info_add_row(0, "Rocket state", "%s", state.data.state); + 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); @@ -272,9 +194,9 @@ public class AltosUI extends JFrame { if (state.gps == null) { info_add_row(1, "GPS", "not available"); } else { - if (state.data.gps.gps_locked) + if (state.data.gps.locked) info_add_row(1, "GPS", " locked"); - else if (state.data.gps.gps_connected) + else if (state.data.gps.connected) info_add_row(1, "GPS", " unlocked"); else info_add_row(1, "GPS", " missing"); @@ -309,13 +231,13 @@ public class AltosUI extends JFrame { info_add_row(1, "Pad GPS alt", "%6.0f m", state.pad_alt); } info_add_row(1, "GPS date", "%04d-%02d-%02d", - state.gps.gps_time.year, - state.gps.gps_time.month, - state.gps.gps_time.day); + state.gps.year, + state.gps.month, + state.gps.day); info_add_row(1, "GPS time", " %02d:%02d:%02d", - state.gps.gps_time.hour, - state.gps.gps_time.minute, - state.gps.gps_time.second); + state.gps.hour, + state.gps.minute, + state.gps.second); int nsat_vis = 0; int c; @@ -344,7 +266,7 @@ public class AltosUI extends JFrame { return; /* reset the landing count once we hear about a new flight */ - if (state.state < AltosTelemetry.ao_flight_drogue) + if (state.state < Altos.ao_flight_drogue) reported_landing = 0; /* Shut up once the rocket is on the ground */ @@ -353,7 +275,7 @@ public class AltosUI extends JFrame { } /* If the rocket isn't on the pad, then report height */ - if (state.state > AltosTelemetry.ao_flight_pad) { + if (state.state > Altos.ao_flight_pad) { voice.speak("%d meters", (int) (state.height + 0.5)); } else { reported_landing = 0; @@ -366,7 +288,7 @@ public class AltosUI extends JFrame { if (!state.ascent && (last || System.currentTimeMillis() - state.report_time >= 15000 || - state.state == AltosTelemetry.ao_flight_landed)) + state.state == Altos.ao_flight_landed)) { if (Math.abs(state.baro_speed) < 20 && state.height < 100) voice.speak("rocket landed safely"); @@ -400,13 +322,13 @@ public class AltosUI extends JFrame { private void tell(AltosState state, AltosState old_state) { if (old_state == null || old_state.state != state.state) { - voice.speak(state.data.state); - if ((old_state == null || old_state.state <= AltosTelemetry.ao_flight_boost) && - state.state > AltosTelemetry.ao_flight_boost) { + 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)); - } else if ((old_state == null || old_state.state < AltosTelemetry.ao_flight_drogue) && - state.state >= AltosTelemetry.ao_flight_drogue) { + } 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)); } @@ -423,7 +345,9 @@ public class AltosUI extends JFrame { class DisplayThread extends Thread { IdleThread idle_thread; - String read() throws InterruptedException { return null; } + String name; + + AltosRecord read() throws InterruptedException, ParseException { return null; } void close() { } @@ -440,18 +364,19 @@ public class AltosUI extends JFrame { info_finish(); idle_thread.start(); try { - while ((line = read()) != null) { + for (;;) { try { - AltosTelemetry t = new AltosTelemetry(line); + AltosRecord record = read(); + if (record == null) + break; old_state = state; - state = new AltosState(t, state); + state = new AltosState(record, state); update(state); show(state); tell(state, old_state); idle_thread.notice(state); } catch (ParseException pp) { - System.out.printf("Parse error on %s\n", line); - System.out.println("exception " + pp); + System.out.printf("Parse error: %d \"%s\"\n", pp.getErrorOffset(), pp.getMessage()); } } } catch (InterruptedException ee) { @@ -471,8 +396,8 @@ public class AltosUI extends JFrame { AltosSerial serial; LinkedBlockingQueue<String> telem; - String read() throws InterruptedException { - return telem.take(); + AltosRecord read() throws InterruptedException, ParseException { + return new AltosTelemetry(telem.take()); } void close() { @@ -484,17 +409,19 @@ public class AltosUI extends JFrame { serial = s; telem = new LinkedBlockingQueue<String>(); serial.add_monitor(telem); + name = "telemetry"; } } private void ConnectToDevice() { - altos_device device = AltosDeviceDialog.show(AltosUI.this, "TeleDongle"); + AltosDevice device = AltosDeviceDialog.show(AltosUI.this, AltosDevice.BaseStation); if (device != null) { try { serial_line.open(device); DeviceThread thread = new DeviceThread(serial_line); serial_line.set_channel(AltosPreferences.channel()); + serial_line.set_callsign(AltosPreferences.callsign()); run_display(thread); } catch (FileNotFoundException ee) { JOptionPane.showMessageDialog(AltosUI.this, @@ -515,61 +442,69 @@ public class AltosUI extends JFrame { stop_display(); } - String readline(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; + void ConfigureCallsign() { + String result; + result = JOptionPane.showInputDialog(AltosUI.this, + "Configure Callsign", + AltosPreferences.callsign()); + if (result != null) { + AltosPreferences.set_callsign(result); + if (serial_line != null) + serial_line.set_callsign(result); } - return null; } + void ConfigureTeleMetrum() { + new AltosConfig(AltosUI.this); + } /* * Open an existing telemetry file and replay it in realtime */ class ReplayThread extends DisplayThread { - FileInputStream replay; - String filename; + AltosReader reader; + String name; - ReplayThread(FileInputStream in, String name) { - replay = in; - filename = name; - } - - String read() { + public AltosRecord read() { try { - return readline(replay); - } catch (IOException ee) { + return reader.read(); + } catch (IOException ie) { JOptionPane.showMessageDialog(AltosUI.this, - filename, + name, "error reading", JOptionPane.ERROR_MESSAGE); + } catch (ParseException pe) { } return null; } - void close () { - try { - replay.close(); - } catch (IOException ee) { - } + public void close () { report(); } + public ReplayThread(AltosReader in_reader, String in_name) { + reader = in_reader; + } void update(AltosState state) throws InterruptedException { /* Make it run in realtime after the rocket leaves the pad */ - if (state.state > AltosTelemetry.ao_flight_pad) + if (state.state > Altos.ao_flight_pad) Thread.sleep((int) (Math.min(state.time_change,10) * 1000)); } } + class ReplayTelemetryThread extends ReplayThread { + ReplayTelemetryThread(FileInputStream in, String in_name) { + super(new AltosTelemetryReader(in), in_name); + } + + } + + class ReplayEepromThread extends ReplayThread { + ReplayEepromThread(FileInputStream in, String in_name) { + super(new AltosEepromReader(in), in_name); + } + } + Thread display_thread; private void stop_display() { @@ -590,8 +525,8 @@ public class AltosUI extends JFrame { private void Replay() { JFileChooser logfile_chooser = new JFileChooser(); - logfile_chooser.setDialogTitle("Select Telemetry File"); - logfile_chooser.setFileFilter(new FileNameExtensionFilter("Telemetry file", "telem")); + logfile_chooser.setDialogTitle("Select Flight Record File"); + logfile_chooser.setFileFilter(new FileNameExtensionFilter("Flight data file", "eeprom", "telem")); logfile_chooser.setCurrentDirectory(AltosPreferences.logdir()); int returnVal = logfile_chooser.showOpenDialog(AltosUI.this); @@ -602,7 +537,11 @@ public class AltosUI extends JFrame { String filename = file.getName(); try { FileInputStream replay = new FileInputStream(file); - ReplayThread thread = new ReplayThread(replay, filename); + DisplayThread thread; + if (filename.endsWith("eeprom")) + thread = new ReplayEepromThread(replay, filename); + else + thread = new ReplayTelemetryThread(replay, filename); run_display(thread); } catch (FileNotFoundException ee) { JOptionPane.showMessageDialog(AltosUI.this, @@ -617,7 +556,7 @@ public class AltosUI extends JFrame { * a TeleDongle over the packet link */ private void SaveFlightData() { - new AltosEeprom(AltosUI.this); + new AltosEepromDownload(AltosUI.this); } /* Create the AltosUI menus @@ -634,6 +573,22 @@ 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("Quit",KeyEvent.VK_Q); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.CTRL_MASK)); @@ -669,20 +624,22 @@ public class AltosUI extends JFrame { menu.addSeparator(); - item = new JMenuItem("Save Flight Data",KeyEvent.VK_S); + item = new JMenuItem("Set Callsign",KeyEvent.VK_S); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - SaveFlightData(); + ConfigureCallsign(); } }); + menu.add(item); - item = new JMenuItem("Replay",KeyEvent.VK_R); + item = new JMenuItem("Configure TeleMetrum device",KeyEvent.VK_T); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - Replay(); + ConfigureTeleMetrum(); } }); + menu.add(item); } // Log menu @@ -736,26 +693,16 @@ public class AltosUI extends JFrame { // Channel menu { - menu = new JMenu("Channel", true); - menu.setMnemonic(KeyEvent.VK_C); - menubar.add(menu); - ButtonGroup group = new ButtonGroup(); - - for (int c = 0; c <= 9; c++) { - radioitem = new JRadioButtonMenuItem(String.format("Channel %1d (%7.3fMHz)", c, - 434.550 + c * 0.1), - c == AltosPreferences.channel()); - radioitem.setActionCommand(String.format("%d", c)); - radioitem.addActionListener(new ActionListener() { + 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.add(radioitem); - group.add(radioitem); - } + }); + menu.setMnemonic(KeyEvent.VK_C); + menubar.add(menu); } this.setJMenuBar(menubar); diff --git a/ao-tools/altosui/Makefile b/ao-tools/altosui/Makefile index bae42c9a..63359fbb 100644 --- a/ao-tools/altosui/Makefile +++ b/ao-tools/altosui/Makefile @@ -2,19 +2,30 @@ CLASSPATH=classes:./*:/usr/share/java/* CLASSFILES=\ + Altos.class \ + AltosChannelMenu.class \ + AltosConfig.class \ + AltosConfigUI.class \ AltosConvert.class \ - AltosEeprom.class \ + AltosCSV.class \ + AltosEepromDownload.class \ AltosEepromMonitor.class \ + AltosEepromReader.class \ + AltosEepromRecord.class \ AltosFile.class \ + AltosFlightInfoTableModel.class \ + AltosFlightStatusTableModel.class \ AltosGPS.class \ AltosGreatCircle.class \ AltosLog.class \ AltosParse.class \ AltosPreferences.class \ + AltosRecord.class \ AltosSerialMonitor.class \ AltosSerial.class \ AltosState.class \ AltosTelemetry.class \ + AltosTelemetryReader.class \ AltosUI.class \ AltosDevice.class \ AltosDeviceDialog.class \ @@ -32,7 +43,7 @@ CLASSFILES=\ # en_us.jar \ # freetts.jar -JAVAFLAGS=-Xlint:unchecked +JAVAFLAGS=-Xlint:unchecked -Xlint:deprecation OS:=$(shell uname) |
