Merge branch 'master' of git://git.gag.com/fw/altos
authorAnthony Towns <aj@erisian.com.au>
Tue, 24 Aug 2010 06:02:31 +0000 (00:02 -0600)
committerAnthony Towns <aj@erisian.com.au>
Tue, 24 Aug 2010 06:02:31 +0000 (00:02 -0600)
44 files changed:
ao-tools/altosui/Altos.java [new file with mode: 0644]
ao-tools/altosui/AltosCSV.java [new file with mode: 0644]
ao-tools/altosui/AltosChannelMenu.java [new file with mode: 0644]
ao-tools/altosui/AltosConfig.java [new file with mode: 0644]
ao-tools/altosui/AltosConfigUI.java [new file with mode: 0644]
ao-tools/altosui/AltosConvert.java
ao-tools/altosui/AltosDevice.java
ao-tools/altosui/AltosDeviceDialog.java
ao-tools/altosui/AltosEeprom.java [deleted file]
ao-tools/altosui/AltosEepromDownload.java [new file with mode: 0644]
ao-tools/altosui/AltosEepromMonitor.java
ao-tools/altosui/AltosEepromReader.java [new file with mode: 0644]
ao-tools/altosui/AltosEepromRecord.java [new file with mode: 0644]
ao-tools/altosui/AltosFlightInfoTableModel.java [new file with mode: 0644]
ao-tools/altosui/AltosFlightStatusTableModel.java [new file with mode: 0644]
ao-tools/altosui/AltosGPS.java
ao-tools/altosui/AltosPreferences.java
ao-tools/altosui/AltosReader.java [new file with mode: 0644]
ao-tools/altosui/AltosRecord.java [new file with mode: 0644]
ao-tools/altosui/AltosSerial.java
ao-tools/altosui/AltosState.java
ao-tools/altosui/AltosTelemetry.java
ao-tools/altosui/AltosTelemetryReader.java [new file with mode: 0644]
ao-tools/altosui/AltosUI.java
ao-tools/altosui/Makefile
ao-tools/ao-dumplog/ao-dumplog.c
ao-tools/lib/cc-usb.c
ao-tools/lib/cc-usb.h
ao-tools/libaltos/Makefile
ao-tools/libaltos/cjnitest.c
ao-tools/libaltos/libaltos.c
ao-tools/libaltos/libaltos.h
src/Makefile.proto
src/ao-make-product.5c
src/ao.h
src/ao_packet_master.c
src/ao_packet_slave.c
src/ao_product.c
src/teledongle-v0.1/Makefile.defs
src/teledongle-v0.2/Makefile.defs
src/telemetrum-v0.1-sirf/Makefile.defs
src/telemetrum-v0.1-sky/Makefile.defs
src/telemetrum-v1.0/Makefile.defs
src/tidongle/Makefile.defs

