Merge remote branch 'origin/master' into new-packet-format
authorKeith Packard <keithp@keithp.com>
Fri, 27 Aug 2010 18:00:31 +0000 (11:00 -0700)
committerKeith Packard <keithp@keithp.com>
Fri, 27 Aug 2010 18:00:31 +0000 (11:00 -0700)
67 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/AltosCSVUI.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/AltosDebug.java [new file with mode: 0644]
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/AltosFlash.java [new file with mode: 0644]
ao-tools/altosui/AltosFlashUI.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/AltosGreatCircle.java
ao-tools/altosui/AltosHexfile.java [new file with mode: 0644]
ao-tools/altosui/AltosLogfileChooser.java [new file with mode: 0644]
ao-tools/altosui/AltosParse.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/AltosRomconfig.java [new file with mode: 0644]
ao-tools/altosui/AltosRomconfigUI.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_adc.c
src/ao_cmd.c
src/ao_config.c
src/ao_dma.c
src/ao_gps_report.c
src/ao_gps_skytraq.c
src/ao_log.c
src/ao_packet_master.c
src/ao_packet_slave.c
src/ao_product.c
src/ao_radio.c
src/ao_romconfig.c [new file with mode: 0644]
src/ao_serial.c
src/ao_timer.c
src/ao_usb.c
src/cc1111.h
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..07bd01a
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * 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;
+
+       static boolean isspace(int c) {
+               switch (c) {
+               case ' ':
+               case '\t':
+                       return true;
+               }
+               return false;
+       }
+
+       static boolean ishex(int c) {
+               if ('0' <= c && c <= '9')
+                       return true;
+               if ('a' <= c && c <= 'f')
+                       return true;
+               if ('A' <= c && c <= 'F')
+                       return true;
+               return false;
+       }
+
+       static boolean ishex(String s) {
+               for (int i = 0; i < s.length(); i++)
+                       if (!ishex(s.charAt(i)))
+                               return false;
+               return true;
+       }
+
+       static int fromhex(int c) {
+               if ('0' <= c && c <= '9')
+                       return c - '0';
+               if ('a' <= c && c <= 'f')
+                       return c - 'a' + 10;
+               if ('A' <= c && c <= 'F')
+                       return c - 'A' + 10;
+               return -1;
+       }
+
+       static int fromhex(String s) throws NumberFormatException {
+               int c, v = 0;
+               for (int i = 0; i < s.length(); i++) {
+                       c = s.charAt(i);
+                       if (!ishex(c)) {
+                               if (i == 0)
+                                       throw new NumberFormatException(String.format("invalid hex \"%s\"", s));
+                               return v;
+                       }
+                       v = v * 16 + fromhex(c);
+               }
+               return v;
+       }
+
+       static boolean isdec(int c) {
+               if ('0' <= c && c <= '9')
+                       return true;
+               return false;
+       }
+
+       static boolean isdec(String s) {
+               for (int i = 0; i < s.length(); i++)
+                       if (!isdec(s.charAt(i)))
+                               return false;
+               return true;
+       }
+
+       static int fromdec(int c) {
+               if ('0' <= c && c <= '9')
+                       return c - '0';
+               return -1;
+       }
+
+       static int fromdec(String s) throws NumberFormatException {
+               int c, v = 0;
+               int sign = 1;
+               for (int i = 0; i < s.length(); i++) {
+                       c = s.charAt(i);
+                       if (i == 0 && c == '-') {
+                               sign = -1;
+                       } else if (!isdec(c)) {
+                               if (i == 0)
+                                       throw new NumberFormatException(String.format("invalid number \"%s\"", s));
+                               return v;
+                       } else
+                               v = v * 10 + fromdec(c);
+               }
+               return v * sign;
+       }
+}
diff --git a/ao-tools/altosui/AltosCSV.java b/ao-tools/altosui/AltosCSV.java
new file mode 100644 (file)
index 0000000..4ce8e30
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * 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 java.text.*;
+import java.util.*;
+
+import altosui.AltosRecord;
+import altosui.AltosReader;
+
+public class AltosCSV {
+       File                    name;
+       PrintStream             out;
+       boolean                 header_written;
+       boolean                 seen_boost;
+       int                     boost_tick;
+       LinkedList<AltosRecord> pad_records;
+       AltosState              state;
+
+       static final int ALTOS_CSV_VERSION = 1;
+
+       /* Version 1 format:
+        *
+        * General info
+        *      version number
+        *      serial number
+        *      flight number
+        *      callsign
+        *      time (seconds since boost)
+        *      rssi
+        *
+        * Flight status
+        *      state
+        *      state name
+        *
+        * Basic sensors
+        *      acceleration (m/s²)
+        *      pressure (mBar)
+        *      altitude (m)
+        *      height (m)
+        *      accelerometer speed (m/s)
+        *      barometer speed (m/s)
+        *      temp (°C)
+        *      battery (V)
+        *      drogue (V)
+        *      main (V)
+        *
+        * GPS data
+        *      connected (1/0)
+        *      locked (1/0)
+        *      nsat (used for solution)
+        *      latitude (°)
+        *      longitude (°)
+        *      altitude (m)
+        *      year (e.g. 2010)
+        *      month (1-12)
+        *      day (1-31)
+        *      hour (0-23)
+        *      minute (0-59)
+        *      second (0-59)
+        *      from_pad_dist (m)
+        *      from_pad_azimuth (deg true)
+        *      from_pad_range (m)
+        *      from_pad_elevation (deg from horizon)
+        *
+        * GPS Sat data
+        *      hdop
+        *      C/N0 data for all 32 valid SDIDs
+        */
+
+       void write_general_header() {
+               out.printf("version serial flight call time rssi");
+       }
+
+       void write_general(AltosRecord record) {
+               out.printf("%s,%d,%d,%s,%8.2f,%4d",
+                          record.version, record.serial, record.flight, record.callsign,
+                          (double) record.time,
+                          record.rssi);
+       }
+
+       void write_flight_header() {
+               out.printf("state state_name");
+       }
+
+       void write_flight(AltosRecord record) {
+               out.printf("%d,%8s", record.state, record.state());
+       }
+
+       void write_basic_header() {
+               out.printf("acceleration pressure altitude height accel_speed baro_speed temperature battery_voltage drogue_voltage main_voltage");
+       }
+
+       void write_basic(AltosRecord record) {
+               out.printf("%8.2f,%10.2f,%8.2f,%8.2f,%8.2f,%8.2f,%5.1f,%5.2f,%5.2f,%5.2f",
+                          record.acceleration(),
+                          record.pressure(),
+                          record.altitude(),
+                          record.height(),
+                          record.accel_speed(),
+                          state.baro_speed,
+                          record.temperature(),
+                          record.battery_voltage(),
+                          record.drogue_voltage(),
+                          record.main_voltage());
+       }
+
+       void write_gps_header() {
+               out.printf("connected locked nsat latitude longitude altitude year month day hour minute second pad_dist pad_range pad_az pad_el");
+       }
+
+       void write_gps(AltosRecord record) {
+               AltosGPS        gps = record.gps;
+               if (gps == null)
+                       gps = new AltosGPS();
+
+               AltosGreatCircle from_pad = state.from_pad;
+               if (from_pad == null)
+                       from_pad = new AltosGreatCircle();
+
+               out.printf("%2d,%2d,%3d,%12.7f,%12.7f,%6d,%5d,%3d,%3d,%3d,%3d,%3d,%9.0f,%9.0f,%4.0f,%4.0f",
+                          gps.connected?1:0,
+                          gps.locked?1:0,
+                          gps.nsat,
+                          gps.lat,
+                          gps.lon,
+                          gps.alt,
+                          gps.year,
+                          gps.month,
+                          gps.day,
+                          gps.hour,
+                          gps.minute,
+                          gps.second,
+                          from_pad.distance,
+                          state.range,
+                          from_pad.bearing,
+                          state.elevation);
+       }
+
+       void write_header() {
+               out.printf("# "); write_general_header();
+               out.printf(" "); write_flight_header();
+               out.printf(" "); write_basic_header();
+               out.printf(" "); write_gps_header();
+               out.printf ("\n");
+       }
+
+       void write_one(AltosRecord record) {
+               state = new AltosState(record, state);
+               write_general(record); out.printf(",");
+               write_flight(record); out.printf(",");
+               write_basic(record); out.printf(",");
+               write_gps(record);
+               out.printf ("\n");
+       }
+
+       void flush_pad() {
+               while (!pad_records.isEmpty()) {
+                       write_one (pad_records.remove());
+               }
+       }
+
+       public void write(AltosRecord record) {
+               if (!header_written) {
+                       write_header();
+                       header_written = true;
+               }
+               if (!seen_boost) {
+                       if (record.state >= Altos.ao_flight_boost) {
+                               seen_boost = true;
+                               boost_tick = record.tick;
+                               flush_pad();
+                       }
+               }
+               if (seen_boost)
+                       write_one(record);
+               else
+                       pad_records.add(record);
+       }
+
+       public PrintStream out() {
+               return out;
+       }
+
+       public void close() {
+               if (!pad_records.isEmpty()) {
+                       boost_tick = pad_records.element().tick;
+                       flush_pad();
+               }
+               out.close();
+       }
+
+       public void write(AltosReader reader) {
+               AltosRecord     record;
+
+               reader.write_comments(out());
+               try {
+                       for (;;) {
+                               record = reader.read();
+                               if (record == null)
+                                       break;
+                               write(record);
+                       }
+               } catch (IOException ie) {
+                       System.out.printf("IOException\n");
+               } catch (ParseException pe) {
+                       System.out.printf("ParseException %s\n", pe.getMessage());
+               }
+       }
+
+       public AltosCSV(File in_name) throws FileNotFoundException {
+               name = in_name;
+               out = new PrintStream(name);
+               pad_records = new LinkedList<AltosRecord>();
+       }
+
+       public AltosCSV(String in_string) throws FileNotFoundException {
+               this(new File(in_string));
+       }
+}
diff --git a/ao-tools/altosui/AltosCSVUI.java b/ao-tools/altosui/AltosCSVUI.java
new file mode 100644 (file)
index 0000000..2d81236
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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.AltosLogfileChooser;
+import altosui.AltosCSV;
+
+public class AltosCSVUI
+       extends JDialog
+       implements Runnable, ActionListener
+{
+       JFrame          frame;
+       Thread          thread;
+       AltosReader     reader;
+       AltosCSV        writer;
+
+       public void run() {
+               AltosLogfileChooser     chooser;
+
+               chooser = new AltosLogfileChooser(frame);
+               reader = chooser.runDialog();
+               if (reader == null)
+                       return;
+               JFileChooser    csv_chooser;
+
+               File file = chooser.file();
+               String path = file.getPath();
+               int dot = path.lastIndexOf(".");
+               if (dot >= 0)
+                       path = path.substring(0,dot);
+               path = path.concat(".csv");
+               csv_chooser = new JFileChooser(path);
+               int ret = csv_chooser.showSaveDialog(frame);
+               if (ret == JFileChooser.APPROVE_OPTION) {
+                       try {
+                               writer = new AltosCSV(csv_chooser.getSelectedFile());
+                       } catch (FileNotFoundException ee) {
+                               JOptionPane.showMessageDialog(frame,
+                                                             file.getName(),
+                                                             "Cannot open file",
+                                                             JOptionPane.ERROR_MESSAGE);
+                       }
+                       writer.write(reader);
+                       reader.close();
+                       writer.close();
+               }
+       }
+
+       public void actionPerformed(ActionEvent e) {
+       }
+
+       public AltosCSVUI(JFrame in_frame) {
+               frame = in_frame;
+               thread = new Thread(this);
+               thread.start();
+       }
+}
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..605ccc8
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * 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;
+               c.ipady = 5;
+               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;
+               c.ipady = 5;
+               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;
+               c.ipady = 5;
+               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;
+               c.ipady = 5;
+               serial_value = new JLabel("");
+               pane.add(serial_value, c);
+
+               /* Main deploy */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 3;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               main_deploy_label = new JLabel("Main Deploy Altitude(m):");
+               pane.add(main_deploy_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 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 = 6;
+               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 = 0; c.gridy = 7;
+               c.gridwidth = 6;
+               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 = 0; c.gridy = 7;
+               c.gridwidth = 6;
+               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)
        {
diff --git a/ao-tools/altosui/AltosDebug.java b/ao-tools/altosui/AltosDebug.java
new file mode 100644 (file)
index 0000000..ca2e5a9
--- /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.lang.*;
+import java.io.*;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.LinkedList;
+import java.util.Iterator;
+import altosui.AltosSerial;
+import altosui.AltosRomconfig;
+
+public class AltosDebug extends AltosSerial {
+
+       public static final byte WR_CONFIG =            0x1d;
+       public static final byte RD_CONFIG =            0x24;
+       public static final byte CONFIG_TIMERS_OFF =            (1 << 3);
+       public static final byte CONFIG_DMA_PAUSE =             (1 << 2);
+       public static final byte CONFIG_TIMER_SUSPEND =         (1 << 1);
+       public static final byte SET_FLASH_INFO_PAGE =          (1 << 0);
+
+       public static final byte GET_PC =               0x28;
+       public static final byte READ_STATUS =          0x34;
+       public static final byte STATUS_CHIP_ERASE_DONE =       (byte) (1 << 7);
+       public static final byte STATUS_PCON_IDLE =             (1 << 6);
+       public static final byte STATUS_CPU_HALTED =            (1 << 5);
+       public static final byte STATUS_POWER_MODE_0 =          (1 << 4);
+       public static final byte STATUS_HALT_STATUS =           (1 << 3);
+       public static final byte STATUS_DEBUG_LOCKED =          (1 << 2);
+       public static final byte STATUS_OSCILLATOR_STABLE =     (1 << 1);
+       public static final byte STATUS_STACK_OVERFLOW =        (1 << 0);
+
+       public static final byte SET_HW_BRKPNT =        0x3b;
+       public static byte       HW_BRKPNT_N(byte n)    { return (byte) ((n) << 3); }
+       public static final byte HW_BRKPNT_N_MASK =             (0x3 << 3);
+       public static final byte HW_BRKPNT_ENABLE =             (1 << 2);
+
+       public static final byte HALT =                 0x44;
+       public static final byte RESUME =               0x4c;
+       public static       byte DEBUG_INSTR(byte n)    { return (byte) (0x54|(n)); }
+       public static final byte STEP_INSTR =           0x5c;
+       public static        byte STEP_REPLACE(byte n)  { return  (byte) (0x64|(n)); }
+       public static final byte GET_CHIP_ID =          0x68;
+
+
+       boolean debug_mode;
+
+       void ensure_debug_mode() {
+               if (!debug_mode) {
+                       printf("m 0\nD\n");
+                       flush_reply();
+                       debug_mode = true;
+               }
+       }
+
+       void dump_memory(String header, int address, byte[] bytes, int start, int len) {
+               System.out.printf("%s\n", header);
+               for (int j = 0; j < len; j++) {
+                       if ((j & 15) == 0) {
+                               if (j != 0)
+                                       System.out.printf("\n");
+                               System.out.printf ("%04x:", address + j);
+                       }
+                       System.out.printf(" %02x", bytes[start + j]);
+               }
+               System.out.printf("\n");
+       }
+
+       /*
+        * Write target memory
+        */
+       public void write_memory(int address, byte[] bytes, int start, int len) {
+               ensure_debug_mode();
+//             dump_memory("write_memory", address, bytes, start, len);
+               printf("O %x %x\n", len, address);
+               for (int i = 0; i < len; i++)
+                       printf("%02x", bytes[start + i]);
+       }
+
+       public void write_memory(int address, byte[] bytes) {
+               write_memory(address, bytes, 0, bytes.length);
+       }
+
+       /*
+        * Read target memory
+        */
+       public byte[] read_memory(int address, int length)
+               throws IOException, InterruptedException {
+               byte[]  data = new byte[length];
+
+               flush_reply();
+               ensure_debug_mode();
+               printf("I %x %x\n", length, address);
+               int i = 0;
+               int start = 0;
+               while (i < length) {
+                       String  line = get_reply().trim();
+                       if (!Altos.ishex(line) || line.length() % 2 != 0)
+                               throw new IOException(
+                                       String.format
+                                       ("Invalid reply \"%s\"", line));
+                       int this_time = line.length() / 2;
+                       for (int j = 0; j < this_time; j++)
+                               data[start + j] = (byte) ((Altos.fromhex(line.charAt(j*2)) << 4) +
+                                                 Altos.fromhex(line.charAt(j*2+1)));
+                       start += this_time;
+                       i += this_time;
+               }
+//             dump_memory("read_memory", address, data, 0, length);
+
+               return data;
+       }
+
+       /*
+        * Write raw bytes to the debug link using the 'P' command
+        */
+       public void write_bytes(byte[] bytes) throws IOException {
+               int i = 0;
+               ensure_debug_mode();
+               while (i < bytes.length) {
+                       int this_time = bytes.length - i;
+                       if (this_time > 8)
+                               this_time = 0;
+                       printf("P");
+                       for (int j = 0; j < this_time; j++)
+                               printf(" %02x", bytes[i+j]);
+                       printf("\n");
+                       i += this_time;
+               }
+       }
+
+       public void write_byte(byte b) throws IOException {
+               byte[] bytes = { b };
+               write_bytes(bytes);
+       }
+
+       /*
+        * Read raw bytes from the debug link using the 'G' command
+        */
+       public byte[] read_bytes(int length)
+               throws IOException, InterruptedException {
+
+               flush_reply();
+               ensure_debug_mode();
+               printf("G %x\n", length);
+               int i = 0;
+               byte[] data = new byte[length];
+               while (i < length) {
+                       String line = get_reply().trim();
+                       String tokens[] = line.split("\\s+");
+                       for (int j = 0; j < tokens.length; j++) {
+                               if (!Altos.ishex(tokens[j]) ||
+                                   tokens[j].length() != 2)
+                                       throw new IOException(
+                                               String.format
+                                               ("Invalid read_bytes reply \"%s\"", line));
+                               try {
+                                       data[i + j] = (byte) Integer.parseInt(tokens[j], 16);
+                               } catch (NumberFormatException ne) {
+                                       throw new IOException(
+                                               String.format
+                                               ("Invalid read_bytes reply \"%s\"", line));
+                               }
+                       }
+                       i += tokens.length;
+               }
+               return data;
+       }
+
+       public byte read_byte() throws IOException, InterruptedException {
+               return read_bytes(1)[0];
+       }
+
+       public byte debug_instr(byte[] instruction) throws IOException, InterruptedException {
+               byte[] command = new byte[1 + instruction.length];
+               command[0] = DEBUG_INSTR((byte) instruction.length);
+               for (int i = 0; i < instruction.length; i++)
+                       command[i+1] = instruction[i];
+               write_bytes(command);
+               return read_byte();
+       }
+
+       public byte resume() throws IOException, InterruptedException {
+               write_byte(RESUME);
+               return read_byte();
+       }
+
+       public int read_uint16() throws IOException, InterruptedException {
+               byte[] d = read_bytes(2);
+               return ((int) (d[0] & 0xff) << 8) | (d[1] & 0xff);
+       }
+
+       public int read_uint8()  throws IOException, InterruptedException {
+               byte[] d = read_bytes(1);
+               return (int) (d[0] & 0xff);
+       }
+
+       public int get_chip_id() throws IOException, InterruptedException {
+               write_byte(GET_CHIP_ID);
+               return read_uint16();
+       }
+
+       public int get_pc() throws IOException, InterruptedException {
+               write_byte(GET_PC);
+               return read_uint16();
+       }
+
+       public byte read_status() throws IOException, InterruptedException {
+               write_byte(READ_STATUS);
+               return read_byte();
+       }
+
+       static final byte LJMP                  = 0x02;
+
+       public void set_pc(int pc) throws IOException, InterruptedException {
+               byte high = (byte) (pc >> 8);
+               byte low = (byte) pc;
+               byte[] jump_mem = { LJMP, high, low };
+               debug_instr(jump_mem);
+       }
+
+       public boolean check_connection() throws IOException, InterruptedException {
+               byte reply = read_status();
+               if ((reply & STATUS_CHIP_ERASE_DONE) == 0)
+                       return false;
+               if ((reply & STATUS_PCON_IDLE) != 0)
+                       return false;
+               if ((reply & STATUS_POWER_MODE_0) == 0)
+                       return false;
+               return true;
+       }
+
+       public AltosRomconfig romconfig() {
+               try {
+                       byte[] bytes = read_memory(0xa0, 10);
+                       return new AltosRomconfig(bytes, 0);
+               } catch (IOException ie) {
+               } catch (InterruptedException ie) {
+               }
+               return new AltosRomconfig();
+       }
+
+       /*
+        * Reset target
+        */
+       public void reset() {
+               printf ("R\n");
+       }
+}
\ No newline at end of file
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..0705d44
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * 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> {
+
+       public 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 AltosOrderedRecord(int in_cmd, int in_tick, int in_a, int in_b, int in_index) {
+               super(in_cmd, in_tick, in_a, in_b);
+               index = in_index;
+       }
+
+       public int compareTo(AltosOrderedRecord o) {
+               int     tick_diff = tick - o.tick;
+               if (tick_diff != 0)
+                       return tick_diff;
+               return index - o.index;
+       }
+}
+
+public class 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;
+
+       boolean                 saw_gps_date;
+
+       boolean                 missing_gps_time;
+
+       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;
+                                       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:
+                               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;
+                               System.out.printf("GPS %2d:%02d:%02d%s%s%s %d\n",
+                                                 state.gps.hour, state.gps.minute, state.gps.second,
+                                                 state.gps.connected ? " connected" : "",
+                                                 state.gps.locked ? " locked" : "",
+                                                 state.gps.date_valid ? " date_valid" : "",
+                                                 state.gps.nsat);
+                               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:
+                               state.serial = record.a;
+                               break;
+                       case Altos.AO_LOG_SOFTWARE_VERSION:
+                               break;
+                       }
+                       record = null;
+               }
+       }
+
+       public void write_comments(PrintStream out) {
+               Iterator<AltosOrderedRecord>    iterator = records.iterator();
+               out.printf("# Comments\n");
+               while (iterator.hasNext()) {
+                       AltosOrderedRecord      record = iterator.next();
+                       switch (record.cmd) {
+                       case Altos.AO_LOG_CONFIG_VERSION:
+                               out.printf("# Config version: %s\n", record.data);
+                               break;
+                       case Altos.AO_LOG_MAIN_DEPLOY:
+                               out.printf("# Main deploy: %s\n", record.a);
+                               break;
+                       case Altos.AO_LOG_APOGEE_DELAY:
+                               out.printf("# Apogee delay: %s\n", record.a);
+                               break;
+                       case Altos.AO_LOG_RADIO_CHANNEL:
+                               out.printf("# Radio channel: %s\n", record.a);
+                               break;
+                       case Altos.AO_LOG_CALLSIGN:
+                               out.printf("# Callsign: %s\n", record.data);
+                               break;
+                       case Altos.AO_LOG_ACCEL_CAL:
+                               out.printf ("# Accel cal: %d %d\n", record.a, record.b);
+                               break;
+                       case Altos.AO_LOG_RADIO_CAL:
+                               out.printf ("# Radio cal: %d\n", record.a);
+                               break;
+                       case Altos.AO_LOG_MANUFACTURER:
+                               out.printf ("# Manufacturer: %s\n", record.data);
+                               break;
+                       case Altos.AO_LOG_PRODUCT:
+                               out.printf ("# Product: %s\n", record.data);
+                               break;
+                       case Altos.AO_LOG_SERIAL_NUMBER:
+                               out.printf ("# Serial number: %d\n", record.a);
+                               break;
+                       case Altos.AO_LOG_SOFTWARE_VERSION:
+                               out.printf ("# Software version: %s\n", record.data);
+                               break;
+                       }
+               }
+       }
+
+       /*
+        * Given an AO_LOG_GPS_TIME record with correct time, and one
+        * missing time, rewrite the missing time values with the good
+        * ones, assuming that the difference between them is 'diff' seconds
+        */
+       void update_time(AltosOrderedRecord good, AltosOrderedRecord bad) {
+
+               int diff = (bad.tick - good.tick + 50) / 100;
+
+               int hour = (good.a & 0xff);
+               int minute = (good.a >> 8);
+               int second = (good.b & 0xff);
+               int flags = (good.b >> 8);
+               int seconds = hour * 3600 + minute * 60 + second;
+
+               int new_seconds = seconds + diff;
+               if (new_seconds < 0)
+                       new_seconds += 24 * 3600;
+               int new_second = (new_seconds % 60);
+               int new_minutes = (new_seconds / 60);
+               int new_minute = (new_minutes % 60);
+               int new_hours = (new_minutes / 60);
+               int new_hour = (new_hours % 24);
+
+               System.out.printf("Synthesizing time good %2d:%02d:%02d bad %2d:%02d:%02d\n",
+                                 hour, minute, second,
+                                 new_hour, new_minute, new_second);
+
+               bad.a = new_hour + (new_minute << 8);
+               bad.b = new_second + (flags << 8);
+       }
+
+       /*
+        * Read the whole file, dumping records into a RB tree so
+        * we can enumerate them in time order -- the eeprom data
+        * are sometimes out of order with GPS data getting timestamps
+        * matching the first packet out of the GPS unit but not
+        * written until the final GPS packet has been received.
+        */
+       public 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>();
+
+               AltosOrderedRecord last_gps_time = null;
+
+               int index = 0;
+               int tick = 0;
+
+               boolean missing_time = false;
+
+               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 = tick;
+                               }
+
+                               /* Two firmware bugs caused the loss of some GPS data.
+                                * The flight date would never be recorded, and often
+                                * the flight time would get overwritten by another
+                                * record. Detect the loss of the GPS date and fix up the
+                                * missing time records
+                                */
+                               if (record.cmd == Altos.AO_LOG_GPS_DATE)
+                                       saw_gps_date = true;
+
+                               /* go back and fix up any missing time values */
+                               if (record.cmd == Altos.AO_LOG_GPS_TIME) {
+                                       last_gps_time = record;
+                                       if (missing_time) {
+                                               System.out.printf("Going back to clean up broken GPS time records\n");
+                                               Iterator<AltosOrderedRecord> iterator = records.iterator();
+                                               while (iterator.hasNext()) {
+                                                       AltosOrderedRecord old = iterator.next();
+                                                       if (old.cmd == Altos.AO_LOG_GPS_TIME) {
+                                                               System.out.printf("Old time record %d, %d\n", old.a, old.b);
+                                                       }
+                                                       if (old.cmd == Altos.AO_LOG_GPS_TIME &&
+                                                           old.a == -1 && old.b == -1)
+                                                       {
+                                                               update_time(record, old);
+                                                       }
+                                               }
+                                               missing_time = false;
+                                       }
+                               }
+
+                               if (record.cmd == Altos.AO_LOG_GPS_LAT) {
+                                       if (last_gps_time == null || last_gps_time.tick != record.tick) {
+                                               AltosOrderedRecord add_gps_time = new AltosOrderedRecord(Altos.AO_LOG_GPS_TIME,
+                                                                                                        record.tick,
+                                                                                                        -1, -1, index-1);
+                                               if (last_gps_time != null)
+                                                       update_time(last_gps_time, add_gps_time);
+                                               else {
+                                                       System.out.printf("early GPS missing time\n");
+                                                       missing_time = true;
+                                               }
+                                               records.add(add_gps_time);
+                                               record.index = index++;
+                                       }
+                               }
+                               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..4d0817a
--- /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.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;
+       public 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;
+                       }
+               }
+       }
+
+       public AltosEepromRecord(int in_cmd, int in_tick, int in_a, int in_b) {
+               tick_valid = true;
+               cmd = in_cmd;
+               tick = in_tick;
+               a = in_a;
+               b = in_b;
+       }
+}
diff --git a/ao-tools/altosui/AltosFlash.java b/ao-tools/altosui/AltosFlash.java
new file mode 100644 (file)
index 0000000..b701855
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * 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.AltosHexfile;
+
+public class AltosFlash {
+       File            file;
+       FileInputStream input;
+       AltosHexfile    image;
+       JFrame          frame;
+       AltosDevice     debug_dongle;
+       AltosDebug      debug;
+       AltosRomconfig  rom_config;
+       ActionListener  listener;
+       boolean         aborted;
+
+       static final byte MOV_direct_data       = (byte) 0x75;
+       static final byte MOV_DPTR_data16       = (byte) 0x90;
+       static final byte MOV_A_data            = (byte) 0x74;
+       static final byte MOVX_atDPTR_A         = (byte) 0xf0;
+       static final byte MOVX_A_atDPTR         = (byte) 0xe0;
+       static final byte INC_DPTR              = (byte) 0xa3;
+       static final byte TRAP                  = (byte) 0xa5;
+
+       static final byte JB                    = (byte) 0x20;
+
+       static final byte MOV_A_direct          = (byte) 0xe5;
+       static final byte MOV_direct1_direct2   = (byte) 0x85;
+       static final byte MOV_direct_A          = (byte) 0xf5;
+       static final byte MOV_R0_data           = (byte) (0x78 | 0);
+       static final byte MOV_R1_data           = (byte) (0x78 | 1);
+       static final byte MOV_R2_data           = (byte) (0x78 | 2);
+       static final byte MOV_R3_data           = (byte) (0x78 | 3);
+       static final byte MOV_R4_data           = (byte) (0x78 | 4);
+       static final byte MOV_R5_data           = (byte) (0x78 | 5);
+       static final byte MOV_R6_data           = (byte) (0x78 | 6);
+       static final byte MOV_R7_data           = (byte) (0x78 | 7);
+       static final byte DJNZ_R0_rel           = (byte) (0xd8 | 0);
+       static final byte DJNZ_R1_rel           = (byte) (0xd8 | 1);
+       static final byte DJNZ_R2_rel           = (byte) (0xd8 | 2);
+       static final byte DJNZ_R3_rel           = (byte) (0xd8 | 3);
+       static final byte DJNZ_R4_rel           = (byte) (0xd8 | 4);
+       static final byte DJNZ_R5_rel           = (byte) (0xd8 | 5);
+       static final byte DJNZ_R6_rel           = (byte) (0xd8 | 6);
+       static final byte DJNZ_R7_rel           = (byte) (0xd8 | 7);
+
+       static final byte P1DIR                 = (byte) 0xFE;
+       static final byte P1                    = (byte) 0x90;
+
+       /* flash controller */
+       static final byte FWT                   = (byte) 0xAB;
+       static final byte FADDRL                = (byte) 0xAC;
+       static final byte FADDRH                = (byte) 0xAD;
+       static final byte FCTL                  = (byte) 0xAE;
+       static final byte FCTL_BUSY             = (byte) 0x80;
+       static final byte FCTL_BUSY_BIT         = (byte) 7;
+       static final byte FCTL_SWBSY            = (byte) 0x40;
+       static final byte FCTL_SWBSY_BIT        = (byte) 6;
+       static final byte FCTL_CONTRD           = (byte) 0x10;
+       static final byte FCTL_WRITE            = (byte) 0x02;
+       static final byte FCTL_ERASE            = (byte) 0x01;
+       static final byte FWDATA                = (byte) 0xAF;
+
+       static final byte ACC                   = (byte) 0xE0;
+
+       /* offsets within the flash_page program */
+       static final int FLASH_ADDR_HIGH        = 8;
+       static final int FLASH_ADDR_LOW         = 11;
+       static final int RAM_ADDR_HIGH          = 13;
+       static final int RAM_ADDR_LOW           = 14;
+       static final int FLASH_WORDS_HIGH       = 16;
+       static final int FLASH_WORDS_LOW        = 18;
+       static final int FLASH_TIMING           = 21;
+
+       /* sleep mode control */
+       static final int SLEEP                  = (byte) 0xbe;
+       static final int  SLEEP_USB_EN          = (byte) 0x80;
+       static final int  SLEEP_XOSC_STB        = (byte) 0x40;
+       static final int  SLEEP_HFRC_STB        = (byte) 0x20;
+       static final int  SLEEP_RST_MASK        = (byte) 0x18;
+       static final int   SLEEP_RST_POWERON    = (byte) 0x00;
+       static final int   SLEEP_RST_EXTERNAL   = (byte) 0x10;
+       static final int   SLEEP_RST_WATCHDOG   = (byte) 0x08;
+       static final int  SLEEP_OSC_PD          = (byte) 0x04;
+       static final int  SLEEP_MODE_MASK       = (byte) 0x03;
+       static final int   SLEEP_MODE_PM0       = (byte) 0x00;
+       static final int   SLEEP_MODE_PM1       = (byte) 0x01;
+       static final int   SLEEP_MODE_PM2       = (byte) 0x02;
+       static final int   SLEEP_MODE_PM3       = (byte) 0x03;
+
+       /* clock controller */
+       static final byte CLKCON                = (byte) 0xC6;
+       static final byte  CLKCON_OSC32K        = (byte) 0x80;
+       static final byte  CLKCON_OSC           = (byte) 0x40;
+       static final byte  CLKCON_TICKSPD       = (byte) 0x38;
+       static final byte  CLKCON_CLKSPD        = (byte) 0x07;
+
+       static final byte[] flash_page_proto = {
+
+               MOV_direct_data, P1DIR, (byte) 0x02,
+               MOV_direct_data, P1,    (byte) 0xFF,
+
+               MOV_direct_data, FADDRH, 0,     /* FLASH_ADDR_HIGH */
+
+               MOV_direct_data, FADDRL, 0,     /* FLASH_ADDR_LOW */
+
+               MOV_DPTR_data16, 0, 0,          /* RAM_ADDR_HIGH, RAM_ADDR_LOW */
+
+               MOV_R7_data, 0,                 /* FLASH_WORDS_HIGH */
+
+               MOV_R6_data, 0,                 /* FLASH_WORDS_LOW */
+
+
+               MOV_direct_data, FWT, 0x20,     /* FLASH_TIMING */
+
+               MOV_direct_data, FCTL, FCTL_ERASE,
+/* eraseWaitLoop: */
+               MOV_A_direct,           FCTL,
+               JB, ACC|FCTL_BUSY_BIT, (byte) 0xfb,
+
+               MOV_direct_data, P1, (byte) 0xfd,
+
+               MOV_direct_data, FCTL, FCTL_WRITE,
+/* writeLoop: */
+               MOV_R5_data, 2,
+/* writeWordLoop: */
+               MOVX_A_atDPTR,
+               INC_DPTR,
+               MOV_direct_A, FWDATA,
+               DJNZ_R5_rel, (byte) 0xfa,               /* writeWordLoop */
+/* writeWaitLoop: */
+               MOV_A_direct, FCTL,
+               JB, ACC|FCTL_SWBSY_BIT, (byte) 0xfb,    /* writeWaitLoop */
+               DJNZ_R6_rel, (byte) 0xf1,               /* writeLoop */
+               DJNZ_R7_rel, (byte) 0xef,                       /* writeLoop */
+
+               MOV_direct_data, P1DIR, (byte) 0x00,
+               MOV_direct_data, P1,    (byte) 0xFF,
+               TRAP,
+       };
+
+       public byte[] make_flash_page(int flash_addr, int ram_addr, int byte_count) {
+               int flash_word_addr = flash_addr >> 1;
+               int flash_word_count = ((byte_count + 1) >> 1);
+
+               byte[] flash_page = new byte[flash_page_proto.length];
+               for (int i = 0; i < flash_page.length; i++)
+                       flash_page[i] = flash_page_proto[i];
+
+               flash_page[FLASH_ADDR_HIGH]  = (byte) (flash_word_addr >> 8);
+               flash_page[FLASH_ADDR_LOW]   = (byte) (flash_word_addr);
+               flash_page[RAM_ADDR_HIGH]    = (byte) (ram_addr >> 8);
+               flash_page[RAM_ADDR_LOW]     = (byte) (ram_addr);
+
+               byte flash_words_low = (byte) (flash_word_count);
+               byte flash_words_high = (byte) (flash_word_count >> 8);
+               /* the flashing code has a minor 'bug' */
+               if (flash_words_low != 0)
+                       flash_words_high++;
+
+               flash_page[FLASH_WORDS_HIGH] = (byte) flash_words_high;
+               flash_page[FLASH_WORDS_LOW]  = (byte) flash_words_low;
+               return flash_page;
+       }
+
+       static byte[] set_clkcon_fast = {
+               MOV_direct_data, CLKCON, 0x00
+       };
+
+       static byte[] get_sleep = {
+               MOV_A_direct, SLEEP
+       };
+
+       public void clock_init() throws IOException, InterruptedException {
+               debug.debug_instr(set_clkcon_fast);
+
+               byte    status;
+               do {
+                       status = debug.debug_instr(get_sleep);
+               } while ((status & SLEEP_XOSC_STB) == 0);
+       }
+
+       void action(String s, int percent) {
+               if (listener != null && !aborted)
+                       listener.actionPerformed(new ActionEvent(this,
+                                                                percent,
+                                                                s));
+       }
+
+       void action(int part, int total) {
+               int percent = 100 * part / total;
+               action(String.format("%d/%d (%d%%)",
+                                    part, total, percent),
+                      percent);
+       }
+
+       public void flash() throws IOException, FileNotFoundException, InterruptedException {
+               if (!check_rom_config())
+                       throw new IOException("Invalid rom config settings");
+               if (image.address + image.data.length > 0x8000)
+                       throw new IOException(String.format("Flash image too long %d",
+                                                           image.address +
+                                                           image.data.length));
+               if ((image.address & 0x3ff) != 0)
+                       throw new IOException(String.format("Flash image must start on page boundary (is 0x%x)",
+                                                           image.address));
+               int ram_address = 0xf000;
+               int flash_prog = 0xf400;
+
+               /*
+                * Store desired config values into image
+                */
+               rom_config.write(image);
+               /*
+                * Bring up the clock
+                */
+               clock_init();
+
+               int remain = image.data.length;
+               int flash_addr = image.address;
+               int image_start = 0;
+
+               action("start", 0);
+               action(0, image.data.length);
+               while (remain > 0 && !aborted) {
+                       int this_time = remain;
+                       if (this_time > 0x400)
+                               this_time = 0x400;
+
+                       /* write the data */
+                       debug.write_memory(ram_address, image.data,
+                                          image_start, this_time);
+
+                       /* write the flash program */
+                       byte[] flash_page = make_flash_page(flash_addr,
+                                                           ram_address,
+                                                           this_time);
+                       debug.write_memory(flash_prog, flash_page);
+
+                       debug.set_pc(flash_prog);
+                       int pc = debug.get_pc();
+                       debug.resume();
+                       Thread.sleep(100);
+                       for (int times = 0; times < 10; times++) {
+                               byte status = debug.read_status();
+                               if ((status & AltosDebug.STATUS_CPU_HALTED) != 0)
+                                       break;
+                               Thread.sleep(100);
+                       }
+
+                       byte[] check = debug.read_memory(flash_addr, this_time);
+                       for (int i = 0; i < this_time; i++)
+                               if (check[i] != image.data[image_start + i])
+                                       throw new IOException(String.format("Flash write failed at 0x%x (%02x != %02x)",
+                                                                           image.address + image_start + i,
+                                                                           check[i], image.data[image_start + i]));
+                       remain -= this_time;
+                       flash_addr += this_time;
+                       image_start += this_time;
+
+                       action(image.data.length - remain, image.data.length);
+               }
+               if (!aborted) {
+                       action("done", 100);
+                       debug.set_pc(image.address);
+                       debug.resume();
+               }
+               debug.close();
+       }
+
+       public void abort() {
+               aborted = true;
+               debug.close();
+       }
+
+       public void addActionListener(ActionListener l) {
+               listener = l;
+       }
+
+       public boolean check_rom_config() {
+               if (rom_config == null)
+                       rom_config = debug.romconfig();
+               return rom_config != null && rom_config.valid();
+       }
+
+       public void set_romconfig (AltosRomconfig romconfig) {
+               rom_config = romconfig;
+       }
+
+       public AltosRomconfig romconfig() {
+               if (!check_rom_config())
+                       return null;
+               return rom_config;
+       }
+
+       public void open() throws IOException, FileNotFoundException, InterruptedException {
+               input = new FileInputStream(file);
+               image = new AltosHexfile(input);
+               debug.open(debug_dongle);
+               if (!debug.check_connection())
+                       throw new IOException("Debug port not connected");
+       }
+
+       public AltosFlash(File in_file, AltosDevice in_debug_dongle) {
+               file = in_file;
+               debug_dongle = in_debug_dongle;
+               debug = new AltosDebug();
+       }
+}
\ No newline at end of file
diff --git a/ao-tools/altosui/AltosFlashUI.java b/ao-tools/altosui/AltosFlashUI.java
new file mode 100644 (file)
index 0000000..73a97a6
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * 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.AltosHexfile;
+import altosui.AltosFlash;
+
+public class AltosFlashUI
+       extends JDialog
+       implements Runnable, ActionListener
+{
+       Container       pane;
+       Box             box;
+       JLabel          serial_label;
+       JLabel          serial_value;
+       JLabel          file_label;
+       JLabel          file_value;
+       JProgressBar    pbar;
+       JButton         cancel;
+
+       File            file;
+       Thread          thread;
+       JFrame          frame;
+       AltosDevice     debug_dongle;
+       AltosFlash      flash;
+
+       public void actionPerformed(ActionEvent e) {
+               if (e.getSource() == cancel) {
+                       abort();
+                       dispose();
+               } else {
+                       String  cmd = e.getActionCommand();
+                       if (cmd.equals("done"))
+                               ;
+                       else if (cmd.equals("start")) {
+                               setVisible(true);
+                       } else {
+                               pbar.setValue(e.getID());
+                               pbar.setString(cmd);
+                       }
+               }
+       }
+
+       public void run() {
+               flash = new AltosFlash(file, debug_dongle);
+               flash.addActionListener(this);
+               try {
+                       flash.open();
+                       AltosRomconfigUI romconfig_ui = new AltosRomconfigUI (frame);
+
+                       romconfig_ui.set(flash.romconfig());
+                       romconfig_ui.showDialog();
+
+                       AltosRomconfig romconfig = romconfig_ui.romconfig();
+                       if (romconfig == null || !romconfig.valid())
+                               return;
+                       flash.set_romconfig(romconfig);
+                       serial_value.setText(String.format("%d",
+                                                          flash.romconfig().serial_number));
+                       file_value.setText(file.toString());
+                       setVisible(true);
+                       flash.flash();
+               } catch (FileNotFoundException ee) {
+                       JOptionPane.showMessageDialog(frame,
+                                                     "Cannot open image",
+                                                     file.toString(),
+                                                     JOptionPane.ERROR_MESSAGE);
+               } catch (IOException e) {
+                       JOptionPane.showMessageDialog(frame,
+                                                     e.getMessage(),
+                                                     file.toString(),
+                                                     JOptionPane.ERROR_MESSAGE);
+               } catch (InterruptedException ie) {
+               }
+               dispose();
+       }
+
+       public void abort() {
+               if (flash != null)
+                       flash.abort();
+       }
+
+       public void build_dialog() {
+               GridBagConstraints c;
+               Insets il = new Insets(4,4,4,4);
+               Insets ir = new Insets(4,4,4,4);
+
+               pane = getContentPane();
+               pane.setLayout(new GridBagLayout());
+
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 0;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               serial_label = new JLabel("Serial:");
+               pane.add(serial_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 1; c.gridy = 0;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               serial_value = new JLabel("");
+               pane.add(serial_value, c);
+
+               c = new GridBagConstraints();
+               c.fill = GridBagConstraints.NONE;
+               c.gridx = 0; c.gridy = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               file_label = new JLabel("File:");
+               pane.add(file_label, c);
+
+               c = new GridBagConstraints();
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.gridx = 1; c.gridy = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               file_value = new JLabel("");
+               pane.add(file_value, c);
+
+               pbar = new JProgressBar();
+               pbar.setMinimum(0);
+               pbar.setMaximum(100);
+               pbar.setValue(0);
+               pbar.setString("");
+               pbar.setStringPainted(true);
+               pbar.setPreferredSize(new Dimension(600, 20));
+               c = new GridBagConstraints();
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.anchor = GridBagConstraints.CENTER;
+               c.gridx = 0; c.gridy = 2;
+               c.gridwidth = GridBagConstraints.REMAINDER;
+               Insets ib = new Insets(4,4,4,4);
+               c.insets = ib;
+               pane.add(pbar, c);
+
+               cancel = new JButton("Cancel");
+               c = new GridBagConstraints();
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.gridx = 0; c.gridy = 3;
+               c.gridwidth = GridBagConstraints.REMAINDER;
+               Insets ic = new Insets(4,4,4,4);
+               c.insets = ic;
+               pane.add(cancel, c);
+               cancel.addActionListener(this);
+               pack();
+               setLocationRelativeTo(frame);
+       }
+
+       public AltosFlashUI(JFrame in_frame) {
+               super(in_frame, "Program Altusmetrum Device", false);
+
+               frame = in_frame;
+
+               build_dialog();
+
+               debug_dongle = AltosDeviceDialog.show(frame, AltosDevice.Any);
+
+               if (debug_dongle == null)
+                       return;
+
+               JFileChooser    hexfile_chooser = new JFileChooser();
+
+               hexfile_chooser.setDialogTitle("Select Flash Image");
+               hexfile_chooser.setFileFilter(new FileNameExtensionFilter("Flash Image", "ihx"));
+               int returnVal = hexfile_chooser.showOpenDialog(frame);
+
+               if (returnVal != JFileChooser.APPROVE_OPTION)
+                       return;
+
+               file = hexfile_chooser.getSelectedFile();
+
+               thread = new Thread(this);
+               thread.start();
+       }
+}
\ No newline at end of file
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..acb6fb2c8bbbdcb4c211022951ea13fb1427c2d2 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,52 +52,166 @@ public class AltosGPS {
 
        AltosGPSSat[] cc_gps_sat;       /* tracking data */
 
-       public AltosGPS(String[] words, int i) throws ParseException {
+       void ParseGPSDate(String date) throws ParseException {
+               String[] ymd = date.split("-");
+               if (ymd.length != 3)
+                       throw new ParseException("error parsing GPS date " + date + " got " + ymd.length, 0);
+               year = AltosParse.parse_int(ymd[0]);
+               month = AltosParse.parse_int(ymd[1]);
+               day = AltosParse.parse_int(ymd[2]);
+       }
+
+       void ParseGPSTime(String time) throws ParseException {
+               String[] hms = time.split(":");
+               if (hms.length != 3)
+                       throw new ParseException("Error parsing GPS time " + time + " got " + hms.length, 0);
+               hour = AltosParse.parse_int(hms[0]);
+               minute = AltosParse.parse_int(hms[1]);
+               second = AltosParse.parse_int(hms[2]);
+       }
+
+       void ClearGPSTime() {
+               year = month = day = 0;
+               hour = minute = second = 0;
+       }
+
+       public AltosGPS(String[] words, int i, int version) throws ParseException {
                AltosParse.word(words[i++], "GPS");
                nsat = AltosParse.parse_int(words[i++]);
                AltosParse.word(words[i++], "sat");
 
-               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;
+                       if (version > 1)
+                               ParseGPSDate(words[i++]);
+                       else
+                               year = month = day = 0;
+                       ParseGPSTime(words[i++]);
                        lat = AltosParse.parse_coord(words[i++]);
                        lon = AltosParse.parse_coord(words[i++]);
-                       alt = AltosParse.parse_int(AltosParse.strip_suffix(words[i++], "m"));
-                       ground_speed = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(H)"));
-                       course = AltosParse.parse_int(AltosParse.strip_suffix(words[i++], "°"));
-                       climb_rate = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(V)"));
-                       hdop = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "(hdop)"));
-                       h_error = AltosParse.parse_int(AltosParse.strip_suffix(words[i++], "(herr)"));
-                       v_error = AltosParse.parse_int(AltosParse.strip_suffix(words[i++], "(verr)"));
+                       alt = AltosParse.parse_int(words[i++]);
+                       if (version > 1 || (i < words.length && !words[i].equals("SAT"))) {
+                               ground_speed = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(H)"));
+                               course = AltosParse.parse_int(words[i++]);
+                               climb_rate = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(V)"));
+                               hdop = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "(hdop)"));
+                               h_error = AltosParse.parse_int(words[i++]);
+                               v_error = AltosParse.parse_int(words[i++]);
+                       }
                } else {
-                       gps_time = new AltosGPSTime();
                        i++;
                }