diff --git a/ao-tools/altosui/Altos.java b/ao-tools/altosui/Altos.java
new file mode 100644 (file)
index 0000000..53359e2
--- /dev/null
@@ -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 (file)
index 0000000..2493675
--- /dev/null
@@ -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 (file)
index 0000000..504c13c
--- /dev/null
@@ -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 (file)
index 0000000..ac73e7c
--- /dev/null
@@ -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 (file)
index 0000000..1d8c579
--- /dev/null
@@ -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
index 3be0716c45bf4d39afdebddfe1fc26fe62e65b30..8cc1df277ecd1da2041d9c6b2d23223249635026 100644 (file)
@@ -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)
        {
index f488174cf7677e3dc345b1a6875b78d2f5e41d7a..3daf0742311b254ccfc35360391847c466c3b807 100644 (file)
@@ -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);
index c60bd7c3ee35a388f5733259e84d8e2d3e4946e4..3df4c6ebeb15c27e4176dc16ff1560bf5fdbe7b6 100644 (file)
@@ -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/AltosEeprom.java
deleted file mode 100644 (file)
index 4c537a8..0000000
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * 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.AltosTelemetry;
-import altosui.AltosState;
-import altosui.AltosDeviceDialog;
-import altosui.AltosPreferences;
-import altosui.AltosLog;
-import altosui.AltosVoice;
-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;
-
-       static final String[] state_names = {
-               "startup",
-               "idle",
-               "pad",
-               "boost",
-               "fast",
-               "coast",
-               "drogue",
-               "main",
-               "landed",
-               "invalid",
-       };
-
-       int[] ParseHex(String line) {
-               String[] tokens = line.split("\\s+");
-               int[] array = new int[tokens.length];
-
-               for (int i = 0; i < tokens.length; i++)
-                       try {
-                               array[i] = Integer.parseInt(tokens[i], 16);
-                       } catch (NumberFormatException ne) {
-                               return null;
-                       }
-               return array;
-       }
-
-       int checksum(int[] line) {
-               int     csum = 0x5a;
-               for (int i = 1; i < line.length; i++)
-                       csum += line[i];
-               return csum & 0xff;
-       }
-
-       void FlushPending(FileWriter file, LinkedList<String> pending) throws IOException {
-               while (!pending.isEmpty()) {
-                       file.write(pending.remove());
-               }
-       }
-
-       JFrame                  frame;
-       altos_device            device;
-       AltosSerial             serial_line;
-       boolean                 remote;
-       Thread                  eeprom_thread;
-       AltosEepromMonitor      monitor;
-
-       void CaptureLog() throws IOException, InterruptedException {
-               int                     serial = 0;
-               int                     block, state_block = 0;
-               int                     addr;
-               int                     flight = 0;
-               int                     year = 0, month = 0, day = 0;
-               int                     state = 0;
-               boolean                 done = false;
-               boolean                 want_file = false;
-               boolean                 any_valid;
-               FileWriter              eeprom_file = null;
-               AltosFile               eeprom_name;
-               LinkedList<String>      eeprom_pending = new LinkedList<String>();
-
-               serial_line.printf("v\n");
-
-               /* Pull the serial number out of the version information */
-
-               for (;;) {
-                       String  line = serial_line.get_reply();
-
-                       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;
-                               }
-                       }
-
-                       /* signals the end of the version info */
-                       if (line.startsWith("software-version"))
-                               break;
-               }
-               if (serial == 0)
-                       throw new IOException("no serial number found");
-
-               monitor.set_serial(serial);
-               /* Now scan the eeprom, reading blocks of data and converting to .eeprom file form */
-
-               state = 0; state_block = 0;
-               for (block = 0; !done && block < 511; block++) {
-                       serial_line.printf("e %x\n", block);
-                       any_valid = false;
-                       monitor.set_value(state_names[state], state, block - state_block);
-                       for (addr = 0; addr < 0x100;) {
-                               String  line = serial_line.get_reply();
-                               int[] values = ParseHex(line);
-
-                               if (values == null) {
-                                       System.out.printf("invalid line: %s\n", line);
-                               } else if (values[0] != addr) {
-                                       System.out.printf("data address out of sync at 0x%x\n",
-                                                         block * 256 + values[0]);
-                               } else if (checksum(values) != 0) {
-                                       System.out.printf("invalid checksum at 0x%x\n",
-                                                         block * 256 + values[0]);
-                               } else {
-                                       any_valid = true;
-                                       int     cmd = values[1];
-                                       int     tick = values[3] + (values[4] << 8);
-                                       int     a = values[5] + (values[6] << 8);
-                                       int     b = values[7] + (values[8] << 8);
-
-                                       if (cmd == 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)
-                                                       want_file = true;
-                                               if (a > state)
-                                                       state_block = block;
-                                               state = a;
-                                       }
-
-                                       if (cmd == AO_LOG_GPS_DATE) {
-                                               year = 2000 + (a & 0xff);
-                                               month = (a >> 8) & 0xff;
-                                               day = (b & 0xff);
-                                               want_file = true;
-                                       }
-
-                                       if (eeprom_file == null) {
-                                               if (serial != 0 && flight != 0 && want_file) {
-                                                       if (year != 0 && month != 0 && day != 0)
-                                                               eeprom_name = new AltosFile(year, month, day, serial, flight, "eeprom");
-                                                       else
-                                                               eeprom_name = new AltosFile(serial, flight, "eeprom");
-
-                                                       monitor.set_file(eeprom_name.getName());
-                                                       eeprom_file = new FileWriter(eeprom_name);
-                                                       if (eeprom_file != null) {
-                                                               FlushPending(eeprom_file, eeprom_pending);
-                                                               eeprom_pending = null;
-                                                       }
-                                               }
-                                       }
-
-                                       String log_line = String.format("%c %4x %4x %4x\n",
-                                                                       cmd, tick, a, b);
-                                       if (eeprom_file != null)
-                                               eeprom_file.write(log_line);
-                                       else
-                                               eeprom_pending.add(log_line);
-
-                                       if (cmd == AO_LOG_STATE && a == ao_flight_landed) {
-                                               done = true;
-                                       }
-                               }
-                               addr += 8;
-                       }
-                       if (!any_valid)
-                               done = true;
-               }
-               if (eeprom_file == null) {
-                       eeprom_name = new AltosFile(serial,flight,"eeprom");
-                       eeprom_file = new FileWriter(eeprom_name);
-                       if (eeprom_file != null) {
-                               FlushPending(eeprom_file, eeprom_pending);
-                       }
-               }
-               if (eeprom_file != null) {
-                       eeprom_file.flush();
-                       eeprom_file.close();
-               }
-       }
-
-       public void run () {
-               if (remote) {
-                       serial_line.printf("m 0\n");
-                       serial_line.set_channel(AltosPreferences.channel());
-                       serial_line.printf("p\n");
-               }
-
-               monitor = new AltosEepromMonitor(frame, ao_flight_boost, ao_flight_landed);
-               monitor.addActionListener(new ActionListener() {
-                               public void actionPerformed(ActionEvent e) {
-                                       eeprom_thread.interrupt();
-                               }
-                       });
-               try {
-                       CaptureLog();
-               } catch (IOException ee) {
-                       JOptionPane.showMessageDialog(frame,
-                                                     device.getPath(),
-                                                     ee.getLocalizedMessage(),
-                                                     JOptionPane.ERROR_MESSAGE);
-               } catch (InterruptedException ie) {
-               }
-               if (remote)
-                       serial_line.printf("~");
-               monitor.done();
-               serial_line.close();
-       }
-
-       public AltosEeprom(JFrame given_frame) {
-               frame = given_frame;
-               device = AltosDeviceDialog.show(frame, null);
-
-               serial_line = new AltosSerial();
-               remote = false;
-
-               if (device != null) {
-                       try {
-                               serial_line.open(device);
-                               if (!device.getProduct().startsWith("TeleMetrum"))
-                                       remote = true;
-                               eeprom_thread = new Thread(this);
-                               eeprom_thread.start();
-                       } catch (FileNotFoundException ee) {
-                               JOptionPane.showMessageDialog(frame,
-                                                             String.format("Cannot open device \"%s\"",
-                                                                           device.getPath()),
-                                                             "Cannot open target device",
-                                                             JOptionPane.ERROR_MESSAGE);
-                       } catch (IOException ee) {
-                               JOptionPane.showMessageDialog(frame,
-                                                             device.getPath(),
-                                                             ee.getLocalizedMessage(),
-                                                             JOptionPane.ERROR_MESSAGE);
-                       }
-               }
-       }
-}
diff --git a/ao-tools/altosui/AltosEepromDownload.java b/ao-tools/altosui/AltosEepromDownload.java
new file mode 100644 (file)
index 0000000..02a7111
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * 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.AltosEepromMonitor;
+
+import libaltosJNI.*;
+
+public class AltosEepromDownload implements Runnable {
+
+       static final String[] state_names = {
+               "startup",
+               "idle",
+               "pad",
+               "boost",
+               "fast",
+               "coast",
+               "drogue",
+               "main",
+               "landed",
+               "invalid",
+       };
+
+       int[] ParseHex(String line) {
+               String[] tokens = line.split("\\s+");
+               int[] array = new int[tokens.length];
+
+               for (int i = 0; i < tokens.length; i++)
+                       try {
+                               array[i] = Integer.parseInt(tokens[i], 16);
+                       } catch (NumberFormatException ne) {
+                               return null;
+                       }
+               return array;
+       }
+
+       int checksum(int[] line) {
+               int     csum = 0x5a;
+               for (int i = 1; i < line.length; i++)
+                       csum += line[i];
+               return csum & 0xff;
+       }
+
+       void FlushPending(FileWriter file, LinkedList<String> pending) throws IOException {
+               while (!pending.isEmpty()) {
+                       file.write(pending.remove());
+               }
+       }
+
+       JFrame                  frame;
+       AltosDevice             device;
+       AltosSerial             serial_line;
+       boolean                 remote;
+       Thread                  eeprom_thread;
+       AltosEepromMonitor      monitor;
+
+       void CaptureLog() throws IOException, InterruptedException {
+               int                     serial = 0;
+               int                     block, state_block = 0;
+               int                     addr;
+               int                     flight = 0;
+               int                     year = 0, month = 0, day = 0;
+               int                     state = 0;
+               boolean                 done = false;
+               boolean                 want_file = false;
+               boolean                 any_valid;
+               FileWriter              eeprom_file = null;
+               AltosFile               eeprom_name;
+               LinkedList<String>      eeprom_pending = new LinkedList<String>();
+
+               serial_line.printf("\nc s\nv\n");
+
+               /* Pull the serial number out of the version information */
+
+               for (;;) {
+                       String  line = serial_line.get_reply();
+
+                       if (line.startsWith("serial-number")) {
+                               try {
+                                       serial = Integer.parseInt(line.substring(13).trim());
+                               } catch (NumberFormatException ne) {
+                                       serial = 0;
+                               }
+                       }
+
+                       eeprom_pending.add(String.format("%s\n", line));
+
+                       /* signals the end of the version info */
+                       if (line.startsWith("software-version"))
+                               break;
+               }
+               if (serial == 0)
+                       throw new IOException("no serial number found");
+
+               monitor.set_serial(serial);
+               /* Now scan the eeprom, reading blocks of data and converting to .eeprom file form */
+
+               state = 0; state_block = 0;
+               for (block = 0; !done && block < 511; block++) {
+                       serial_line.printf("e %x\n", block);
+                       any_valid = false;
+                       monitor.set_value(state_names[state], state, block - state_block);
+                       for (addr = 0; addr < 0x100;) {
+                               String  line = serial_line.get_reply();
+                               int[] values = ParseHex(line);
+
+                               if (values == null) {
+                                       System.out.printf("invalid line: %s\n", line);
+                               } else if (values[0] != addr) {
+                                       System.out.printf("data address out of sync at 0x%x\n",
+                                                         block * 256 + values[0]);
+                               } else if (checksum(values) != 0) {
+                                       System.out.printf("invalid checksum at 0x%x\n",
+                                                         block * 256 + values[0]);
+                               } else {
+                                       any_valid = true;
+                                       int     cmd = values[1];
+                                       int     tick = values[3] + (values[4] << 8);
+                                       int     a = values[5] + (values[6] << 8);
+                                       int     b = values[7] + (values[8] << 8);
+
+                                       if (cmd == Altos.AO_LOG_FLIGHT) {
+                                               flight = b;
+                                               monitor.set_flight(flight);
+                                       }
+
+                                       /* Monitor state transitions to update display */
+                                       if (cmd == Altos.AO_LOG_STATE && a <= Altos.ao_flight_landed) {
+                                               if (a > Altos.ao_flight_pad)
+                                                       want_file = true;
+                                               if (a > state)
+                                                       state_block = block;
+                                               state = a;
+                                       }
+
+                                       if (cmd == Altos.AO_LOG_GPS_DATE) {
+                                               year = 2000 + (a & 0xff);
+                                               month = (a >> 8) & 0xff;
+                                               day = (b & 0xff);
+                                               want_file = true;
+                                       }
+
+                                       if (eeprom_file == null) {
+                                               if (serial != 0 && flight != 0 && want_file) {
+                                                       if (year != 0 && month != 0 && day != 0)
+                                                               eeprom_name = new AltosFile(year, month, day, serial, flight, "eeprom");
+                                                       else
+                                                               eeprom_name = new AltosFile(serial, flight, "eeprom");
+
+                                                       monitor.set_file(eeprom_name.getName());
+                                                       eeprom_file = new FileWriter(eeprom_name);
+                                                       if (eeprom_file != null) {
+                                                               FlushPending(eeprom_file, eeprom_pending);
+                                                               eeprom_pending = null;
+                                                       }
+                                               }
+                                       }
+
+                                       String log_line = String.format("%c %4x %4x %4x\n",
+                                                                       cmd, tick, a, b);
+                                       if (eeprom_file != null)
+                                               eeprom_file.write(log_line);
+                                       else
+                                               eeprom_pending.add(log_line);
+
+                                       if (cmd == Altos.AO_LOG_STATE && a == Altos.ao_flight_landed) {
+                                               done = true;
+                                       }
+                               }
+                               addr += 8;
+                       }
+                       if (!any_valid)
+                               done = true;
+               }
+               if (eeprom_file == null) {
+                       eeprom_name = new AltosFile(serial,flight,"eeprom");
+                       eeprom_file = new FileWriter(eeprom_name);
+                       if (eeprom_file != null) {
+                               FlushPending(eeprom_file, eeprom_pending);
+                       }
+               }
+               if (eeprom_file != null) {
+                       eeprom_file.flush();
+                       eeprom_file.close();
+               }
+       }
+
+       public void run () {
+               if (remote) {
+                       serial_line.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, Altos.ao_flight_boost, Altos.ao_flight_landed);
+               monitor.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       eeprom_thread.interrupt();
+                               }
+                       });
+               try {
+                       CaptureLog();
+               } catch (IOException ee) {
+                       JOptionPane.showMessageDialog(frame,
+                                                     device.getPath(),
+                                                     ee.getLocalizedMessage(),
+                                                     JOptionPane.ERROR_MESSAGE);
+               } catch (InterruptedException ie) {
+               }
+               if (remote)
+                       serial_line.printf("~");
+               monitor.done();
+               serial_line.close();
+       }
+
+       public AltosEepromDownload(JFrame given_frame) {
+               frame = given_frame;
+               device = AltosDeviceDialog.show(frame, AltosDevice.Any);
+
+               serial_line = new AltosSerial();
+               remote = false;
+
+               if (device != null) {
+                       try {
+                               serial_line.open(device);
+                               if (!device.matchProduct(AltosDevice.TeleMetrum))
+                                       remote = true;
+                               eeprom_thread = new Thread(this);
+                               eeprom_thread.start();
+                       } catch (FileNotFoundException ee) {
+                               JOptionPane.showMessageDialog(frame,
+                                                             String.format("Cannot open device \"%s\"",
+                                                                           device.getPath()),
+                                                             "Cannot open target device",
+                                                             JOptionPane.ERROR_MESSAGE);
+                       } catch (IOException ee) {
+                               JOptionPane.showMessageDialog(frame,
+                                                             device.getPath(),
+                                                             ee.getLocalizedMessage(),
+                                                             JOptionPane.ERROR_MESSAGE);
+                       }
+               }
+       }
+}
index e110a354d11337fc75ffd7a1f302b28f5c57606e..b88fdd2924cc4f60e9e7e2f407e79b2c9c9acd1d 100644 (file)
@@ -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 (file)
index 0000000..c29fd90
--- /dev/null
@@ -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 (file)
index 0000000..86ac1fd
--- /dev/null
@@ -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 (file)
index 0000000..2a22e3e
--- /dev/null
@@ -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 (file)
index 0000000..4c24b6a
--- /dev/null
@@ -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);
+       }
+}
index f8eb5f4844150024244f3ef08211db8f75c62d9d..b3ee67e8369186dabbf392efc6a33ae06fa9ec75 100644 (file)
@@ -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;
+                       }
+               }
+       }
 }
index 297e1aae77ba0819a71af5ef53718a648cf2f727..690f8f1ed5e60eff6e2f858147d7c66fc5ae4e8f 100644 (file)
@@ -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 (file)
index 0000000..81779e2
--- /dev/null
@@ -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 (file)
index 0000000..b670ee3
--- /dev/null
@@ -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();
+       }
+}
index efa63f681819e2ed9f3d22ebfac66d79e47f7750..ba00b55e98d3b4674974e3dab4fa2d7fa323f588 100644 (file)
@@ -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;
index 9aa10a0887502ee80417b5adb44084c07c8c1a7b..deeb4c771dcec9fb825d918bea922f75c3ff4024 100644 (file)
  */
 
 /*
- * 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);
        }
 }
index e13b42e2df811465faf2c038a828f2d18e5adada..af29b8c04ef5e940e25dd1ca3e796e9dc895fbeb 100644 (file)
@@ -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 (file)
index 0000000..f1f6788
--- /dev/null
@@ -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) {
+               }
+       }
+}
index 4994f093dcbfed6eb80e7c9534a2715fea65843a..49d1f11a67f23e04c52d69ea768d9d0a9aff8393 100644 (file)
@@ -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);
index bae42c9ac14cd41d565db70cbe568bf03938abc0..63359fbbf4be82e3c01b970f3baf4c6370ebb203 100644 (file)
@@ -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)
 
index 440a02b5925d8b473f633a8a08cfd1638c66a159..57c432902c81151f366bb887752dd6bf8cba56ff 100644 (file)
@@ -30,12 +30,13 @@ static const struct option options[] = {
        { .name = "tty", .has_arg = 1, .val = 'T' },
        { .name = "device", .has_arg = 1, .val = 'D' },
        { .name = "remote", .has_arg = 1, .val = 'R' },
+       { .name = "channel", .has_arg = 1, .val = 'C' },
        { 0, 0, 0, 0},
 };
 
 static void usage(char *program)
 {
-       fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>] [-R]\n", program);
+       fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>] [--remote] [--channel <radio-channel>]\n", program);
        exit(1);
 }
 
@@ -75,6 +76,7 @@ main (int argc, char **argv)
        FILE            *out;
        char            *filename;
        int             serial_number = 0;
+       int             channel = 0;
        int             flight = 0;
        char            cmd;
        int             tick, a, b;
@@ -100,6 +102,9 @@ main (int argc, char **argv)
                case 'R':
                        remote = 1;
                        break;
+               case 'C':
+                       channel = atoi(optarg);
+                       break;
                default:
                        usage(argv[0]);
                        break;
@@ -119,7 +124,7 @@ main (int argc, char **argv)
        if (!cc)
                exit(1);
        if (remote)
-               cc_usb_open_remote(cc);
+               cc_usb_open_remote(cc, channel);
        /* send a 'version' command followed by a 'log' command */
        cc_usb_printf(cc, "v\n");
        out = NULL;