-               AltosParse.word(words[i++], "SAT");
-               int tracking_channels = 0;
-               if (words[i].equals("not-connected"))
-                       tracking_channels = 0;
-               else
-                       tracking_channels = AltosParse.parse_int(words[i]);
-               i++;
-               cc_gps_sat = new AltosGPS.AltosGPSSat[tracking_channels];
-               for (int chan = 0; chan < tracking_channels; chan++) {
-                       cc_gps_sat[chan] = new AltosGPS.AltosGPSSat();
-                       cc_gps_sat[chan].svid = AltosParse.parse_int(words[i++]);
-                       cc_gps_sat[chan].c_n0 = AltosParse.parse_int(words[i++]);
+               if (i < words.length) {
+                       AltosParse.word(words[i++], "SAT");
+                       int tracking_channels = 0;
+                       if (words[i].equals("not-connected"))
+                               tracking_channels = 0;
+                       else
+                               tracking_channels = AltosParse.parse_int(words[i]);
+                       i++;
+                       cc_gps_sat = new AltosGPS.AltosGPSSat[tracking_channels];
+                       for (int chan = 0; chan < tracking_channels; chan++) {
+                               cc_gps_sat[chan] = new AltosGPS.AltosGPSSat();
+                               cc_gps_sat[chan].svid = AltosParse.parse_int(words[i++]);
+                               /* Older versions included SiRF status bits */
+                               if (version < 2)
+                                       i++;
+                               cc_gps_sat[chan].c_n0 = AltosParse.parse_int(words[i++]);
+                       }
+               } else
+                       cc_gps_sat = new AltosGPS.AltosGPSSat[0];
+       }
+
+       public void set_latitude(int in_lat) {
+               lat = in_lat / 10.0e7;
+       }
+
+       public void set_longitude(int in_lon) {
+               lon = in_lon / 10.0e7;
+       }
+
+       public void set_time(int hour, int minute, int second) {
+               hour = hour;
+               minute = minute;
+               second = second;
+       }
+
+       public void set_date(int year, int month, int day) {
+               year = year;
+               month = month;
+               day = day;
+       }
+
+       public void set_flags(int flags) {
+               flags = flags;
+       }
+
+       public void set_altitude(int altitude) {
+               altitude = altitude;
+       }
+
+       public void add_sat(int svid, int c_n0) {
+               if (cc_gps_sat == null) {
+                       cc_gps_sat = new AltosGPS.AltosGPSSat[1];
+               } else {
+                       AltosGPSSat[] new_gps_sat = new AltosGPS.AltosGPSSat[cc_gps_sat.length + 1];
+                       for (int i = 0; i < cc_gps_sat.length; i++)
+                               new_gps_sat[i] = cc_gps_sat[i];
+                       cc_gps_sat = new_gps_sat;
+               }
+               AltosGPS.AltosGPSSat    sat = new AltosGPS.AltosGPSSat();
+               sat.svid = svid;
+               sat.c_n0 = c_n0;
+               cc_gps_sat[cc_gps_sat.length - 1] = sat;
+       }
+
+       public AltosGPS() {
+               ClearGPSTime();
+               cc_gps_sat = null;
+       }
+
+       public AltosGPS(AltosGPS old) {
+               nsat = old.nsat;
+               locked = old.locked;
+               connected = old.connected;
+               date_valid = old.date_valid;
+               lat = old.lat;          /* degrees (+N -S) */
+               lon = old.lon;          /* degrees (+E -W) */
+               alt = old.alt;          /* m */
+               year = old.year;
+               month = old.month;
+               day = old.day;
+               hour = old.hour;
+               minute = old.minute;
+               second = old.second;
+
+               gps_extended = old.gps_extended;        /* has extra data */
+               ground_speed = old.ground_speed;        /* m/s */
+               course = old.course;            /* degrees */
+               climb_rate = old.climb_rate;    /* m/s */
+               hdop = old.hdop;                /* unitless? */
+               h_error = old.h_error;  /* m */
+               v_error = old.v_error;  /* m */
+
+               if (old.cc_gps_sat != null) {
+                       cc_gps_sat = new AltosGPSSat[old.cc_gps_sat.length];
+                       for (int i = 0; i < old.cc_gps_sat.length; i++) {
+                               cc_gps_sat[i] = new AltosGPSSat();
+                               cc_gps_sat[i].svid = old.cc_gps_sat[i].svid;
+                               cc_gps_sat[i].c_n0 = old.cc_gps_sat[i].c_n0;
+                       }
                }
        }
 }