index 53a507412bbb9189077671bb8b2e05969ef40d82..1580c6d9ffee488f84e1910e3e1cc9ecec943938 100644 (file)
@@ -375,10 +375,11 @@ cc_usb_reset(struct cc_usb *cc)
 }
 
 void
-cc_usb_open_remote(struct cc_usb *cc)
+cc_usb_open_remote(struct cc_usb *cc, int channel)
 {
        if (!cc->remote) {
-               cc_usb_printf(cc, "\np\nE 0\n");
+               printf ("channel %d\n", channel);
+               cc_usb_printf(cc, "\nc r %d\np\nE 0\n", channel);
                do {
                        cc->in_count = cc->in_pos = 0;
                        _cc_usb_sync(cc, 100);
index 627f1b5d86ad10995f3e62aaae7e604c8bc8ef21..d3539281f83aa4001c22a696a8eb15c90e69160f 100644 (file)
@@ -63,7 +63,7 @@ void
 cc_usb_printf(struct cc_usb *cc, char *format, ...);
 
 void
-cc_usb_open_remote(struct cc_usb *cc);
+cc_usb_open_remote(struct cc_usb *cc, int channel);
 
 void
 cc_usb_close_remote(struct cc_usb *cc);
index fa5127ebc271e3ad755fa48ae6591637f41f7160..a251e54eee979381ab034dfa5f5a5368cd0cb29a 100644 (file)
@@ -1,27 +1,56 @@
 OS:=$(shell uname)
 
+#
+# Linux
+#
 ifeq ($(OS),Linux)
 
 JAVA_CFLAGS=-I/usr/lib/jvm/java-6-openjdk/include
 
 OS_CFLAGS=-DLINUX -DPOSIX_TTY $(JAVA_CFLAGS)
 
-LIBEXT=so
+OS_LDFLAGS=
+
+LIBNAME=libaltos.so
+EXEEXT=
 
 endif
 
+#
+# Darwin (Mac OS X)
+#
 ifeq ($(OS),Darwin)
 
-DARWIN_CFLAGS=\
+OS_CFLAGS=\
+       -DDARWIN -DPOSIX_TTY -arch i386 -arch x86_64 \
        --sysroot=/Developer/SDKs/MacOSX10.5.sdk -mmacosx-version-min=10.5 \
        -iwithsysroot /System/Library/Frameworks/JavaVM.framework/Headers \
        -iwithsysroot /System/Library/Frameworks/IOKit.framework/Headers \
        -iwithsysroot /System/Library/Frameworks/CoreFoundation.framework/Headers
-DARWIN_LIBS=\
+
+OS_LDFLAGS =\
        -framework IOKit -framework CoreFoundation
 
-OS_CFLAGS = $(DARWIN_CFLAGS) -DDARWIN -DPOSIX_TTY -arch i386 -arch x86_64
-LIBEXT=dylib
+LIBNAME=libaltos.dylib
+EXEEXT=
+
+endif
+
+#
+# Windows
+#
+ifneq (,$(findstring MINGW,$(OS)))
+
+CC=gcc
+
+OS_CFLAGS = -DWINDOWS -mconsole
+
+OS_LDFLAGS = -lgdi32 -luser32 -lcfgmgr32 -lsetupapi -lole32 \
+       -ladvapi32 -lcomctl32 -mconsole -Wl,--add-stdcall-alias
+
+LIBNAME=altos.dll
+
+EXEEXT=.exe
 
 endif
 
@@ -48,26 +77,30 @@ CLASSFILES = $(JAVAFILES:%.java=%.class)
 
 JAVAFLAGS=-Xlint:unchecked
 
-all: libaltos.$(LIBEXT) cjnitest $(CLASSFILES)
+CJNITEST=cjnitest$(EXEEXT)
+
+all: $(LIBNAME) $(CJNITEST) $(CLASSFILES)
 
 .java.class:
        javac -encoding UTF8 -classpath "$(CLASSPATH)" $(JAVAFLAGS) $*.java
 
 CFLAGS=$(OS_CFLAGS) -O0 -g -I.
 
+LDFLAGS=$(OS_LDFLAGS)
+
 HEADERS=libaltos.h
 SRCS = libaltos.c $(SWIG_WRAP)
 OBJS = $(SRCS:%.c=%.o)
 LIBS = $(DARWIN_LIBS)
 
-cjnitest: cjnitest.o $(OBJS)
+$(CJNITEST): cjnitest.o $(OBJS)
        cc -o $@ $(CFLAGS) cjnitest.o $(OBJS) $(LIBS)
 
-libaltos.$(LIBEXT): $(OBJS)
-       gcc -shared $(CFLAGS) -o $@ $(OBJS) $(LIBS)
+$(LIBNAME): $(OBJS)
+       gcc -shared $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(LDFLAGS)
 
 clean:
-       rm -f $(CLASSFILES) $(OBJS) libaltos.$(LIBEXT) cjnitest cjnitest.o
+       rm -f $(CLASSFILES) $(OBJS) $(LIBNAME) $(CJNITEST) cjnitest.o
        rm -rf swig_bindings libaltosJNI
 
 $(JNI_FILE): libaltos.i0 $(HEADERS)
index cd3898edadbd656178eadfbd70897d15484c160f..93d1f3763cf13cae66c35686bf11c6e2d48f31e6 100644 (file)
@@ -1,6 +1,15 @@
 #include <stdio.h>
 #include "libaltos.h"
 
+static void
+altos_puts(struct altos_file *file, char *string)
+{
+       char    c;
+
+       while ((c = *string++))
+               altos_putchar(file, c);
+}
+
 main ()
 {
        struct altos_device     device;
@@ -12,12 +21,20 @@ main ()
                struct altos_file       *file;
                int                     c;
 
+               printf ("%04x:%04x %-20s %4d %s\n", device.vendor, device.product,
+                       device.name, device.serial, device.path);
+
                file = altos_open(&device);
-               altos_putchar(file, '?'); altos_putchar(file, '\n'); altos_flush(file);
+               if (!file) {
+                       printf("altos_open failed\n");
+                       continue;
+               }
+               altos_puts(file,"v\nc s\n");
                while ((c = altos_getchar(file, 100)) >= 0) {
                        putchar (c);
                }
-               printf ("getchar returns %d\n", c);
+               if (c != LIBALTOS_TIMEOUT)
+                       printf ("getchar returns %d\n", c);
                altos_close(file);
        }
        altos_list_finish(list);
index 00fb2125443bc452b9807f8f106d81b726dd30b9..3e8485e450a141084a69f5432ab269ef6dcff628 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
+#define BUILD_DLL
 #include "libaltos.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
-static int
-match_dev(char *product, int serial, struct altos_device *device)
+PUBLIC int
+altos_init(void)
 {
-       struct altos_list       *list;
-       int                     i;
+       return LIBALTOS_SUCCESS;
+}
 
-       list = altos_list_start();
-       if (!list)
-               return 0;
-       while ((i = altos_list_next(list, device)) != 0) {
-               if (product && strncmp (product, device->product, strlen(product)) != 0)
-                       continue;
-               if (serial && serial != device->serial)
-                       continue;
-               break;
-       }
-       altos_list_finish(list);
-       return i;
+PUBLIC void
+altos_fini(void)
+{
 }
 
 #ifdef DARWIN
@@ -60,48 +52,9 @@ altos_strndup (const char *s, size_t n)
 #define altos_strndup strndup
 #endif
 
-int
-altos_find_by_arg(char *arg, char *default_product, struct altos_device *device)
-{
-       char    *product;
-       int     serial;
-       char    *end;
-       char    *colon;
-       int     ret;
-
-       if (arg)
-       {
-               /* check for <serial> */
-               serial = strtol(arg, &end, 0);
-               if (end != arg) {
-                       if (*end != '\0')
-                               return 0;
-                       product = NULL;
-               } else {
-                       /* check for <product>:<serial> */
-                       colon = strchr(arg, ':');
-                       if (colon) {
-                               product = altos_strndup(arg, colon - arg);
-                               serial = strtol(colon + 1, &end, 0);
-                               if (*end != '\0')
-                                       return 0;
-                       } else {
-                               product = arg;
-                               serial = 0;
-                       }
-               }
-       } else {
-               product = NULL;
-               serial = 0;
-       }
-       if (!product && default_product)
-               ret = match_dev(default_product, serial, device);
-       if (!ret)
-               ret = match_dev(product, serial, device);
-       if (product && product != arg)
-               free(product);
-       return ret;
-}
+/*
+ * Scan for Altus Metrum devices by looking through /sys
+ */
 
 #ifdef LINUX
 
@@ -216,7 +169,7 @@ struct altos_usbdev {
        char    *sys;
        char    *tty;
        char    *manufacturer;
-       char    *product;
+       char    *product_name;
        int     serial; /* AltOS always uses simple integer serial numbers */
        int     idProduct;
        int     idVendor;
@@ -286,7 +239,7 @@ usb_scan_device(char *sys)
                return NULL;
        usbdev->sys = strdup(sys);
        usbdev->manufacturer = load_string(sys, "manufacturer");
-       usbdev->product = load_string(sys, "product");
+       usbdev->product_name = load_string(sys, "product");
        usbdev->serial = load_dec(sys, "serial");
        usbdev->idProduct = load_hex(sys, "idProduct");
        usbdev->idVendor = load_hex(sys, "idVendor");
@@ -299,7 +252,7 @@ usbdev_free(struct altos_usbdev *usbdev)
 {
        free(usbdev->sys);
        free(usbdev->manufacturer);
-       free(usbdev->product);
+       free(usbdev->product_name);
        /* this can get used as a return value */
        if (usbdev->tty)
                free(usbdev->tty);
@@ -332,17 +285,6 @@ struct altos_list {
        int                     ndev;
 };
 
-int
-altos_init(void)
-{
-       return 1;
-}
-
-void
-altos_fini(void)
-{
-}
-
 struct altos_list *
 altos_list_start(void)
 {
@@ -366,7 +308,7 @@ altos_list_start(void)
                dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
                dev = usb_scan_device(dir);
                free(dir);
-               if (dev->idVendor == 0xfffe && dev->tty) {
+               if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) {
                        if (devs->dev)
                                devs->dev = realloc(devs->dev,
                                                    devs->ndev + 1 * sizeof (struct usbdev *));
@@ -387,7 +329,9 @@ altos_list_next(struct altos_list *list, struct altos_device *device)
        if (list->current >= list->ndev)
                return 0;
        dev = list->dev[list->current];
-       strcpy(device->product, dev->product);
+       strcpy(device->name, dev->product_name);
+       device->vendor = dev->idVendor;
+       device->product = dev->idProduct;
        strcpy(device->path, dev->tty);
        device->serial = dev->serial;
        list->current++;
@@ -447,17 +391,6 @@ get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
        return 0;
 }
 
-int
-altos_init(void)
-{
-       return 1;
-}
-
-void
-altos_fini(void)
-{
-}
-
 struct altos_list *
 altos_list_start(void)
 {
@@ -566,15 +499,16 @@ altos_open(struct altos_device *device)
 void
 altos_close(struct altos_file *file)
 {
-       close(file->fd);
-       file->fd = -1;
+       if (file->fd != -1) {
+               close(file->fd);
+               file->fd = -1;
+       }
 }
 
 void
 altos_free(struct altos_file *file)
 {
-       if (file->fd != -1)
-               close(file->fd);
+       altos_close(file);
        free(file);
 }
 
@@ -613,6 +547,8 @@ altos_flush(struct altos_file *file)
        }
 }
 
+#include <poll.h>
+
 int
 altos_getchar(struct altos_file *file, int timeout)
 {
@@ -622,9 +558,18 @@ altos_getchar(struct altos_file *file, int timeout)
                altos_flush(file);
                if (file->fd < 0)
                        return -EBADF;
+               if (timeout) {
+                       struct pollfd fd;
+                       int ret;
+                       fd.fd = file->fd;
+                       fd.events = POLLIN;
+                       ret = poll(&fd, 1, timeout);
+                       if (ret == 0)
+                               return LIBALTOS_TIMEOUT;
+               }
                ret = read(file->fd, file->in_data, USB_BUF_SIZE);
                if (ret < 0)
-                       return -errno;
+                       return LIBALTOS_ERROR;
                file->in_read = 0;
                file->in_used = ret;
        }
@@ -633,143 +578,255 @@ altos_getchar(struct altos_file *file, int timeout)
 
 #endif /* POSIX_TTY */
 
-#ifdef USE_LIBUSB
-#include <libusb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#ifdef WINDOWS
 
-libusb_context *usb_context;
+#include <windows.h>
+#include <setupapi.h>
 
-int altos_init(void)
-{
-       int     ret;
-       ret = libusb_init(&usb_context);
-       if (ret)
-               return ret;
-       libusb_set_debug(usb_context, 3);
-       return 0;
-}
+struct altos_list {
+       HDEVINFO        dev_info;
+       int             index;
+};
 
-void altos_fini(void)
-{
-       libusb_exit(usb_context);
-       usb_context = NULL;
-}
+#define USB_BUF_SIZE   64
 
-static libusb_device **list;
-static ssize_t num, current;
+struct altos_file {
+       HANDLE                          handle;
+       unsigned char                   out_data[USB_BUF_SIZE];
+       int                             out_used;
+       unsigned char                   in_data[USB_BUF_SIZE];
+       int                             in_used;
+       int                             in_read;
+};
 
-int altos_list_start(void)
+
+PUBLIC struct altos_list *
+altos_list_start(void)
 {
-       if (list)
-               altos_list_finish();
-       current = 0;
-       num = libusb_get_device_list(usb_context, &list);
-       if (num == 0) {
-               current = num = 0;
-               list = NULL;
-               return 0;
+       struct altos_list       *list = calloc(1, sizeof (struct altos_list));
+
+       if (!list)
+               return NULL;
+       list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
+                                            DIGCF_ALLCLASSES|DIGCF_PRESENT);
+       if (list->dev_info == INVALID_HANDLE_VALUE) {
+               printf("SetupDiGetClassDevs failed %d\n", GetLastError());
+               free(list);
+               return NULL;
        }
-       return 1;
+       list->index = 0;
+       return list;
 }
 
-int altos_list_next(struct altos_device *device)
-{
-       while (current < num) {
-               struct libusb_device_descriptor descriptor;
-               libusb_device *usb_device = list[current++];
-
-               if (libusb_get_device_descriptor(usb_device, &descriptor) == 0) {
-                       if (descriptor.idVendor == 0xfffe)
-                       {
-                               libusb_device_handle    *handle;
-                               if (libusb_open(usb_device, &handle) == 0) {
-                                       char    serial_number[256];
-                                       libusb_get_string_descriptor_ascii(handle, descriptor.iProduct,
-                                                                          device->product,
-                                                                          sizeof(device->product));
-                                       libusb_get_string_descriptor_ascii(handle, descriptor.iSerialNumber,
-                                                                          serial_number,
-                                                                          sizeof (serial_number));
-                                       libusb_close(handle);
-                                       device->serial = atoi(serial_number);
-                                       device->device = usb_device;
-                                       return 1;
-                               }
-                       }
+PUBLIC int
+altos_list_next(struct altos_list *list, struct altos_device *device)
+{
+       SP_DEVINFO_DATA dev_info_data;
+       char            port[128];
+       DWORD           port_len;
+       char            location[256];
+       char            symbolic[256];
+       DWORD           symbolic_len;
+       HKEY            dev_key;
+       int             vid, pid;
+       int             serial;
+       HRESULT         result;
+       DWORD           location_type;
+       DWORD           location_len;
+
+       dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
+       while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
+                                   &dev_info_data))
+       {
+               list->index++;
+
+               dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
+                                              DICS_FLAG_GLOBAL, 0, DIREG_DEV,
+                                              KEY_READ);
+               if (dev_key == INVALID_HANDLE_VALUE) {
+                       printf("cannot open device registry key\n");
+                       continue;
                }
+
+               /* Fetch symbolic name for this device and parse out
+                * the vid/pid/serial info */
+               symbolic_len = sizeof(symbolic);
+               result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
+                                        symbolic, &symbolic_len);
+               if (result != 0) {
+                       printf("cannot find SymbolicName value\n");
+                       RegCloseKey(dev_key);
+                       continue;
+               }
+               vid = pid = serial = 0;
+               sscanf(symbolic + sizeof("\\??\\USB#VID_") - 1,
+                      "%04X", &vid);
+               sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
+                      "%04X", &pid);
+               sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
+                      "%d", &serial);
+               if (!USB_IS_ALTUSMETRUM(vid, pid)) {
+                       printf("Not Altus Metrum symbolic name: %s\n",
+                              symbolic);
+                       RegCloseKey(dev_key);
+                       continue;
+               }
+
+               /* Fetch the com port name */
+               port_len = sizeof (port);
+               result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
+                                        port, &port_len);
+               RegCloseKey(dev_key);
+               if (result != 0) {
+                       printf("failed to get PortName\n");
+                       continue;
+               }
+
+               /* Fetch the 'location information' which is the device name,
+                * at least on XP */
+               location_len = sizeof (location);
+               if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
+                                                    &dev_info_data,
+                                                    SPDRP_LOCATION_INFORMATION,
+                                                    &location_type,
+                                                    (BYTE *)location,
+                                                    sizeof(location),
+                                                    &location_len))
+               {
+                       printf("Failed to get location\n");
+                       continue;
+               }
+               device->vendor = vid;
+               device->product = pid;
+               device->serial = serial;
+
+               if (strcasestr(location, "tele"))
+                       strcpy(device->name, location);
+               else
+                       strcpy(device->name, "");
+
+               strcpy(device->path, port);
+               printf ("product: %04x:%04x (%s)  path: %s serial %d\n",
+                       device->vendor, device->product, device->name,
+                       device->path, device->serial);
+               return 1;
        }
+       result = GetLastError();
+       if (result != ERROR_NO_MORE_ITEMS)
+               printf ("SetupDiEnumDeviceInfo failed error %d\n", result);
        return 0;
 }
 
-void altos_list_finish(void)
+PUBLIC void
+altos_list_finish(struct altos_list *list)
+{
+       SetupDiDestroyDeviceInfoList(list->dev_info);
+       free(list);
+}
+
+static int
+altos_fill(struct altos_file *file, int timeout)
 {
-       if (list) {
-               libusb_free_device_list(list, 1);
-               list = NULL;
+       DWORD   result;
+       DWORD   got;
+       COMMTIMEOUTS timeouts;
+
+       if (file->in_read < file->in_used)
+               return LIBALTOS_SUCCESS;
+       file->in_read = file->in_used = 0;
+
+       if (timeout) {
+               timeouts.ReadIntervalTimeout = MAXDWORD;
+               timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
+               timeouts.ReadTotalTimeoutConstant = timeout;
+       } else {
+               timeouts.ReadIntervalTimeout = 0;
+               timeouts.ReadTotalTimeoutMultiplier = 0;
+               timeouts.ReadTotalTimeoutConstant = 0;
+       }
+       timeouts.WriteTotalTimeoutMultiplier = 0;
+       timeouts.WriteTotalTimeoutConstant = 0;
+
+       if (!SetCommTimeouts(file->handle, &timeouts)) {
+               printf("SetCommTimeouts failed %d\n", GetLastError());
        }
+
+       if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, NULL)) {
+               result = GetLastError();
+               printf ("read failed %d\n", result);
+               return LIBALTOS_ERROR;
+               got = 0;
+       }
+       if (got)
+               return LIBALTOS_SUCCESS;
+       return LIBALTOS_TIMEOUT;
 }
 