index 878da03e2b6a7ad2ade1e87fc97a19fa80055315..07c02c16c34c17d665335ff3c657482a289a4a62 100644 (file)
@@ -17,6 +17,8 @@
 
 package altosui;
 
+import altosui.AltosGPS;
+
 import java.lang.Math;
 
 public class AltosGreatCircle {
@@ -28,8 +30,8 @@ public class AltosGreatCircle {
        static final double rad = Math.PI / 180;
        static final double earth_radius = 6371.2 * 1000;       /* in meters */
 
-       AltosGreatCircle (double start_lat, double start_lon,
-                         double end_lat, double end_lon)
+       public AltosGreatCircle (double start_lat, double start_lon,
+                                double end_lat, double end_lon)
        {
                double lat1 = rad * start_lat;
                double lon1 = rad * -start_lon;
@@ -63,4 +65,13 @@ public class AltosGreatCircle {
                distance = d * earth_radius;
                bearing = course * 180/Math.PI;
        }
+
+       public AltosGreatCircle(AltosGPS start, AltosGPS end) {
+               this(start.lat, start.lon, end.lat, end.lon);
+       }
+
+       public AltosGreatCircle() {
+               distance = 0;
+               bearing = 0;
+       }
 }
diff --git a/ao-tools/altosui/AltosHexfile.java b/ao-tools/altosui/AltosHexfile.java
new file mode 100644 (file)
index 0000000..19e35ae
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * 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 java.util.concurrent.LinkedBlockingQueue;
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.util.Arrays;
+
+class HexFileInputStream extends PushbackInputStream {
+       public int line;
+
+       public HexFileInputStream(FileInputStream o) {
+               super(new BufferedInputStream(o));
+               line = 1;
+       }
+
+       public int read() throws IOException {
+               int     c = super.read();
+               if (c == '\n')
+                       line++;
+               return c;
+       }
+
+       public void unread(int c) throws IOException {
+               if (c == '\n')
+                       line--;
+               if (c != -1)
+                       super.unread(c);
+       }
+}
+
+class HexRecord implements Comparable {
+       public int      address;
+       public int      type;
+       public byte     checksum;
+       public byte[]   data;
+
+       static final int NORMAL = 0;
+       static final int EOF = 1;
+       static final int EXTENDED_ADDRESS = 2;
+
+       enum read_state {
+               marker,
+               length,
+               address,
+               type,
+               data,
+               checksum,
+               newline,
+               white,
+               done,
+       }
+
+       boolean ishex(int c) {
+               if ('0' <= c && c <= '9')
+                       return true;
+               if ('a' <= c && c <= 'f')
+                       return true;
+               if ('A' <= c && c <= 'F')
+                       return true;
+               return false;
+       }
+
+       boolean isspace(int c) {
+               switch (c) {
+               case ' ':
+               case '\t':
+                       return true;
+               }
+               return false;
+       }
+
+       int fromhex(int c) {
+               if ('0' <= c && c <= '9')
+                       return c - '0';
+               if ('a' <= c && c <= 'f')
+                       return c - 'a' + 10;
+               if ('A' <= c && c <= 'F')
+                       return c - 'A' + 10;
+               return -1;
+       }
+
+       public byte checksum() {
+               byte    got = 0;
+
+               got += data.length;
+               got += (address >> 8) & 0xff;
+               got += (address     ) & 0xff;
+               got += type;
+               for (int i = 0; i < data.length; i++)
+                       got += data[i];
+               return (byte) (-got);
+       }
+
+       public int compareTo(Object other) {
+               HexRecord       o = (HexRecord) other;
+               return address - o.address;
+       }
+
+       public String toString() {
+               return String.format("%04x: %02x (%d)", address, type, data.length);
+       }
+
+       public HexRecord(HexFileInputStream input) throws IOException {
+               read_state      state = read_state.marker;
+               int             nhexbytes = 0;
+               int             hex = 0;
+               int             ndata = 0;
+               byte            got_checksum;
+
+               while (state != read_state.done) {
+                       int c = input.read();
+                       if (c < 0 && state != read_state.white)
+                               throw new IOException(String.format("%d: Unexpected EOF", input.line));
+                       if (c == ' ')
+                               continue;
+                       switch (state) {
+                       case marker:
+                               if (c != ':')
+                                       throw new IOException("Missing ':'");
+                               state = read_state.length;
+                               nhexbytes = 2;
+                               hex = 0;
+                               break;
+                       case length:
+                       case address:
+                       case type:
+                       case data:
+                       case checksum:
+                               if(!ishex(c))
+                                       throw new IOException(String.format("Non-hex char '%c'", c));
+                               hex = hex << 4 | fromhex(c);
+                               --nhexbytes;
+                               if (nhexbytes != 0)
+                                       break;
+
+                               switch (state) {
+                               case length:
+                                       data = new byte[hex];
+                                       state = read_state.address;
+                                       nhexbytes = 4;
+                                       break;
+                               case address:
+                                       address = hex;
+                                       state = read_state.type;
+                                       nhexbytes = 2;
+                                       break;
+                               case type:
+                                       type = hex;
+                                       if (data.length > 0)
+                                               state = read_state.data;
+                                       else
+                                               state = read_state.checksum;
+                                       nhexbytes = 2;
+                                       ndata = 0;
+                                       break;
+                               case data:
+                                       data[ndata] = (byte) hex;
+                                       ndata++;
+                                       nhexbytes = 2;
+                                       if (ndata == data.length)
+                                               state = read_state.checksum;
+                                       break;
+                               case checksum:
+                                       checksum = (byte) hex;
+                                       state = read_state.newline;
+                                       break;
+                               default:
+                                       break;
+                               }
+                               hex = 0;
+                               break;
+                       case newline:
+                               if (c != '\n' && c != '\r')
+                                       throw new IOException("Missing newline");
+                               state = read_state.white;
+                               break;
+                       case white:
+                               if (!isspace(c)) {
+                                       input.unread(c);
+                                       state = read_state.done;
+                               }
+                               break;
+                       case done:
+                               break;
+                       }
+               }
+               got_checksum = checksum();
+               if (got_checksum != checksum)
+                       throw new IOException(String.format("Invalid checksum (read 0x%02x computed 0x%02x)\n",
+                                                           checksum, got_checksum));
+       }
+}
+
+public class AltosHexfile {
+       public int      address;
+       public byte[]   data;
+
+       public byte get_byte(int a) {
+               return data[a - address];
+       }
+
+       public AltosHexfile(FileInputStream file) throws IOException {
+               HexFileInputStream      input = new HexFileInputStream(file);
+               LinkedList<HexRecord>   record_list = new LinkedList<HexRecord>();
+               boolean                 done = false;
+
+               while (!done) {
+                       HexRecord       record = new HexRecord(input);
+
+                       if (record.type == HexRecord.EOF)
+                               done = true;
+                       else
+                               record_list.add(record);
+               }
+               HexRecord[] records  = record_list.toArray(new HexRecord[0]);
+               Arrays.sort(records);
+               if (records.length > 0) {
+                       int     base = records[0].address;
+                       int     bound = records[records.length-1].address +
+                               records[records.length-1].data.length;
+
+                       data = new byte[bound - base];
+                       address = base;
+                       Arrays.fill(data, (byte) 0xff);
+
+                       /* Paint the records into the new array */
+                       for (int i = 0; i < records.length; i++) {
+                               for (int j = 0; j < records[i].data.length; j++)
+                                       data[records[i].address - base + j] = records[i].data[j];
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/ao-tools/altosui/AltosLogfileChooser.java b/ao-tools/altosui/AltosLogfileChooser.java
new file mode 100644 (file)
index 0000000..36b51de
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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 altosui.AltosPreferences;
+import altosui.AltosReader;
+import altosui.AltosEepromReader;
+import altosui.AltosTelemetryReader;
+
+public class AltosLogfileChooser extends JFileChooser {
+       JFrame  frame;
+       String  filename;
+       File    file;
+
+       public String filename() {
+               return filename;
+       }
+
+       public File file() {
+               return file;
+       }
+
+       public AltosReader runDialog() {
+               int     ret;
+
+               ret = showOpenDialog(frame);
+               if (ret == APPROVE_OPTION) {
+                       file = getSelectedFile();
+                       if (file == null)
+                               return null;
+                       filename = file.getName();
+                       try {
+                               FileInputStream in;
+
+                               in = new FileInputStream(file);
+                               if (filename.endsWith("eeprom"))
+                                       return new AltosEepromReader(in);
+                               else
+                                       return new AltosTelemetryReader(in);
+                       } catch (FileNotFoundException fe) {
+                               JOptionPane.showMessageDialog(frame,
+                                                             filename,
+                                                             "Cannot open file",
+                                                             JOptionPane.ERROR_MESSAGE);
+                       }
+               }
+               return null;
+       }
+
+       public AltosLogfileChooser(JFrame in_frame) {
+               frame = in_frame;
+               setDialogTitle("Select Flight Record File");
+               setFileFilter(new FileNameExtensionFilter("Flight data file",
+                                                         "eeprom",
+                                                         "telem"));
+               setCurrentDirectory(AltosPreferences.logdir());
+       }
+}
\ No newline at end of file
index a60dc69434a67aa454519979041fee7f7a4d47a3..4d82de7816ef2d625d3ef3ec386116c2bae527d7 100644 (file)
@@ -20,10 +20,16 @@ package altosui;
 import java.text.*;
 import java.lang.*;
 
+import altosui.Altos;
+
 public class AltosParse {
+       static boolean isdigit(char c) {
+               return '0' <= c && c <= '9';
+       }
+
        static int parse_int(String v) throws ParseException {
                try {
-                       return Integer.parseInt(v);
+                       return Altos.fromdec(v);
                } catch (NumberFormatException e) {
                        throw new ParseException("error parsing int " + v, 0);
                }
@@ -31,7 +37,7 @@ public class AltosParse {
 
        static int parse_hex(String v) throws ParseException {
                try {
-                       return Integer.parseInt(v, 16);
+                       return Altos.fromhex(v);
                } catch (NumberFormatException e) {
                        throw new ParseException("error parsing hex " + v, 0);
                }
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..5be8795
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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; }
+       public void close() { }
+       public void write_comments(PrintStream out) { }
+}
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();
+       }
+}
diff --git a/ao-tools/altosui/AltosRomconfig.java b/ao-tools/altosui/AltosRomconfig.java
new file mode 100644 (file)
index 0000000..8c9cb27
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * 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 altosui.AltosHexfile;
+
+public class AltosRomconfig {
+       public boolean  valid;
+       public int      version;
+       public int      check;
+       public int      serial_number;
+       public int      radio_calibration;
+
+       static int get_int(byte[] bytes, int start, int len) {
+               int     v = 0;
+               int     o = 0;
+               while (len > 0) {
+                       v = v | ((((int) bytes[start]) & 0xff) << o);
+                       start++;
+                       len--;
+                       o += 8;
+               }
+               return v;
+       }
+
+       static void put_int(int value, byte[] bytes, int start, int len) {
+               while (len > 0) {
+                       bytes[start] = (byte) (value & 0xff);
+                       start++;
+                       len--;
+                       value >>= 8;
+               }
+       }
+
+       static void put_string(String value, byte[] bytes, int start) {
+               for (int i = 0; i < value.length(); i++)
+                       bytes[start + i] = (byte) value.charAt(i);
+       }
+
+       static final int AO_USB_DESC_STRING     = 3;
+
+       static void put_usb_serial(int value, byte[] bytes, int start) {
+               int offset = start + 0xa;
+               int string_num = 0;
+               System.out.printf("Put usb serial %d\n", value);
+
+               while (offset < bytes.length && bytes[offset] != 0) {
+                       if (bytes[offset + 1] == AO_USB_DESC_STRING) {
+                               ++string_num;
+                               if (string_num == 4)
+                                       break;
+                       }
+                       offset += ((int) bytes[offset]) & 0xff;
+               }
+               System.out.printf("offset %d content %d\n",
+                                 offset, bytes[offset]);
+               if (offset >= bytes.length || bytes[offset] == 0)
+                       return;
+               int len = ((((int) bytes[offset]) & 0xff) - 2) / 2;
+               String fmt = String.format("%%0%dd", len);
+               System.out.printf("existing serial length %d format %s\n", len, fmt);
+
+               String s = String.format(fmt, value);
+               if (s.length() != len) {
+                       System.out.printf("weird usb length issue %s isn't %d\n",
+                                         s, len);
+                       return;
+               }
+               for (int i = 0; i < len; i++) {
+                       bytes[offset + 2 + i*2] = (byte) s.charAt(i);
+                       bytes[offset + 2 + i*2+1] = 0;
+               }
+       }
+
+       public AltosRomconfig(byte[] bytes, int offset) {
+               version = get_int(bytes, offset + 0, 2);
+               check = get_int(bytes, offset + 2, 2);
+               System.out.printf("version %d check %d\n", version, check);
+               if (check == (~version & 0xffff)) {
+                       switch (version) {
+                       case 2:
+                       case 1:
+                               serial_number = get_int(bytes, offset + 4, 2);
+                               radio_calibration = get_int(bytes, offset + 6, 4);
+                               System.out.printf("serial %d cal %d\n", serial_number, radio_calibration);
+                               valid = true;
+                               break;
+                       }
+               }
+       }
+
+       public AltosRomconfig(AltosHexfile hexfile) {
+               this(hexfile.data, 0xa0 - hexfile.address);
+       }
+
+       public void write(byte[] bytes, int offset) throws IOException {
+               if (!valid)
+                       throw new IOException("rom configuration invalid");
+
+               if (offset < 0 || bytes.length < offset + 10)
+                       throw new IOException("image cannot contain rom config");
+
+               AltosRomconfig existing = new AltosRomconfig(bytes, offset);
+               if (!existing.valid)
+                       throw new IOException("image does not contain existing rom config");
+
+               switch (existing.version) {
+               case 2:
+                       put_usb_serial(serial_number, bytes, offset);
+               case 1:
+                       put_int(serial_number, bytes, offset + 4, 2);
+                       put_int(radio_calibration, bytes, offset + 6, 4);
+                       break;
+               }
+       }
+
+       public void write (AltosHexfile hexfile) throws IOException {
+               write(hexfile.data, 0xa0 - hexfile.address);
+               AltosRomconfig check = new AltosRomconfig(hexfile);
+               if (!check.valid())
+                       throw new IOException("writing new rom config failed\n");
+       }
+
+       public AltosRomconfig(int in_serial_number, int in_radio_calibration) {
+               valid = true;
+               version = 1;
+               check = (~version & 0xffff);
+               serial_number = in_serial_number;
+               radio_calibration = in_radio_calibration;
+       }
+
+       public boolean valid() {
+               return valid && serial_number != 0;
+       }
+
+       public AltosRomconfig() {
+               valid = false;
+       }
+}
diff --git a/ao-tools/altosui/AltosRomconfigUI.java b/ao-tools/altosui/AltosRomconfigUI.java
new file mode 100644 (file)
index 0000000..bc51186
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * 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 altosui.AltosRomconfig;
+
+public class AltosRomconfigUI
+       extends JDialog
+       implements ActionListener
+{
+       Container       pane;
+       Box             box;
+       JLabel          serial_label;
+       JLabel          radio_calibration_label;
+
+       JFrame          owner;
+       JTextField      serial_value;
+       JTextField      radio_calibration_value;
+
+       JButton         ok;
+       JButton         cancel;
+
+       /* Build the UI using a grid bag */
+       public AltosRomconfigUI(JFrame in_owner) {
+               super (in_owner, "Configure TeleMetrum Rom Values", true);
+
+               owner = in_owner;
+               GridBagConstraints c;
+
+               Insets il = new Insets(4,4,4,4);
+               Insets ir = new Insets(4,4,4,4);
+
+               pane = getContentPane();
+               pane.setLayout(new GridBagLayout());
+
+               /* Serial */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 0;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               serial_label = new JLabel("Serial:");
+               pane.add(serial_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 3; c.gridy = 0;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               serial_value = new JTextField("0");
+               pane.add(serial_value, c);
+
+               /* Radio calibration value */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 1;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               radio_calibration_label = new JLabel("Radio Calibration:");
+               pane.add(radio_calibration_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 3; c.gridy = 1;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               radio_calibration_value = new JTextField("1186611");
+               pane.add(radio_calibration_value, c);
+
+               /* Buttons */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 2;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = il;
+               ok = new JButton("OK");
+               pane.add(ok, c);
+               ok.addActionListener(this);
+               ok.setActionCommand("ok");
+
+               c = new GridBagConstraints();
+               c.gridx = 3; c.gridy = 2;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = il;
+               cancel = new JButton("Cancel");
+               pane.add(cancel, c);
+               cancel.addActionListener(this);
+               cancel.setActionCommand("cancel");
+
+               pack();
+               setLocationRelativeTo(owner);
+       }
+
+       boolean selected;
+
+       /* Listen for events from our buttons */
+       public void actionPerformed(ActionEvent e) {
+               String  cmd = e.getActionCommand();
+
+               if (cmd.equals("ok"))
+                       selected = true;
+               setVisible(false);
+       }
+
+       int serial() {
+               return Integer.parseInt(serial_value.getText());
+       }
+
+       void set_serial(int serial) {
+               serial_value.setText(String.format("%d", serial));
+       }
+
+       int radio_calibration() {
+               return Integer.parseInt(radio_calibration_value.getText());
+       }
+
+       void set_radio_calibration(int calibration) {
+               radio_calibration_value.setText(String.format("%d", calibration));
+       }
+
+       public void set(AltosRomconfig config) {
+               if (config != null && config.valid()) {
+                       set_serial(config.serial_number);
+                       set_radio_calibration(config.radio_calibration);
+               }
+       }
+
+       public AltosRomconfig romconfig() {
+               try {
+                       return new AltosRomconfig(serial(), radio_calibration());
+               } catch (NumberFormatException ne) {
+                       return null;
+               }
+       }
+
+       public AltosRomconfig showDialog() {
+               setVisible(true);
+               if (selected)
+                       return romconfig();
+               return null;
+       }
+}
index efa63f681819e2ed9f3d22ebfac66d79e47f7750..5b47960f79a28f8d7e4bd92977147b6eaad30587 100644 (file)
@@ -45,6 +45,8 @@ public class AltosSerial implements Runnable {
        LinkedBlockingQueue<String> reply_queue;
        Thread input_thread;
        String line;
+       byte[] line_bytes;
+       int line_count;
 
        public void run () {
                int c;
@@ -60,18 +62,36 @@ public class AltosSerial implements Runnable {
                                        continue;
                                synchronized(this) {
                                        if (c == '\n') {
-                                               if (line != "") {
+                                               if (line_count != 0) {
+                                                       try {
+                                                               line = new String(line_bytes, 0, line_count, "UTF-8");
+                                                       } catch (UnsupportedEncodingException ue) {
+                                                               line = "";
+                                                               for (int i = 0; i < line_count; i++)
+                                                                       line = line + line_bytes[i];
+                                                       }
                                                        if (line.startsWith("VERSION")) {
                                                                for (int e = 0; e < monitors.size(); e++) {
                                                                        LinkedBlockingQueue<String> q = monitors.get(e);
                                                                        q.put(line);
                                                                }
-                                                       } else
+                                                       } else {
+//                                                             System.out.printf("GOT: %s\n", line);
                                                                reply_queue.put(line);
+                                                       }
+                                                       line_count = 0;
                                                        line = "";
                                                }
                                        } else {
-                                               line = line + (char) c;
+                                               if (line_bytes == null) {
+                                                       line_bytes = new byte[256];
+                                               } else if (line_count == line_bytes.length) {
+                                                       byte[] new_line_bytes = new byte[line_count * 2];
+                                                       System.arraycopy(line_bytes, 0, new_line_bytes, 0, line_count);
+                                                       line_bytes = new_line_bytes;
+                                               }
+                                               line_bytes[line_count] = (byte) c;
+                                               line_count++;
                                        }
                                }
                        }
@@ -79,8 +99,19 @@ public class AltosSerial implements Runnable {
                }
        }
 
+       public void flush_reply() {
+               libaltos.altos_flush(altos);
+               try {
+                       Thread.sleep(100);
+               } catch (InterruptedException ie) {
+               }
+               reply_queue.clear();
+       }
+
        public String get_reply() throws InterruptedException {
-               return reply_queue.take();
+               libaltos.altos_flush(altos);
+               String line = reply_queue.take();
+               return line;
        }
 
        public void add_monitor(LinkedBlockingQueue<String> q) {
@@ -126,6 +157,7 @@ public class AltosSerial implements Runnable {
        }
 
        public void print(String data) {
+//h            System.out.printf("\"%s\" ", data);
                for (int i = 0; i < data.length(); i++)
                        putc(data.charAt(i));
        }
@@ -154,6 +186,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..3ef00f35d7d87375cf4967553f5e28c3bdd6edd6 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 */
 
@@ -64,6 +64,8 @@ public class AltosState {
        boolean gps_ready;
 
        AltosGreatCircle from_pad;
+       double  elevation;      /* from pad */
+       double  range;          /* total distance */
 
        double  gps_height;
 
@@ -71,27 +73,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 +125,8 @@ 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 && data.gps.locked) {
                                npad++;
                                if (npad > 1) {
                                        /* filter pad position */
@@ -149,8 +149,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,23 +161,30 @@ 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);
+                       }
                }
+               elevation = 0;
+               range = -1;
                if (npad > 0) {
                        gps_height = gps.alt - pad_alt;
+                       if (from_pad != null) {
+                               elevation = Math.atan2(height, from_pad.distance) * 180 / Math.PI;
+                               range = Math.sqrt(height * height + from_pad.distance * from_pad.distance);
+                       }
                } else {
                        gps_height = 0;
                }
        }
 
-       public AltosState(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..bc62690b262874b4006ff723f208ae2a8daebfd2 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,76 +52,17 @@ 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");
-               version = AltosParse.parse_int(words[i++]);
+               if (words[i].equals("CALL")) {
+                       version = 0;
+               } else {
+                       AltosParse.word (words[i++], "VERSION");
+                       version = AltosParse.parse_int(words[i++]);
+               }
 
                AltosParse.word (words[i++], "CALL");
                callsign = words[i++];
@@ -128,17 +70,24 @@ public class AltosTelemetry {
                AltosParse.word (words[i++], "SERIAL");
                serial = AltosParse.parse_int(words[i++]);
 
-               AltosParse.word (words[i++], "FLIGHT");
-               flight = AltosParse.parse_int(words[i++]);
+               if (version >= 2) {
+                       AltosParse.word (words[i++], "FLIGHT");
+                       flight = AltosParse.parse_int(words[i++]);
+               } else
+                       flight = 0;
 
                AltosParse.word(words[i++], "RSSI");
                rssi = AltosParse.parse_int(words[i++]);
 
+               /* Older telemetry data had mis-computed RSSI value */
+               if (version <= 2)
+                       rssi = (rssi + 74) / 2 - 74;
+
                AltosParse.word(words[i++], "STATUS");
                status = AltosParse.parse_hex(words[i++]);
 
                AltosParse.word(words[i++], "STATE");
-               state = words[i++];
+               state = Altos.state(words[i++]);
 
                tick = AltosParse.parse_int(words[i++]);
 
@@ -175,12 +124,17 @@ public class AltosTelemetry {
                AltosParse.word(words[i++], "gp:");
                ground_pres = AltosParse.parse_int(words[i++]);
 
-               AltosParse.word(words[i++], "a+:");
-               accel_plus_g = AltosParse.parse_int(words[i++]);
+               if (version >= 1) {
+                       AltosParse.word(words[i++], "a+:");
+                       accel_plus_g = AltosParse.parse_int(words[i++]);
 
-               AltosParse.word(words[i++], "a-:");
-               accel_minus_g = AltosParse.parse_int(words[i++]);
+                       AltosParse.word(words[i++], "a-:");
+                       accel_minus_g = AltosParse.parse_int(words[i++]);
+               } else {
+                       accel_plus_g = ground_accel;
+                       accel_minus_g = ground_accel + 530;
+               }
 
-               gps = new AltosGPS(words, i);
+               gps = new AltosGPS(words, i, version);
        }
 }
diff --git a/ao-tools/altosui/AltosTelemetryReader.java b/ao-tools/altosui/AltosTelemetryReader.java
new file mode 100644 (file)
index 0000000..a3402f9
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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;
+                               }
+                               try {
+                                       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 (ParseException pe) {
+                                       System.out.printf("parse exception %s\n", pe.getMessage());
+                               }
+                       }
+               } catch (IOException io) {
+                       System.out.printf("io exception\n");
+               }
+               record_iterator = records.iterator();
+               try {
+                       input.close();
+               } catch (IOException ie) {
+               }
+       }
+}
index 4994f093dcbfed6eb80e7c9534a2715fea65843a..5b48e26f16fda3c6cec6044580c8654908272cf5 100644 (file)
@@ -28,100 +28,25 @@ 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 altosui.AltosFlashUI;
+import altosui.AltosLogfileChooser;
+import altosui.AltosCSVUI;
 
 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 +177,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 +197,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 +234,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;
 
@@ -339,12 +264,12 @@ public class AltosUI extends JFrame {
                private AltosState state;
                int     reported_landing;
 
-               public void report(boolean last) {
+               public synchronized void report(boolean last) {
                        if (state == null)
                                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 +278,16 @@ public class AltosUI extends JFrame {
                        }
 
                        /* If the rocket isn't on the pad, then report height */
-                       if (state.state > AltosTelemetry.ao_flight_pad) {
+                       if (Altos.ao_flight_drogue <= state.state &&
+                           state.state < Altos.ao_flight_landed &&
+                           state.range >= 0)
+                       {
+                               voice.speak("Height %d, bearing %d, elevation %d, range %d.\n",
+                                           (int) (state.height + 0.5),
+                                           (int) (state.from_pad.bearing + 0.5),
+                                           (int) (state.elevation + 0.5),
+                                           (int) (state.range + 0.5));
+                       } else if (state.state > Altos.ao_flight_pad) {
                                voice.speak("%d meters", (int) (state.height + 0.5));
                        } else {
                                reported_landing = 0;
@@ -363,17 +297,17 @@ public class AltosUI extends JFrame {
                         * either we've got a landed report or we haven't heard from it in
                         * a long time
                         */
-                       if (!state.ascent &&
+                       if (state.state >= Altos.ao_flight_drogue &&
                            (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");
                                else
                                        voice.speak("rocket may have crashed");
                                if (state.from_pad != null)
-                                       voice.speak("bearing %d degrees, range %d meters",
+                                       voice.speak("Bearing %d degrees, range %d meters.",
                                                    (int) (state.from_pad.bearing + 0.5),
                                                    (int) (state.from_pad.distance + 0.5));
                                ++reported_landing;
@@ -386,7 +320,7 @@ public class AltosUI extends JFrame {
                        state = null;
                        try {
                                for (;;) {
-                                       Thread.sleep(10000);
+                                       Thread.sleep(20000);
                                        report(false);
                                }
                        } catch (InterruptedException ie) {
@@ -394,19 +328,22 @@ public class AltosUI extends JFrame {
                }
 
                public void notice(AltosState new_state) {
+                       AltosState old_state = state;
                        state = new_state;
+                       if (old_state != null && old_state.state != state.state)
+                               report(false);
                }
        }
 
        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 +360,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 +379,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 +411,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 +424,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,19 +457,24 @@ 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);
+       }
+
+       void FlashImage() {
+               new AltosFlashUI(AltosUI.this);
        }
 
        /*
@@ -535,41 +482,49 @@ public class AltosUI extends JFrame {
         */
 
        class ReplayThread extends DisplayThread {
-               FileInputStream replay;
-               String filename;
-
-               ReplayThread(FileInputStream in, String name) {
-                       replay = in;
-                       filename = name;
-               }
+               AltosReader     reader;
+               String          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() {
@@ -588,36 +543,27 @@ public class AltosUI extends JFrame {
         * Replay a flight from telemetry data
         */
        private void Replay() {
-               JFileChooser    logfile_chooser = new JFileChooser();
-
-               logfile_chooser.setDialogTitle("Select Telemetry File");
-               logfile_chooser.setFileFilter(new FileNameExtensionFilter("Telemetry file", "telem"));
-               logfile_chooser.setCurrentDirectory(AltosPreferences.logdir());
-               int returnVal = logfile_chooser.showOpenDialog(AltosUI.this);
-
-               if (returnVal == JFileChooser.APPROVE_OPTION) {
-                       File file = logfile_chooser.getSelectedFile();
-                       if (file == null)
-                               System.out.println("No file selected?");
-                       String  filename = file.getName();
-                       try {
-                               FileInputStream replay = new FileInputStream(file);
-                               ReplayThread    thread = new ReplayThread(replay, filename);
-                               run_display(thread);
-                       } catch (FileNotFoundException ee) {
-                               JOptionPane.showMessageDialog(AltosUI.this,
-                                                             filename,
-                                                             "Cannot open telemetry file",
-                                                             JOptionPane.ERROR_MESSAGE);
-                       }
-               }
+               AltosLogfileChooser chooser = new AltosLogfileChooser(
+                       AltosUI.this);
+               AltosReader reader = chooser.runDialog();
+               if (reader != null)
+                       run_display(new ReplayThread(reader,
+                                                    chooser.filename()));
        }
 
        /* Connect to TeleMetrum, either directly or through
         * a TeleDongle over the packet link
         */
        private void SaveFlightData() {
-               new AltosEeprom(AltosUI.this);
+               new AltosEepromDownload(AltosUI.this);
+       }
+
+       /* Load a flight log file and write out a CSV file containing
+        * all of the data in standard units
+        */
+
+       private void ExportData() {
+               new AltosCSVUI(AltosUI.this);
        }
 
        /* Create the AltosUI menus
@@ -634,6 +580,38 @@ public class AltosUI extends JFrame {
                        menu.setMnemonic(KeyEvent.VK_F);
                        menubar.add(menu);
 
+                       item = new JMenuItem("Replay File",KeyEvent.VK_R);
+                       item.addActionListener(new ActionListener() {
+                                       public void actionPerformed(ActionEvent e) {
+                                               Replay();
+                                       }
+                               });
+                       menu.add(item);
+
+                       item = new JMenuItem("Save Flight Data",KeyEvent.VK_S);
+                       item.addActionListener(new ActionListener() {
+                                       public void actionPerformed(ActionEvent e) {
+                                               SaveFlightData();
+                                       }
+                               });
+                       menu.add(item);
+
+                       item = new JMenuItem("Flash Image",KeyEvent.VK_F);
+                       item.addActionListener(new ActionListener() {
+                                       public void actionPerformed(ActionEvent e) {
+                                               FlashImage();
+                                       }
+                               });
+                       menu.add(item);
+
+                       item = new JMenuItem("Export Data",KeyEvent.VK_F);
+                       item.addActionListener(new ActionListener() {
+                                       public void actionPerformed(ActionEvent e) {
+                                               ExportData();
+                                       }
+                               });
+                       menu.add(item);
+
                        item = new JMenuItem("Quit",KeyEvent.VK_Q);
                        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
                                                                   ActionEvent.CTRL_MASK));
@@ -669,20 +647,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,33 +716,82 @@ 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);
 
        }
+
+       static String replace_extension(String input, String extension) {
+               int dot = input.lastIndexOf(".");
+               if (dot > 0)
+                       input = input.substring(0,dot);
+               return input.concat(extension);
+       }
+
+       static AltosReader open_logfile(String filename) {
+               File file = new File (filename);
+               try {
+                       FileInputStream in;
+
+                       in = new FileInputStream(file);
+                       if (filename.endsWith("eeprom"))
+                               return new AltosEepromReader(in);
+                       else
+                               return new AltosTelemetryReader(in);
+               } catch (FileNotFoundException fe) {
+                       System.out.printf("Cannot open '%s'\n", filename);
+                       return null;
+               }
+       }
+
+       static AltosCSV open_csv(String filename) {
+               File file = new File (filename);
+               try {
+                       return new AltosCSV(file);
+               } catch (FileNotFoundException fe) {
+                       System.out.printf("Cannot open '%s'\n", filename);
+                       return null;
+               }
+       }
+
+       static void process_file(String input) {
+               String output = replace_extension(input,".csv");
+               if (input.equals(output)) {
+                       System.out.printf("Not processing '%s'\n", input);
+                       return;
+               }
+               System.out.printf("Processing \"%s\" to \"%s\"\n", input, output);
+               AltosReader reader = open_logfile(input);
+               if (reader == null)
+                       return;
+               AltosCSV writer = open_csv(output);
+               if (writer == null)
+                       return;
+               writer.write(reader);
+               reader.close();
+               writer.close();
+       }
+
        public static void main(final String[] args) {
-               AltosUI altosui = new AltosUI();
-               altosui.setVisible(true);
+
+               /* Handle batch-mode */
+               if (args.length > 0) {
+                       for (int i = 0; i < args.length; i++)
+                               process_file(args[i]);
+               } else {
+                       AltosUI altosui = new AltosUI();
+                       altosui.setVisible(true);
+               }
        }
 }
\ No newline at end of file
index bae42c9ac14cd41d565db70cbe568bf03938abc0..d3ecb889d909b3f83afd2df15d6a8ba1c15d2641 100644 (file)
@@ -2,22 +2,41 @@
 
 CLASSPATH=classes:./*:/usr/share/java/*
 CLASSFILES=\
+       Altos.class \
+       AltosChannelMenu.class \
+       AltosConfig.class \
+       AltosConfigUI.class \
        AltosConvert.class \
-       AltosEeprom.class \
+       AltosCSV.class \
+       AltosCSVUI.class \
+       AltosDebug.class \
+       AltosEepromDownload.class \
        AltosEepromMonitor.class \
+       AltosEepromReader.class \
+       AltosEepromRecord.class \
        AltosFile.class \
+       AltosFlash.class \
+       AltosFlashUI.class \
+       AltosFlightInfoTableModel.class \
+       AltosFlightStatusTableModel.class \
        AltosGPS.class \
        AltosGreatCircle.class \
+       AltosHexfile.class \
        AltosLog.class \
+       AltosLogfileChooser.class \
        AltosParse.class \
        AltosPreferences.class \
+       AltosRecord.class \
        AltosSerialMonitor.class \
        AltosSerial.class \
        AltosState.class \
        AltosTelemetry.class \
+       AltosTelemetryReader.class \
        AltosUI.class \
        AltosDevice.class \
        AltosDeviceDialog.class \
+       AltosRomconfig.class \
+       AltosRomconfigUI.class \
        AltosVoice.class
 
 #FREETTSSRC=/home/keithp/src/freetts/freetts-1.2.2
@@ -32,7 +51,7 @@ CLASSFILES=\
 #      en_us.jar \
 #      freetts.jar
 
-JAVAFLAGS=-Xlint:unchecked
+JAVAFLAGS=-Xlint:unchecked -Xlint:deprecation
 
 OS:=$(shell uname)
 
index 440a02b5925d8b473f633a8a08cfd1638c66a159..6d4fa5bfb0cd56a13b02f48265adcd7801288602 100644 (file)
 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 = "remote", .has_arg = 0, .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;
@@ -89,7 +91,7 @@ main (int argc, char **argv)
        int             invalid;
        char            serial_line[8192];
 
-       while ((c = getopt_long(argc, argv, "T:D:R", options, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "T:D:C:R", options, NULL)) != -1) {
                switch (c) {
                case 'T':
                        tty = optarg;
@@ -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..ffdb23669964c2be417b0905e7cf5a862122fdf6 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)
 {
@@ -515,16 +448,20 @@ altos_list_finish(struct altos_list *list)
 #include <fcntl.h>
 #include <termios.h>
 #include <errno.h>
+#include <pthread.h>
 
 #define USB_BUF_SIZE   64
 
 struct altos_file {
        int                             fd;
+       int                             pipe[2];
        unsigned char                   out_data[USB_BUF_SIZE];
        int                             out_used;
        unsigned char                   in_data[USB_BUF_SIZE];
        int                             in_used;
        int                             in_read;
+       pthread_mutex_t                 putc_mutex;
+       pthread_mutex_t                 getc_mutex;
 };
 
 struct altos_file *
@@ -537,6 +474,7 @@ altos_open(struct altos_device *device)
        if (!file)
                return NULL;
 
+       pipe(file->pipe);
        file->fd = open(device->path, O_RDWR | O_NOCTTY);
        if (file->fd < 0) {
                perror(device->path);
@@ -551,8 +489,8 @@ altos_open(struct altos_device *device)
                return NULL;
        }
        cfmakeraw(&term);
-       term.c_cc[VMIN] = 0;
-       term.c_cc[VTIME] = 1;
+       term.c_cc[VMIN] = 1;
+       term.c_cc[VTIME] = 0;
        ret = tcsetattr(file->fd, TCSAFLUSH, &term);
        if (ret < 0) {
                perror("tcsetattr");
@@ -560,216 +498,380 @@ altos_open(struct altos_device *device)
                free(file);
                return NULL;
        }
+       pthread_mutex_init(&file->putc_mutex,NULL);
+       pthread_mutex_init(&file->getc_mutex,NULL);
        return file;
 }
 
 void
 altos_close(struct altos_file *file)
 {
-       close(file->fd);
-       file->fd = -1;
+       if (file->fd != -1) {
+               int     fd = file->fd;
+               file->fd = -1;
+               write(file->pipe[1], "\r", 1);
+               close(fd);
+       }
 }
 
 void
 altos_free(struct altos_file *file)
 {
-       if (file->fd != -1)
-               close(file->fd);
+       altos_close(file);
        free(file);
 }
 
+static int
+_altos_flush(struct altos_file *file)
+{
+       while (file->out_used) {
+               int     ret;
+
+               if (file->fd < 0)
+                       return -EBADF;
+               fflush(stdout);
+               ret = write (file->fd, file->out_data, file->out_used);
+               if (ret < 0)
+                       return -errno;
+               if (ret) {
+                       memmove(file->out_data, file->out_data + ret,
+                               file->out_used - ret);
+                       file->out_used -= ret;
+               }
+       }
+}
+
 int
 altos_putchar(struct altos_file *file, char c)
 {
        int     ret;
 
+       pthread_mutex_lock(&file->putc_mutex);
        if (file->out_used == USB_BUF_SIZE) {
-               ret = altos_flush(file);
-               if (ret)
+               ret = _altos_flush(file);
+               if (ret) {
+                       pthread_mutex_unlock(&file->putc_mutex);
                        return ret;
+               }
        }
        file->out_data[file->out_used++] = c;
+       ret = 0;
        if (file->out_used == USB_BUF_SIZE)
-               return altos_flush(file);
+               ret = _altos_flush(file);
+       pthread_mutex_unlock(&file->putc_mutex);
        return 0;
 }
 
 int
 altos_flush(struct altos_file *file)
 {
-       while (file->out_used) {
-               int     ret;
-
-               if (file->fd < 0)
-                       return -EBADF;
-               ret = write (file->fd, file->out_data, file->out_used);
-               if (ret < 0)
-                       return -errno;
-               if (ret) {
-                       memmove(file->out_data, file->out_data + ret,
-                               file->out_used - ret);
-                       file->out_used -= ret;
-               }
-       }
+       int ret;
+       pthread_mutex_lock(&file->putc_mutex);
+       ret = _altos_flush(file);
+       pthread_mutex_unlock(&file->putc_mutex);
+       return ret;
 }
 
+
+#include <poll.h>
+
 int
 altos_getchar(struct altos_file *file, int timeout)
 {
-       while (file->in_read == file->in_used) {
-               int     ret;
+       int             ret;
+       struct pollfd   fd[2];
 
+       if (timeout == 0)
+               timeout = -1;
+       pthread_mutex_lock(&file->getc_mutex);
+       fd[0].fd = file->fd;
+       fd[0].events = POLLIN;
+       fd[1].fd = file->pipe[0];
+       fd[1].events = POLLIN;
+       while (file->in_read == file->in_used) {
+               if (file->fd < 0) {
+                       pthread_mutex_unlock(&file->getc_mutex);
+                       return LIBALTOS_ERROR;
+               }
                altos_flush(file);
-               if (file->fd < 0)
-                       return -EBADF;
-               ret = read(file->fd, file->in_data, USB_BUF_SIZE);
-               if (ret < 0)
-                       return -errno;
-               file->in_read = 0;
-               file->in_used = ret;
+
+               ret = poll(fd, 2, timeout);
+               if (ret < 0) {
+                       perror("altos_getchar");
+                       pthread_mutex_unlock(&file->getc_mutex);
+                       return LIBALTOS_ERROR;
+               }
+               if (ret == 0) {
+                       pthread_mutex_unlock(&file->getc_mutex);
+                       return LIBALTOS_TIMEOUT;
+               }
+               if (fd[0].revents & POLLIN) {
+                       ret = read(file->fd, file->in_data, USB_BUF_SIZE);
+                       if (ret < 0) {
+                               perror("altos_getchar");
+                               pthread_mutex_unlock(&file->getc_mutex);
+                               return LIBALTOS_ERROR;
+                       }
+                       file->in_read = 0;
+                       file->in_used = ret;
+               }
        }
-       return file->in_data[file->in_read++];
+       ret = file->in_data[file->in_read++];
+       pthread_mutex_unlock(&file->getc_mutex);
+       return ret;
 }
 
 #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
+
+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;
+};
 
-static libusb_device **list;
-static ssize_t num, current;
 
-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 +880,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..59a3b8a616d7969a80f0ba24f12f85703aa6b6a3 100644 (file)
@@ -35,6 +35,7 @@ ALTOS_SRC = \
        ao_panic.c \
        ao_task.c \
        ao_timer.c \
+       ao_romconfig.c \
        _bp.c
 
 #
@@ -213,8 +214,10 @@ all: ../$(PROG)
 ../altitude.h: make-altitude
        nickle $< > $@
 
-ao_product.h: ao-make-product.5c
-       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -p $(PRODUCT) -v $(VERSION) > $@
+ao_product.h: ao-make-product.5c always
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+always:
 
 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..cd4e4814e07a8a27611f9a56c06db47bcc25941f 100644 (file)
--- a/src/ao.h
+++ b/src/ao.h
@@ -79,7 +79,7 @@ ao_alarm(uint16_t delay);
 
 /* Yield the processor to another task */
 void
-ao_yield(void) _naked;
+ao_yield(void) __naked;
 
 /* Add a task to the run queue */
 void
@@ -139,7 +139,7 @@ ao_timer_set_adc_interval(uint8_t interval) __critical;
 
 /* Timer interrupt */
 void
-ao_timer_isr(void) interrupt 9;
+ao_timer_isr(void) __interrupt 9;
 
 /* Initialize the timer */
 void
@@ -198,7 +198,7 @@ ao_adc_get(__xdata struct ao_adc *packet);
 /* The A/D interrupt handler */
 
 void
-ao_adc_isr(void) interrupt 1;
+ao_adc_isr(void) __interrupt 1;
 
 /* Initialize the A/D converter */
 void
@@ -289,6 +289,18 @@ ao_led_for(uint8_t colors, uint16_t ticks) __reentrant;
 void
 ao_led_init(uint8_t enable);
 
+/*
+ * ao_romconfig.c
+ */
+
+#define AO_ROMCONFIG_VERSION   2
+
+extern __code __at (0x00a0) uint16_t ao_romconfig_version;
+extern __code __at (0x00a2) uint16_t ao_romconfig_check;
+extern __code __at (0x00a4) uint16_t ao_serial_number;
+extern __code __at (0x00a6) uint32_t ao_radio_cal;
+extern __code __at (0x00aa) uint8_t ao_usb_descriptors [];
+
 /*
  * ao_usb.c
  */
@@ -313,7 +325,7 @@ ao_usb_flush(void);
 
 /* USB interrupt handler */
 void
-ao_usb_isr(void) interrupt 6;
+ao_usb_isr(void) __interrupt 6;
 
 /* Enable the USB controller */
 void
@@ -413,7 +425,7 @@ ao_dma_abort(uint8_t id);
 
 /* DMA interrupt routine */
 void
-ao_dma_isr(void) interrupt 8;
+ao_dma_isr(void) __interrupt 8;
 
 /*
  * ao_mutex.c
@@ -552,6 +564,7 @@ struct ao_log_record {
                        uint8_t         year;
                        uint8_t         month;
                        uint8_t         day;
+                       uint8_t         extra;
                } gps_date;
                struct {
                        uint16_t        d0;
@@ -561,7 +574,7 @@ struct ao_log_record {
 };
 
 /* Write a record to the eeprom log */
-void
+uint8_t
 ao_log_data(__xdata struct ao_log_record *log) __reentrant;
 
 /* Flush the log */
@@ -709,10 +722,10 @@ ao_dbg_init(void);
 
 #if HAS_SERIAL_1
 void
-ao_serial_rx1_isr(void) interrupt 3;
+ao_serial_rx1_isr(void) __interrupt 3;
 
 void
-ao_serial_tx1_isr(void) interrupt 14;
+ao_serial_tx1_isr(void) __interrupt 14;
 
 char
 ao_serial_getchar(void) __critical;
@@ -848,7 +861,7 @@ extern __xdata uint8_t ao_radio_done;
 extern __xdata uint8_t ao_radio_mutex;
 
 void
-ao_radio_general_isr(void) interrupt 16;
+ao_radio_general_isr(void) __interrupt 16;
 
 void
 ao_radio_get(void);
@@ -997,8 +1010,7 @@ ao_rssi_init(uint8_t rssi_led);
  * each instance of a product
  */
 
-extern const uint8_t ao_usb_descriptors [];
-extern const uint16_t ao_serial_number;
+extern __code __at(0x00aa) uint8_t ao_usb_descriptors [];
 extern const char ao_version[];
 extern const char ao_manufacturer[];
 extern const char ao_product[];
@@ -1034,7 +1046,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 +1055,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 50f968488ab2ebf6b06257de3d2ab408967e99b7..49d2519e209d5c4f2faf594c5afb08da9da42af6 100644 (file)
@@ -41,7 +41,7 @@ ao_adc_get(__xdata struct ao_adc *packet)
 }
 
 void
-ao_adc_isr(void) interrupt 1
+ao_adc_isr(void) __interrupt 1
 {
        uint8_t sequence;
        uint8_t __xdata *a;
index 4a68fba49d56110b252a2896bc8041478c111171..a54a2316f006fb4a20909d430389413ac66922c1 100644 (file)
@@ -263,13 +263,12 @@ ao_cmd_register(__code struct ao_cmds *cmds)
 }
 
 void
-ao_cmd(void *parameters)
+ao_cmd(void)
 {
        __xdata char    c;
        __xdata uint8_t cmd, cmds;
        __code struct ao_cmds * __xdata cs;
        void (*__xdata func)(void);
-       (void) parameters;
 
        lex_echo = 1;
        for (;;) {
index cbd639a5d026461571b9b979ceb79017327fd00a..88b52dc05950ebfe37f950bdb7673551387b63b2 100644 (file)
@@ -27,16 +27,6 @@ __xdata uint8_t ao_config_mutex;
 #define AO_CONFIG_DEFAULT_CALLSIGN     "N0CALL"
 #define AO_CONFIG_DEFAULT_ACCEL_ZERO_G 16000
 #define AO_CONFIG_DEFAULT_APOGEE_DELAY 0
-/*
- * For 434.550MHz, the frequency value is:
- *
- * 434.550e6 / (24e6 / 2**16) = 1186611.2
- *
- * This value is stored in a const variable so that
- * ao-load can change it during programming for
- * devices that have no eeprom for config data.
- */
-const uint32_t ao_radio_cal = 1186611;
 
 #if HAS_EEPROM
 static void
index 110138b56a42d5cfd50f19c3b3df673bff32d1da..946666abc6ff9993410fa7c155479c43c6a1e511 100644 (file)
@@ -112,7 +112,7 @@ ao_dma_abort(uint8_t id)
 }
 
 void
-ao_dma_isr(void) interrupt 8
+ao_dma_isr(void) __interrupt 8
 {
        uint8_t id, mask;
 
index cceb79ffeb82be0b03e72b73f53b3ca2cdb2db5c..7abc93f5d8e868cda3c5542a75b06a9e73846f4e 100644 (file)
@@ -51,12 +51,12 @@ ao_gps_report(void)
                gps_log.u.gps_altitude.unused = 0xffff;
                ao_log_data(&gps_log);
                if (!date_reported && (gps_data.flags & AO_GPS_DATE_VALID)) {
-                       date_reported = 1;
                        gps_log.type = AO_LOG_GPS_DATE;
                        gps_log.u.gps_date.year = gps_data.year;
                        gps_log.u.gps_date.month = gps_data.month;
                        gps_log.u.gps_date.day = gps_data.day;
-                       ao_log_data(&gps_log);
+                       gps_log.u.gps_date.extra = 0;
+                       date_reported = ao_log_data(&gps_log);
                }
        }
 }
index ae8c7ef7cafb1980d5cb08ffd541eac66d2f596c..c822f7fab57bf2125922f3fac5263369d74dcaf2 100644 (file)
@@ -422,6 +422,7 @@ gps_dump(void) __reentrant
        printf ("Time: %02d:%02d:%02d\n", ao_gps_data.hour, ao_gps_data.minute, ao_gps_data.second);
        printf ("Lat/Lon: %ld %ld\n", ao_gps_data.latitude, ao_gps_data.longitude);
        printf ("Alt: %d\n", ao_gps_data.altitude);
+       printf ("Flags: 0x%x\n", ao_gps_data.flags);
        ao_mutex_put(&ao_gps_mutex);
 }
 
index d550d40850cde2a8eef8b71c8d4828f7ea49306c..18bdb8c8d4f63cdca7b370d6c40cf0445322ffa7 100644 (file)
@@ -33,14 +33,16 @@ ao_log_csum(__xdata uint8_t *b) __reentrant
        return -sum;
 }
 
-void
+uint8_t
 ao_log_data(__xdata struct ao_log_record *log) __reentrant
 {
+       uint8_t wrote = 0;
        /* set checksum */
        log->csum = 0;
        log->csum = ao_log_csum((__xdata uint8_t *) log);
        ao_mutex_get(&ao_log_mutex); {
                if (ao_log_running) {
+                       wrote = 1;
                        ao_ee_write(ao_log_current_pos,
                                    (uint8_t *) log,
                                    sizeof (struct ao_log_record));
@@ -51,6 +53,7 @@ ao_log_data(__xdata struct ao_log_record *log) __reentrant
                                ao_log_running = 0;
                }
        } ao_mutex_put(&ao_log_mutex);
+       return wrote;
 }
 
 void
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..82d6298f2dbe4751b6de6bd496ea6412f1c9a2d8 100644 (file)
@@ -21,7 +21,6 @@
 
 /* Defines which mark this particular AltOS product */
 
-const uint16_t ao_serial_number = AO_iSerial_NUMBER;
 const char ao_version[] = AO_iVersion_STRING;
 const char ao_manufacturer[] = AO_iManufacturer_STRING;
 const char ao_product[] = AO_iProduct_STRING;
@@ -29,7 +28,7 @@ const char ao_product[] = AO_iProduct_STRING;
 #define LE_WORD(x)    ((x)&0xFF),((uint8_t) (((uint16_t) (x))>>8))
 
 /* USB descriptors in one giant block of bytes */
-const uint8_t ao_usb_descriptors [] =
+__code __at(0x00aa) uint8_t ao_usb_descriptors [] =
 {
        /* Device descriptor */
        0x12,
@@ -40,7 +39,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 0849349edb1d10a59208aebad7929c523da414f4..f4a9d3b265edb1a82ea37ee95cac9568022da9c6 100644 (file)
@@ -275,7 +275,7 @@ __xdata uint8_t ao_radio_done;
 __xdata uint8_t ao_radio_mutex;
 
 void
-ao_radio_general_isr(void) interrupt 16
+ao_radio_general_isr(void) __interrupt 16
 {
        S1CON &= ~0x03;
        if (RFIF & RFIF_IM_TIMEOUT) {
diff --git a/src/ao_romconfig.c b/src/ao_romconfig.c
new file mode 100644 (file)
index 0000000..f3fe61b
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#include "ao.h"
+
+__code __at (0x00a0) uint16_t ao_romconfig_version = AO_ROMCONFIG_VERSION;
+__code __at (0x00a2) uint16_t ao_romconfig_check = ~AO_ROMCONFIG_VERSION;
+__code __at (0x00a4) uint16_t ao_serial_number = 0;
+/*
+ * For 434.550MHz, the frequency value is:
+ *
+ * 434.550e6 / (24e6 / 2**16) = 1186611.2
+ *
+ * This value is stored in a const variable so that
+ * ao-load can change it during programming for
+ * devices that have no eeprom for config data.
+ */
+__code __at (0x00a6) uint32_t ao_radio_cal = 1186611;
index 3f10376658cb10d02d627a9eed6c5b42be24b1c7..a48734c2f22793a957fb9575f6f5d0d2e0d21c26 100644 (file)
@@ -21,7 +21,7 @@ volatile __xdata struct ao_fifo       ao_usart1_rx_fifo;
 volatile __xdata struct ao_fifo        ao_usart1_tx_fifo;
 
 void
-ao_serial_rx1_isr(void) interrupt 3
+ao_serial_rx1_isr(void) __interrupt 3
 {
        if (!ao_fifo_full(ao_usart1_rx_fifo))
                ao_fifo_insert(ao_usart1_rx_fifo, U1DBUF);
@@ -42,7 +42,7 @@ ao_serial_tx1_start(void)
 }
 
 void
-ao_serial_tx1_isr(void) interrupt 14
+ao_serial_tx1_isr(void) __interrupt 14
 {
        UTX1IF = 0;
        ao_serial_tx1_started = 0;
index d1731475c226eee9992ae00315c333b1877f2285..c977fbc8b85a2f997c38e48df05f857306bac658 100644 (file)
@@ -41,7 +41,7 @@ volatile __data uint8_t       ao_adc_interval = 1;
 volatile __data uint8_t        ao_adc_count;
 #endif
 
-void ao_timer_isr(void) interrupt 9
+void ao_timer_isr(void) __interrupt 9
 {
        ++ao_tick_count;
 #if HAS_ADC
index f6e0fcf91b54e83e643b46eeec599f5dacce06db..b55130f2b73366dff311d0130133eb5b5b05ddc3 100644 (file)
@@ -43,7 +43,7 @@ ao_usb_set_interrupts(void)
  * so when we hook that up, fix this
  */
 void
-ao_usb_isr(void) interrupt 6
+ao_usb_isr(void) __interrupt 6
 {
        USBIF = 0;
        ao_usb_iif |= USBIIF;
index e8302df240736848d8800d84f111c77eba15170c..20ed052a1d5cf600dd1eabd7c6078b9542cc0781 100644 (file)
 #include <cc1110.h>
 #include <stdint.h>
 
-sfr at 0xA8 IEN0;              /* Interrupt Enable 0 Register */
+sfr __at 0xA8 IEN0;            /* Interrupt Enable 0 Register */
 
-sbit at 0xA8 RFTXRXIE;         /* RF TX/RX done interrupt enable */
-sbit at 0xA9 ADCIE;            /* ADC interrupt enable */
-sbit at 0xAA URX0IE;           /* USART0 RX interrupt enable */
-sbit at 0xAB URX1IE;           /* USART1 RX interrupt enable (shared with I2S RX) */
-sbit at 0xAB I2SRXIE;          /* I2S RX interrupt enable (shared with USART1 RX) */
-sbit at 0xAC ENCIE;            /* AES encryption/decryption interrupt enable */
-sbit at 0xAD STIE;             /* Sleep Timer interrupt enable */
-sbit at 0xAF EA;               /* Enable All */
+sbit __at 0xA8 RFTXRXIE;       /* RF TX/RX done interrupt enable */
+sbit __at 0xA9 ADCIE;          /* ADC interrupt enable */
+sbit __at 0xAA URX0IE;         /* USART0 RX interrupt enable */
+sbit __at 0xAB URX1IE;         /* USART1 RX interrupt enable (shared with I2S RX) */
+sbit __at 0xAB I2SRXIE;                /* I2S RX interrupt enable (shared with USART1 RX) */
+sbit __at 0xAC ENCIE;          /* AES encryption/decryption interrupt enable */
+sbit __at 0xAD STIE;           /* Sleep Timer interrupt enable */
+sbit __at 0xAF EA;             /* Enable All */
 
 #define IEN0_EA                        (1 << 7)
 #define IEN0_STIE              (1 << 5)
@@ -60,7 +60,7 @@ sbit at 0xAF EA;              /* Enable All */
 #define IEN0_ADCIE             (1 << 1)
 #define IEN0_RFTXRXIE          (1 << 0)
 
-sfr at 0xB8 IEN1;              /* Interrupt Enable 1 Register */
+sfr __at 0xB8 IEN1;            /* Interrupt Enable 1 Register */
 
 #define IEN1_P0IE              (1 << 5)        /* Port 0 interrupt enable */
 #define IEN1_T4IE              (1 << 4)        /* Timer 4 interrupt enable */
@@ -70,7 +70,7 @@ sfr at 0xB8 IEN1;             /* Interrupt Enable 1 Register */
 #define IEN1_DMAIE             (1 << 0)        /* DMA transfer interrupt enable */
 
 /* IEN2 */
-sfr at 0x9A IEN2;              /* Interrupt Enable 2 Register */
+sfr __at 0x9A IEN2;            /* Interrupt Enable 2 Register */
 
 #define IEN2_WDTIE             (1 << 5)        /* Watchdog timer interrupt enable */
 #define IEN2_P1IE              (1 << 4)        /* Port 1 interrupt enable */
@@ -82,7 +82,7 @@ sfr at 0x9A IEN2;             /* Interrupt Enable 2 Register */
 #define IEN2_RFIE              (1 << 0)        /* RF general interrupt enable */
 
 /* CLKCON 0xC6 */
-sfr at 0xC6 CLKCON;            /* Clock Control */
+sfr __at 0xC6 CLKCON;          /* Clock Control */
 
 #define CLKCON_OSC32K_RC       (1 << 7)
 #define CLKCON_OSC32K_XTAL     (0 << 7)
@@ -126,20 +126,20 @@ sfr at 0xC6 CLKCON;               /* Clock Control */
 #define SLEEP_MODE_MASK                (3 << 0)
 
 /* PCON 0x87 */
-sfr at 0x87 PCON;              /* Power Mode Control Register */
+sfr __at 0x87 PCON;            /* Power Mode Control Register */
 
 #define PCON_IDLE              (1 << 0)
 
 /*
  * TCON
  */
-sfr at 0x88 TCON;      /* CPU Interrupt Flag 1 */
+sfr __at 0x88 TCON;            /* CPU Interrupt Flag 1 */
 
-sbit at 0x8F URX1IF;   /* USART1 RX interrupt flag. Automatically cleared */
-sbit at 0x8F I2SRXIF;  /* I2S RX interrupt flag. Automatically cleared */
-sbit at 0x8D ADCIF;    /* ADC interrupt flag. Automatically cleared */
-sbit at 0x8B URX0IF;   /* USART0 RX interrupt flag. Automatically cleared */
-sbit at 0x89 RFTXRXIF; /* RF TX/RX complete interrupt flag. Automatically cleared */
+sbit __at 0x8F URX1IF;         /* USART1 RX interrupt flag. Automatically cleared */
+sbit __at 0x8F I2SRXIF;                /* I2S RX interrupt flag. Automatically cleared */
+sbit __at 0x8D ADCIF;          /* ADC interrupt flag. Automatically cleared */
+sbit __at 0x8B URX0IF;         /* USART0 RX interrupt flag. Automatically cleared */
+sbit __at 0x89 RFTXRXIF;       /* RF TX/RX complete interrupt flag. Automatically cleared */
 
 #define TCON_URX1IF    (1 << 7)
 #define TCON_I2SRXIF   (1 << 7)
@@ -150,10 +150,10 @@ sbit at 0x89 RFTXRXIF;    /* RF TX/RX complete interrupt flag. Automatically cleare
 /*
  * S0CON
  */
-sfr at 0x98 S0CON;     /* CPU Interrupt Flag 2 */
+sfr __at 0x98 S0CON;   /* CPU Interrupt Flag 2 */
 
-sbit at 0x98 ENCIF_0;  /* AES interrupt 0. */
-sbit at 0x99 ENCIF_1;  /* AES interrupt 1. */
+sbit __at 0x98 ENCIF_0;        /* AES interrupt 0. */
+sbit __at 0x99 ENCIF_1;        /* AES interrupt 1. */
 
 #define S0CON_ENCIF_1  (1 << 1)
 #define S0CON_ENCIF_0  (1 << 0)
@@ -161,7 +161,7 @@ sbit at 0x99 ENCIF_1;       /* AES interrupt 1. */
 /*
  * S1CON
  */
-sfr at 0x9B S1CON;     /* CPU Interrupt Flag 3 */
+sfr __at 0x9B S1CON;   /* CPU Interrupt Flag 3 */
 
 #define S1CON_RFIF_1   (1 << 1)
 #define S1CON_RFIF_0   (1 << 0)
@@ -169,15 +169,15 @@ sfr at 0x9B S1CON;        /* CPU Interrupt Flag 3 */
 /*
  * IRCON
  */
-sfr at 0xC0 IRCON;     /* CPU Interrupt Flag 4 */
+sfr __at 0xC0 IRCON;   /* CPU Interrupt Flag 4 */
 
-sbit at 0xC0 DMAIF;    /* DMA complete interrupt flag */
-sbit at 0xC1 T1IF;     /* Timer 1 interrupt flag. Automatically cleared */
-sbit at 0xC2 T2IF;     /* Timer 2 interrupt flag. Automatically cleared */
-sbit at 0xC3 T3IF;     /* Timer 3 interrupt flag. Automatically cleared */
-sbit at 0xC4 T4IF;     /* Timer 4 interrupt flag. Automatically cleared */
-sbit at 0xC5 P0IF;     /* Port0 interrupt flag */
-sbit at 0xC7 STIF;     /* Sleep Timer interrupt flag */
+sbit __at 0xC0 DMAIF;  /* DMA complete interrupt flag */
+sbit __at 0xC1 T1IF;   /* Timer 1 interrupt flag. Automatically cleared */
+sbit __at 0xC2 T2IF;   /* Timer 2 interrupt flag. Automatically cleared */
+sbit __at 0xC3 T3IF;   /* Timer 3 interrupt flag. Automatically cleared */
+sbit __at 0xC4 T4IF;   /* Timer 4 interrupt flag. Automatically cleared */
+sbit __at 0xC5 P0IF;   /* Port0 interrupt flag */
+sbit __at 0xC7 STIF;   /* Sleep Timer interrupt flag */
 
 #define IRCON_DMAIF    (1 << 0)        /* DMA complete interrupt flag */
 #define IRCON_T1IF     (1 << 1)        /* Timer 1 interrupt flag. Automatically cleared */
@@ -190,15 +190,15 @@ sbit at 0xC7 STIF;        /* Sleep Timer interrupt flag */
 /*
  * IRCON2
  */
-sfr at 0xE8 IRCON2;    /* CPU Interrupt Flag 5 */
+sfr __at 0xE8 IRCON2;  /* CPU Interrupt Flag 5 */
 
-sbit at 0xE8 USBIF;    /* USB interrupt flag (shared with Port2) */
-sbit at 0xE8 P2IF;     /* Port2 interrupt flag (shared with USB) */
-sbit at 0xE9 UTX0IF;   /* USART0 TX interrupt flag */
-sbit at 0xEA UTX1IF;   /* USART1 TX interrupt flag (shared with I2S TX) */
-sbit at 0xEA I2STXIF;  /* I2S TX interrupt flag (shared with USART1 TX) */
-sbit at 0xEB P1IF;     /* Port1 interrupt flag */
-sbit at 0xEC WDTIF;    /* Watchdog timer interrupt flag */
+sbit __at 0xE8 USBIF;  /* USB interrupt flag (shared with Port2) */
+sbit __at 0xE8 P2IF;   /* Port2 interrupt flag (shared with USB) */
+sbit __at 0xE9 UTX0IF; /* USART0 TX interrupt flag */
+sbit __at 0xEA UTX1IF; /* USART1 TX interrupt flag (shared with I2S TX) */
+sbit __at 0xEA I2STXIF;        /* I2S TX interrupt flag (shared with USART1 TX) */
+sbit __at 0xEB P1IF;   /* Port1 interrupt flag */
+sbit __at 0xEC WDTIF;  /* Watchdog timer interrupt flag */
 
 #define IRCON2_USBIF   (1 << 0)        /* USB interrupt flag (shared with Port2) */
 #define IRCON2_P2IF    (1 << 0)        /* Port2 interrupt flag (shared with USB) */
@@ -225,8 +225,8 @@ sbit at 0xEC WDTIF; /* Watchdog timer interrupt flag */
  * Priority = (IP1 << 1) | IP0. Higher priority interrupts served first
  */
 
-sfr at 0xB9 IP1;       /* Interrupt Priority 1 */
-sfr at 0xA9 IP0;       /* Interrupt Priority 0 */
+sfr __at 0xB9 IP1;     /* Interrupt Priority 1 */
+sfr __at 0xA9 IP0;     /* Interrupt Priority 0 */
 
 #define IP1_IPG5       (1 << 5)
 #define IP1_IPG4       (1 << 4)
@@ -286,13 +286,13 @@ sfr at 0xA9 IP0;  /* Interrupt Priority 0 */
  */
 
 /* Timer count */
-sfr at 0xCA T3CNT;
-sfr at 0xEA T4CNT;
+sfr __at 0xCA T3CNT;
+sfr __at 0xEA T4CNT;
 
 /* Timer control */
 
-sfr at 0xCB T3CTL;
-sfr at 0xEB T4CTL;
+sfr __at 0xCB T3CTL;
+sfr __at 0xEB T4CTL;
 
 #define TxCTL_DIV_1            (0 << 5)
 #define TxCTL_DIV_2            (1 << 5)
@@ -312,10 +312,10 @@ sfr at 0xEB T4CTL;
 
 /* Timer 4 channel 0 compare control */
 
-sfr at 0xCC T3CCTL0;
-sfr at 0xCE T3CCTL1;
-sfr at 0xEC T4CCTL0;
-sfr at 0xEE T4CCTL1;
+sfr __at 0xCC T3CCTL0;
+sfr __at 0xCE T3CCTL1;
+sfr __at 0xEC T4CCTL0;
+sfr __at 0xEE T4CCTL1;
 
 #define TxCCTLy_IM                     (1 << 6)
 #define TxCCTLy_CMP_SET                        (0 << 3)
@@ -328,16 +328,16 @@ sfr at 0xEE T4CCTL1;
 #define TxCCTLy_CMP_MODE_ENABLE                (1 << 2)
 
 /* Timer compare value */
-sfr at 0xCD T3CC0;
-sfr at 0xCF T3CC1;
-sfr at 0xED T4CC0;
-sfr at 0xEF T4CC1;
+sfr __at 0xCD T3CC0;
+sfr __at 0xCF T3CC1;
+sfr __at 0xED T4CC0;
+sfr __at 0xEF T4CC1;
 
 /*
  * Peripheral control
  */
 
-sfr at 0xf1 PERCFG;
+sfr __at 0xf1 PERCFG;
 #define PERCFG_T1CFG_ALT_1      (0 << 6)
 #define PERCFG_T1CFG_ALT_2      (1 << 6)
 #define PERCFG_T1CFG_ALT_MASK   (1 << 6)
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