-#define USB_BUF_SIZE   64
+PUBLIC int
+altos_flush(struct altos_file *file)
+{
+       DWORD   put;
+       char    *data = file->out_data;
+       char    used = file->out_used;
+       DWORD   result;
 
-struct altos_file {
-       struct libusb_device            *device;
-       struct libusb_device_handle     *handle;
-       int                             out_ep;
-       int                             out_size;
-       int                             in_ep;
-       int                             in_size;
-       unsigned char                   out_data[USB_BUF_SIZE];
-       int                             out_used;
-       unsigned char                   in_data[USB_BUF_SIZE];
-       int                             in_used;
-       int                             in_read;
-};
+       while (used) {
+               if (!WriteFile(file->handle, data, used, &put, NULL)) {
+                       result = GetLastError();
+                       printf ("write failed %d\n", result);
+                       return LIBALTOS_ERROR;
+               }
+               data += put;
+               used -= put;
+       }
+       file->out_used = 0;
+       return LIBALTOS_SUCCESS;
+}
 
-struct altos_file *
+PUBLIC struct altos_file *
 altos_open(struct altos_device *device)
 {
-       struct altos_file               *file;
-       struct libusb_device_handle     *handle;
-       if (libusb_open(device->device, &handle) == 0) {
-               int     ret;
+       struct altos_file       *file = calloc (sizeof (struct altos_file), 1);
+       char    full_name[64];
 
-               ret = libusb_claim_interface(handle, 1);
-#if 0
-               if (ret) {
-                       libusb_close(handle);
-                       return NULL;
-               }
-#endif
-               ret = libusb_detach_kernel_driver(handle, 1);
-#if 0
-               if (ret) {
-                       libusb_close(handle);
-                       return NULL;
-               }
-#endif
+       if (!file)
+               return NULL;
 
-               file = calloc(sizeof (struct altos_file), 1);
-               file->device = libusb_ref_device(device->device);
-               file->handle = handle;
-               /* XXX should get these from the endpoint descriptors */
-               file->out_ep = 4 | LIBUSB_ENDPOINT_OUT;
-               file->out_size = 64;
-               file->in_ep = 5 | LIBUSB_ENDPOINT_IN;
-               file->in_size = 64;
+       strcpy(full_name, "\\\\.\\");
+       strcat(full_name, device->path);
+       file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
+                                 0, NULL, OPEN_EXISTING,
+                                 FILE_ATTRIBUTE_NORMAL, NULL);
+       if (file->handle == INVALID_HANDLE_VALUE) {
+               free(file);
+               return NULL;
+       }
 
-               return file;
+       timeouts.ReadIntervalTimeout = MAXDWORD;
+       timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
+       timeouts.ReadTotalTimeoutConstant = 100;
+       timeouts.WriteTotalTimeoutMultiplier = 0;
+       timeouts.WriteTotalTimeoutConstant = 10000;
+       if (!SetCommTimeouts(file->handle, &timeouts)) {
+               printf("SetCommTimeouts failed %d\n", GetLastError());
        }
-       return NULL;
+
+       return file;
 }
 
-void
+PUBLIC void
 altos_close(struct altos_file *file)
 {
-       libusb_close(file->handle);
-       libusb_unref_device(file->device);
-       file->handle = NULL;
+       if (file->handle != INVALID_HANDLE_VALUE) {
+               CloseHandle(file->handle);
+               file->handle = INVALID_HANDLE_VALUE;
+       }
+}
+
+PUBLIC void
+altos_free(struct altos_file *file)
+{
+       altos_close(file);
        free(file);
 }
 
@@ -778,60 +835,32 @@ altos_putchar(struct altos_file *file, char c)
 {
        int     ret;
 
-       if (file->out_used == file->out_size) {
+       if (file->out_used == USB_BUF_SIZE) {
                ret = altos_flush(file);
                if (ret)
                        return ret;
        }
        file->out_data[file->out_used++] = c;
-       if (file->out_used == file->out_size)
+       if (file->out_used == USB_BUF_SIZE)
                return altos_flush(file);
-       return 0;
-}
-
-int
-altos_flush(struct altos_file *file)
-{
-       while (file->out_used) {
-               int     transferred;
-               int     ret;
-
-               ret = libusb_bulk_transfer(file->handle,
-                                          file->out_ep,
-                                          file->out_data,
-                                          file->out_used,
-                                          &transferred,
-                                          0);
-               if (ret)
-                       return ret;
-               if (transferred) {
-                       memmove(file->out_data, file->out_data + transferred,
-                               file->out_used - transferred);
-                       file->out_used -= transferred;
-               }
-       }
+       return LIBALTOS_SUCCESS;
 }
 
 int
 altos_getchar(struct altos_file *file, int timeout)
 {
+       int     ret;
        while (file->in_read == file->in_used) {
-               int     ret;
-               int     transferred;
-
-               altos_flush(file);
-               ret = libusb_bulk_transfer(file->handle,
-                                          file->in_ep,
-                                          file->in_data,
-                                          file->in_size,
-                                          &transferred,
-                                          (unsigned int) timeout);
+               ret = altos_flush(file);
+               if (ret)
+                       return ret;
+               if (file->handle == INVALID_HANDLE_VALUE)
+                       return LIBALTOS_ERROR;
+               ret = altos_fill(file, timeout);
                if (ret)
                        return ret;
-               file->in_read = 0;
-               file->in_used = transferred;
        }
        return file->in_data[file->in_read++];
 }
 
-#endif /* USE_LIBUSB */
+#endif
index 53026e0a755fa4d16609d27ec15c68602d15f47b..fe2c483c0cc2d8cc8735a388b7309d5449ac550b 100644 (file)
 #ifndef _LIBALTOS_H_
 #define _LIBALTOS_H_
 
+#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# ifndef BUILD_STATIC
+#  ifdef BUILD_DLL
+#   define PUBLIC __declspec(dllexport)
+#  else
+#   define PUBLIC __declspec(dllimport)
+#  endif
+# endif /* BUILD_STATIC */
+#endif
+
+#ifndef PUBLIC
+# define PUBLIC
+#endif
+
+#define USB_VENDOR_FSF                 0xfffe
+#define USB_VENDOR_ALTUSMETRUM         USB_VENDOR_FSF
+#define USB_PRODUCT_ALTUSMETRUM                0x000a
+#define USB_PRODUCT_TELEMETRUM         0x000b
+#define USB_PRODUCT_TELEDONGLE         0x000c
+#define USB_PRODUCT_TELETERRA          0x000d
+#define USB_PRODUCT_ALTUSMETRUM_MIN    0x000a
+#define USB_PRODUCT_ALTUSMETRUM_MAX    0x0013
+
+#define USB_IS_ALTUSMETRUM(v,p)        ((v) == USB_VENDOR_ALTUSMETRUM && \
+               (USB_PRODUCT_ALTUSMETRUM_MIN <= (p) && \
+                (p) <= USB_PRODUCT_ALTUSMETRUM_MAX))
+
 struct altos_device {
        //%immutable;
-       char                            product[256];
+       int                             vendor;
+       int                             product;
        int                             serial;
+       char                            name[256];
        char                            path[256];
        //%mutable;
 };
 
-int altos_init(void);
+#define LIBALTOS_SUCCESS       0
+#define LIBALTOS_ERROR         -1
+#define LIBALTOS_TIMEOUT       -2
+
+/* Returns 0 for success, < 0 on error */
+PUBLIC int
+altos_init(void);
 
-void altos_fini(void);
+PUBLIC void
+altos_fini(void);
 
-struct altos_list *
+PUBLIC struct altos_list *
 altos_list_start(void);
 
-int altos_list_next(struct altos_list *list, struct altos_device *device);
+/* Returns 1 for success, zero on end of list */
+PUBLIC int
+altos_list_next(struct altos_list *list, struct altos_device *device);
 
-void altos_list_finish(struct altos_list *list);
+PUBLIC void
+altos_list_finish(struct altos_list *list);
 
-struct altos_file *
+PUBLIC struct altos_file *
 altos_open(struct altos_device *device);
 
-void altos_close(struct altos_file *file);
+PUBLIC void
+altos_close(struct altos_file *file);
 
-void altos_free(struct altos_file *file);
+PUBLIC void
+altos_free(struct altos_file *file);
 
-int
+/* Returns < 0 for error */
+PUBLIC int
 altos_putchar(struct altos_file *file, char c);
 
-int
+/* Returns < 0 for error */
+PUBLIC int
 altos_flush(struct altos_file *file);
 
-int
+/* Returns < 0 for error or timeout. timeout of 0 == wait forever */
+PUBLIC int
 altos_getchar(struct altos_file *file, int timeout);
 
 #endif /* _LIBALTOS_H_ */
index 106333a677fd68bb29f41fddb219d309c5603ae3..eedb878a778e3f7b6875ff31ea3550d5eccc10f2 100644 (file)
@@ -214,7 +214,7 @@ all: ../$(PROG)
        nickle $< > $@
 
 ao_product.h: ao-make-product.5c
-       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -p $(PRODUCT) -v $(VERSION) > $@
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
 
 ao_product.rel: ao_product.c ao_product.h
        $(call quiet,CC) -c $(CFLAGS) -D PRODUCT_DEFS='\"ao_product.h\"' -o$@ $<
index 933032dd6344f3835bfe88d8066ba1c2c508d724..5f2eb8e895557a492405c016336cac51dd3eedea 100644 (file)
@@ -37,11 +37,19 @@ write_int(int a, string description)
        printf ("#define AO_%s_NUMBER %d\n\n", description, a);
 }
 
+void
+write_hex(int a, string description)
+{
+       printf ("/* %s */\n", description);
+       printf ("#define AO_%s_NUMBER 0x%04x\n\n", description, a);
+}
+
 string manufacturer = "altusmetrum.org";
 string product = "TeleMetrum";
 string version = "0.0";
 int serial = 1;
 int user_argind = 0;
+int id_product = 0x000a;
 
 argdesc argd = {
        .args = {
@@ -57,6 +65,12 @@ argdesc argd = {
                        .name = "product",
                        .expr_name = "prod",
                        .desc = "Product name." },
+               {
+                       .var = { .arg_int = &id_product },
+                       .abbr = 'i',
+                       .name = "id_product",
+                       .expr_name = "id_p",
+                       .desc = "Product ID." },
                {
                        .var = { .arg_int = &serial },
                        .abbr = 's',
@@ -82,6 +96,7 @@ main()
        write_ucs2(product, "iProduct");
        write_ucs2(sprintf("%06d", serial), "iSerial");
        write_int(serial, "iSerial");
+       write_hex(id_product, "idProduct");
        write_string(version, "iVersion");
 }
 
index 5dd756dac48e21671d1f66f0615cfc2abfdce8af..5f2b83394daf96eb78abd5f89bff2fb92ec35388 100644 (file)
--- a/src/ao.h
+++ b/src/ao.h
@@ -1034,7 +1034,7 @@ struct ao_fifo {
  * Packet-based command interface
  */
 
-#define AO_PACKET_MAX  8
+#define AO_PACKET_MAX          64
 #define AO_PACKET_SYN          (uint8_t) 0xff
 
 struct ao_packet {
@@ -1043,6 +1043,7 @@ struct ao_packet {
        uint8_t         seq;
        uint8_t         ack;
        uint8_t         d[AO_PACKET_MAX];
+       uint8_t         callsign[AO_MAX_CALLSIGN];
 };
 
 struct ao_packet_recv {
index ef86fa281a7f809e2c0ce38bdd6d45c823eaf710..72bb908a671e4e2f790f1f6492c340627e43468a 100644 (file)
@@ -77,12 +77,14 @@ ao_packet_master(void)
 {
        uint8_t status;
 
+       ao_config_get();
        ao_radio_set_packet();
        ao_tx_packet.addr = ao_serial_number;
        ao_tx_packet.len = AO_PACKET_SYN;
        ao_packet_master_time = ao_time();
        ao_packet_master_delay = AO_PACKET_MASTER_DELAY_SHORT;
        while (ao_packet_enable) {
+               memcpy(ao_tx_packet.callsign, ao_config.callsign, AO_MAX_CALLSIGN);
                ao_packet_send();
                if (ao_tx_packet.len)
                        ao_packet_master_busy();
index e03ebdc3a20cd90475ee00ea1610b3a3a6f28260..9b78767f55282b4e77c0f28f806124728151fccc 100644 (file)
@@ -27,8 +27,10 @@ ao_packet_slave(void)
        ao_tx_packet.len = AO_PACKET_SYN;
        while (ao_packet_enable) {
                status = ao_packet_recv();
-               if (status & AO_DMA_DONE)
+               if (status & AO_DMA_DONE) {
+                       memcpy(&ao_tx_packet.callsign, &ao_rx_packet.packet.callsign, AO_MAX_CALLSIGN);
                        ao_packet_send();
+               }
        }
        ao_exit();
 }
index b42e62c09a800c92644542a63795d59cb537c017..2bd0b59c870146865fc36fc5091b818f23b6b3c5 100644 (file)
@@ -40,7 +40,7 @@ const uint8_t ao_usb_descriptors [] =
        0x00,                   /*  bDeviceProtocol */
        AO_USB_CONTROL_SIZE,    /*  bMaxPacketSize */
        LE_WORD(0xFFFE),        /*  idVendor */
-       LE_WORD(0x000A),        /*  idProduct */
+       LE_WORD(AO_idProduct_NUMBER),   /*  idProduct */
        LE_WORD(0x0100),        /*  bcdDevice */
        0x01,                   /*  iManufacturer */
        0x02,                   /*  iProduct */
index 06559b2881a1ed5ca71456ffeda948eb1e16636c..be7741d889798be64b6577c83ba54131fdb31493 100644 (file)
@@ -6,3 +6,4 @@ SRC = \
 
 PRODUCT=TeleDongle-v0.1
 PRODUCT_DEF=-DTELEDONGLE_V_0_1
+IDPRODUCT=0x000c
index 23694fdaaf6463283307dcf271817297440b5331..cbec7805df5b45f651ab6b4f57b2b0a08e645e98 100644 (file)
@@ -6,3 +6,4 @@ SRC = \
 
 PRODUCT=TeleDongle-v0.2
 PRODUCT_DEF=-DTELEDONGLE_V_0_2
+IDPRODUCT=0x000c
index 7306683eb340aa07ea7298a700c71b3acdeea07a..2ce6e6ed2cdf31b5c821b00054d23ae8fc3d8d07 100644 (file)
@@ -8,3 +8,4 @@ SRC = \
 
 PRODUCT=TeleMetrum-v0.1-SiRF
 PRODUCT_DEF=-DTELEMETRUM_V_0_1
+IDPRODUCT=0x000b
index df475baafa4c3110e9e96222dd67012779522d6a..098ac547aa8bebe5f735e6814f2f84f9afb29a62 100644 (file)
@@ -8,3 +8,4 @@ SRC = \
 
 PRODUCT=TeleMetrum-v0.1
 PRODUCT_DEF=-DTELEMETRUM_V_0_1
+IDPRODUCT=0x000b
index e3cc5eb2f80335b8593023c9e7882083195a067c..624ce6e84e5febd303816cdd358df31ce6c7fe92 100644 (file)
@@ -8,3 +8,4 @@ SRC = \
 
 PRODUCT=TeleMetrum-v1.0
 PRODUCT_DEF=-DTELEMETRUM_V_1_0
+IDPRODUCT=0x000b
index 5b910b9b3c39250b468a767428bc251846e3e953..fdd517327b55e0336a5938c4c2654fac8a975872 100644 (file)
@@ -6,3 +6,4 @@ SRC = \
 PRODUCT=TIDongle
 
 PRODUCT_DEF=-DTIDONGLE
+IDPRODUCT=0x000a