Merge branch 'master' of git://git.gag.com/fw/altos
authorAnthony Towns <aj@erisian.com.au>
Tue, 28 Sep 2010 02:55:47 +0000 (12:55 +1000)
committerAnthony Towns <aj@erisian.com.au>
Tue, 28 Sep 2010 02:55:47 +0000 (12:55 +1000)
13 files changed:
ao-tools/altosui/AltosCSV.java
ao-tools/altosui/AltosCSVUI.java
ao-tools/altosui/AltosDisplayThread.java [new file with mode: 0644]
ao-tools/altosui/AltosEepromIterable.java [new file with mode: 0644]
ao-tools/altosui/AltosEepromReader.java [deleted file]
ao-tools/altosui/AltosLogfileChooser.java
ao-tools/altosui/AltosRecord.java
ao-tools/altosui/AltosRecordIterable.java [new file with mode: 0644]
ao-tools/altosui/AltosReplayThread.java [new file with mode: 0644]
ao-tools/altosui/AltosTelemetryIterable.java [new file with mode: 0644]
ao-tools/altosui/AltosTelemetryReader.java [deleted file]
ao-tools/altosui/AltosUI.java
ao-tools/altosui/Makefile.am

index f7b3c03c0c60be6af31c8ed13ac208b110c0a91e..c01d55a8dd7b7bfa156e68ba50e8f3bc773c0698 100644 (file)
@@ -45,6 +45,7 @@ public class AltosCSV {
         *      callsign
         *      time (seconds since boost)
         *      rssi
+        *      link quality
         *
         * Flight status
         *      state
@@ -79,21 +80,22 @@ public class AltosCSV {
         *      from_pad_azimuth (deg true)
         *      from_pad_range (m)
         *      from_pad_elevation (deg from horizon)
+        *      hdop
         *
         * GPS Sat data
-        *      hdop
         *      C/N0 data for all 32 valid SDIDs
         */
 
        void write_general_header() {
-               out.printf("version,serial,flight,call,time,rssi");
+               out.printf("version,serial,flight,call,time,rssi,lqi");
        }
 
        void write_general(AltosRecord record) {
-               out.printf("%s,%d,%d,%s,%8.2f,%4d",
+               out.printf("%s, %d, %d, %s, %8.2f, %4d, %3d",
                           record.version, record.serial, record.flight, record.callsign,
                           (double) record.time,
-                          record.rssi);
+                          record.rssi,
+                          record.status & 0x7f);
        }
 
        void write_flight_header() {
@@ -123,7 +125,7 @@ public class AltosCSV {
        }
 
        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");
+               out.printf("connected,locked,nsat,latitude,longitude,altitude,year,month,day,hour,minute,second,pad_dist,pad_range,pad_az,pad_el,hdop");
        }
 
        void write_gps(AltosRecord record) {
@@ -135,7 +137,7 @@ public class AltosCSV {
                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",
+               out.printf("%2d,%2d,%3d,%12.7f,%12.7f,%6d,%5d,%3d,%3d,%3d,%3d,%3d,%9.0f,%9.0f,%4.0f,%4.0f,%6.1f",
                           gps.connected?1:0,
                           gps.locked?1:0,
                           gps.nsat,
@@ -151,7 +153,33 @@ public class AltosCSV {
                           from_pad.distance,
                           state.range,
                           from_pad.bearing,
-                          state.elevation);
+                          state.elevation,
+                          gps.hdop);
+       }
+
+       void write_gps_sat_header() {
+               for(int i = 1; i <= 32; i++) {
+                       out.printf("sat%02d", i);
+                       if (i != 32)
+                               out.printf(",");
+               }
+       }
+
+       void write_gps_sat(AltosRecord record) {
+               AltosGPS        gps = record.gps;
+               for(int i = 1; i <= 32; i++) {
+                       int     c_n0 = 0;
+                       if (gps != null && gps.cc_gps_sat != null) {
+                               for(int j = 0; j < gps.cc_gps_sat.length; j++)
+                                       if (gps.cc_gps_sat[j].svid == i) {
+                                               c_n0 = gps.cc_gps_sat[j].c_n0;
+                                               break;
+                                       }
+                       }
+                       out.printf ("%3d", c_n0);
+                       if (i != 32)
+                               out.printf(",");
+               }
        }
 
        void write_header() {
@@ -159,6 +187,7 @@ public class AltosCSV {
                out.printf(","); write_flight_header();
                out.printf(","); write_basic_header();
                out.printf(","); write_gps_header();
+               out.printf(","); write_gps_sat_header();
                out.printf ("\n");
        }
 
@@ -167,7 +196,8 @@ public class AltosCSV {
                write_general(record); out.printf(",");
                write_flight(record); out.printf(",");
                write_basic(record); out.printf(",");
-               write_gps(record);
+               write_gps(record); out.printf(",");
+               write_gps_sat(record);
                out.printf ("\n");
        }
 
@@ -207,22 +237,10 @@ public class AltosCSV {
                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 void write(AltosRecordIterable iterable) {
+               iterable.write_comments(out());
+               for (AltosRecord r : iterable)
+                       write(r);
        }
 
        public AltosCSV(File in_name) throws FileNotFoundException {
index 643d4112ae96666107a33e43c8f0d2f59fca6605..4eb72de820f9f6bd0af3a94fc75c0fcb4fc1ae90 100644 (file)
@@ -35,17 +35,17 @@ public class AltosCSVUI
        extends JDialog
        implements Runnable, ActionListener
 {
-       JFrame          frame;
-       Thread          thread;
-       AltosReader     reader;
-       AltosCSV        writer;
+       JFrame                  frame;
+       Thread                  thread;
+       AltosRecordIterable     iterable;
+       AltosCSV                writer;
 
        public void run() {
                AltosLogfileChooser     chooser;
 
                chooser = new AltosLogfileChooser(frame);
-               reader = chooser.runDialog();
-               if (reader == null)
+               iterable = chooser.runDialog();
+               if (iterable == null)
                        return;
                JFileChooser    csv_chooser;
 
@@ -67,8 +67,7 @@ public class AltosCSVUI
                                                              "Cannot open file",
                                                              JOptionPane.ERROR_MESSAGE);
                        }
-                       writer.write(reader);
-                       reader.close();
+                       writer.write(iterable);
                        writer.close();
                }
        }
diff --git a/ao-tools/altosui/AltosDisplayThread.java b/ao-tools/altosui/AltosDisplayThread.java
new file mode 100644 (file)
index 0000000..9cc3d5c
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * 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 AltosDisplayThread extends Thread {
+
+       Frame           parent;
+       IdleThread      idle_thread;
+       AltosVoice      voice;
+       String          name;
+       int             crc_errors;
+       AltosStatusTable flightStatus;
+       AltosInfoTable flightInfo;
+
+       class IdleThread extends Thread {
+
+               boolean started;
+               private AltosState state;
+               int     reported_landing;
+               int     report_interval;
+               long    report_time;
+
+               public synchronized void report(boolean last) {
+                       if (state == null)
+                               return;
+
+                       /* reset the landing count once we hear about a new flight */
+                       if (state.state < Altos.ao_flight_drogue)
+                               reported_landing = 0;
+
+                       /* Shut up once the rocket is on the ground */
+                       if (reported_landing > 2) {
+                               return;
+                       }
+
+                       /* If the rocket isn't on the pad, then report height */
+                       if (Altos.ao_flight_drogue <= state.state &&
+                           state.state < Altos.ao_flight_landed &&
+                           state.range >= 0)
+                       {
+                               voice.speak("Height %d, bearing %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;
+                       }
+
+                       /* If the rocket is coming down, check to see if it has landed;
+                        * either we've got a landed report or we haven't heard from it in
+                        * a long time
+                        */
+                       if (state.state >= Altos.ao_flight_drogue &&
+                           (last ||
+                            System.currentTimeMillis() - state.report_time >= 15000 ||
+                            state.state == Altos.ao_flight_landed))
+                       {
+                               if (Math.abs(state.baro_speed) < 20 && state.height < 100)
+                                       voice.speak("rocket landed safely");
+                               else
+                                       voice.speak("rocket may have crashed");
+                               if (state.from_pad != null)
+                                       voice.speak("Bearing %d degrees, range %d meters.",
+                                                   (int) (state.from_pad.bearing + 0.5),
+                                                   (int) (state.from_pad.distance + 0.5));
+                               ++reported_landing;
+                       }
+               }
+
+               long now () {
+                       return System.currentTimeMillis();
+               }
+
+               void set_report_time() {
+                       report_time = now() + report_interval;
+               }
+
+               public void run () {
+
+                       reported_landing = 0;
+                       state = null;
+                       report_interval = 10000;
+                       try {
+                               for (;;) {
+                                       set_report_time();
+                                       for (;;) {
+                                               voice.drain();
+                                               synchronized (this) {
+                                                       long    sleep_time = report_time - now();
+                                                       if (sleep_time <= 0)
+                                                               break;
+                                                       wait(sleep_time);
+                                               }
+                                       }
+                                       report(false);
+                               }
+                       } catch (InterruptedException ie) {
+                               try {
+                                       voice.drain();
+                               } catch (InterruptedException iie) { }
+                       }
+               }
+
+               public synchronized void notice(AltosState new_state, boolean spoken) {
+                       AltosState old_state = state;
+                       state = new_state;
+                       if (!started && state.state > Altos.ao_flight_pad) {
+                               started = true;
+                               start();
+                       }
+
+                       if (state.state < Altos.ao_flight_drogue)
+                               report_interval = 10000;
+                       else
+                               report_interval = 20000;
+                       if (old_state != null && old_state.state != state.state) {
+                               report_time = now();
+                               this.notify();
+                       } else if (spoken)
+                               set_report_time();
+               }
+       }
+
+       void init() { }
+
+       AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; }
+
+       void close(boolean interrupted) { }
+
+       void update(AltosState state) throws InterruptedException { }
+
+       boolean tell(AltosState state, AltosState old_state) {
+               boolean ret = false;
+               if (old_state == null || old_state.state != state.state) {
+                       voice.speak(state.data.state());
+                       if ((old_state == null || old_state.state <= Altos.ao_flight_boost) &&
+                           state.state > Altos.ao_flight_boost) {
+                               voice.speak("max speed: %d meters per second.",
+                                           (int) (state.max_speed + 0.5));
+                               ret = true;
+                       } else if ((old_state == null || old_state.state < Altos.ao_flight_drogue) &&
+                                  state.state >= Altos.ao_flight_drogue) {
+                               voice.speak("max height: %d meters.",
+                                           (int) (state.max_height + 0.5));
+                               ret = true;
+                       }
+               }
+               if (old_state == null || old_state.gps_ready != state.gps_ready) {
+                       if (state.gps_ready) {
+                               voice.speak("GPS ready");
+                               ret = true;
+                       }
+                       else if (old_state != null) {
+                               voice.speak("GPS lost");
+                               ret = true;
+                       }
+               }
+               old_state = state;
+               return ret;
+       }
+
+       void show(AltosState state, int crc_errors) {
+               if (state != null) {
+                       flightStatus.set(state);
+                       flightInfo.show(state, crc_errors);
+               }
+       }
+
+       public void run() {
+               boolean         interrupted = false;
+               String          line;
+               AltosState      state = null;
+               AltosState      old_state = null;
+               boolean         told;
+
+               idle_thread = new IdleThread();
+
+               flightInfo.clear();
+               try {
+                       for (;;) {
+                               try {
+                                       AltosRecord record = read();
+                                       if (record == null)
+                                               break;
+                                       old_state = state;
+                                       state = new AltosState(record, state);
+                                       update(state);
+                                       show(state, crc_errors);
+                                       told = tell(state, old_state);
+                                       idle_thread.notice(state, told);
+                               } catch (ParseException pp) {
+                                       System.out.printf("Parse error: %d \"%s\"\n", pp.getErrorOffset(), pp.getMessage());
+                               } catch (AltosCRCException ce) {
+                                       ++crc_errors;
+                                       show(state, crc_errors);
+                               }
+                       }
+               } catch (InterruptedException ee) {
+                       interrupted = true;
+               } catch (IOException ie) {
+                       JOptionPane.showMessageDialog(parent,
+                                                     String.format("Error reading from \"%s\"", name),
+                                                     "Telemetry Read Error",
+                                                     JOptionPane.ERROR_MESSAGE);
+               } finally {
+                       close(interrupted);
+                       idle_thread.interrupt();
+                       try {
+                               idle_thread.join();
+                       } catch (InterruptedException ie) {}
+               }
+       }
+
+       public AltosDisplayThread(Frame in_parent, AltosVoice in_voice, AltosStatusTable in_status, AltosInfoTable in_info) {
+               parent = in_parent;
+               voice = in_voice;
+               flightStatus = in_status;
+               flightInfo = in_info;
+       }
+
+       public void report() {
+               if (idle_thread != null)
+                       idle_thread.report(true);
+       }
+
+}
diff --git a/ao-tools/altosui/AltosEepromIterable.java b/ao-tools/altosui/AltosEepromIterable.java
new file mode 100644 (file)
index 0000000..2f1e7e9
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * 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, boolean prev_tick_valid)
+               throws ParseException {
+               super(line);
+               if (prev_tick_valid) {
+                       tick |= (prev_tick & ~0xffff);
+                       if (tick < prev_tick) {
+                               if (prev_tick - tick > 0x8000)
+                                       tick += 0x10000;
+                       } else {
+                               if (tick - prev_tick > 0x8000)
+                                       tick -= 0x10000;
+                       }
+               }
+               index = in_index;
+       }
+
+       public AltosOrderedRecord(int in_cmd, int in_tick, int in_a, int in_b, int in_index) {
+               super(in_cmd, in_tick, in_a, in_b);
+               index = in_index;
+       }
+
+       public int compareTo(AltosOrderedRecord o) {
+               int     tick_diff = tick - o.tick;
+               if (tick_diff != 0)
+                       return tick_diff;
+               return index - o.index;
+       }
+}
+
+public class AltosEepromIterable extends AltosRecordIterable {
+
+       static final int        seen_flight = 1;
+       static final int        seen_sensor = 2;
+       static final int        seen_temp_volt = 4;
+       static final int        seen_deploy = 8;
+       static final int        seen_gps_time = 16;
+       static final int        seen_gps_lat = 32;
+       static final int        seen_gps_lon = 64;
+
+       static final int        seen_basic = seen_flight|seen_sensor|seen_temp_volt|seen_deploy;
+
+       AltosEepromRecord       flight_record;
+       AltosEepromRecord       gps_date_record;
+
+       TreeSet<AltosOrderedRecord>     records;
+
+       LinkedList<AltosRecord> list;
+
+       class EepromState {
+               int     seen;
+               int     n_pad_samples;
+               double  ground_pres;
+               int     gps_tick;
+               int     boost_tick;
+
+               EepromState() {
+                       seen = 0;
+                       n_pad_samples = 0;
+                       ground_pres = 0.0;
+                       gps_tick = 0;
+               }
+       }
+
+       void update_state(AltosRecord state, AltosEepromRecord record, EepromState eeprom) {
+               state.tick = record.tick;
+               switch (record.cmd) {
+               case Altos.AO_LOG_FLIGHT:
+                       eeprom.seen |= seen_flight;
+                       state.ground_accel = record.a;
+                       state.flight_accel = record.a;
+                       state.flight = record.b;
+                       eeprom.boost_tick = record.tick;
+                       break;
+               case Altos.AO_LOG_SENSOR:
+                       state.accel = record.a;
+                       state.pres = record.b;
+                       if (state.state < Altos.ao_flight_boost) {
+                               eeprom.n_pad_samples++;
+                               eeprom.ground_pres += state.pres;
+                               state.ground_pres = (int) (eeprom.ground_pres / eeprom.n_pad_samples);
+                               state.flight_pres = state.ground_pres;
+                       } else {
+                               state.flight_pres = (state.flight_pres * 15 + state.pres) / 16;
+                               state.flight_accel = (state.flight_accel * 15 + state.accel) / 16;
+                               state.flight_vel += (state.accel_plus_g - state.accel);
+                       }
+                       eeprom.seen |= seen_sensor;
+                       break;
+               case Altos.AO_LOG_TEMP_VOLT:
+                       state.temp = record.a;
+                       state.batt = record.b;
+                       eeprom.seen |= seen_temp_volt;
+                       break;
+               case Altos.AO_LOG_DEPLOY:
+                       state.drogue = record.a;
+                       state.main = record.b;
+                       eeprom.seen |= seen_deploy;
+                       break;
+               case Altos.AO_LOG_STATE:
+                       state.state = record.a;
+                       break;
+               case Altos.AO_LOG_GPS_TIME:
+                       eeprom.gps_tick = state.tick;
+                       AltosGPS old = state.gps;
+                       state.gps = new AltosGPS();
+
+                       /* GPS date doesn't get repeated through the file */
+                       if (old != null) {
+                               state.gps.year = old.year;
+                               state.gps.month = old.month;
+                               state.gps.day = old.day;
+                       }
+                       state.gps.hour = (record.a & 0xff);
+                       state.gps.minute = (record.a >> 8);
+                       state.gps.second = (record.b & 0xff);
+
+                       int flags = (record.b >> 8);
+                       state.gps.connected = (flags & Altos.AO_GPS_RUNNING) != 0;
+                       state.gps.locked = (flags & Altos.AO_GPS_VALID) != 0;
+                       state.gps.date_valid = (flags & Altos.AO_GPS_DATE_VALID) != 0;
+                       state.gps.nsat = (flags & Altos.AO_GPS_NUM_SAT_MASK) >>
+                               Altos.AO_GPS_NUM_SAT_SHIFT;
+                       break;
+               case Altos.AO_LOG_GPS_LAT:
+                       int lat32 = record.a | (record.b << 16);
+                       state.gps.lat = (double) lat32 / 1e7;
+                       break;
+               case Altos.AO_LOG_GPS_LON:
+                       int lon32 = record.a | (record.b << 16);
+                       state.gps.lon = (double) lon32 / 1e7;
+                       break;
+               case Altos.AO_LOG_GPS_ALT:
+                       state.gps.alt = record.a;
+                       break;
+               case Altos.AO_LOG_GPS_SAT:
+                       if (state.tick == eeprom.gps_tick) {
+                               int svid = record.a;
+                               int c_n0 = record.b >> 8;
+                               state.gps.add_sat(svid, c_n0);
+                       }
+                       break;
+               case Altos.AO_LOG_GPS_DATE:
+                       state.gps.year = (record.a & 0xff) + 2000;
+                       state.gps.month = record.a >> 8;
+                       state.gps.day = record.b & 0xff;
+                       break;
+
+               case Altos.AO_LOG_CONFIG_VERSION:
+                       break;
+               case Altos.AO_LOG_MAIN_DEPLOY:
+                       break;
+               case Altos.AO_LOG_APOGEE_DELAY:
+                       break;
+               case Altos.AO_LOG_RADIO_CHANNEL:
+                       break;
+               case Altos.AO_LOG_CALLSIGN:
+                       state.callsign = record.data;
+                       break;
+               case Altos.AO_LOG_ACCEL_CAL:
+                       state.accel_plus_g = record.a;
+                       state.accel_minus_g = record.b;
+                       break;
+               case Altos.AO_LOG_RADIO_CAL:
+                       break;
+               case Altos.AO_LOG_MANUFACTURER:
+                       break;
+               case Altos.AO_LOG_PRODUCT:
+                       break;
+               case Altos.AO_LOG_SERIAL_NUMBER:
+                       state.serial = record.a;
+                       break;
+               case Altos.AO_LOG_SOFTWARE_VERSION:
+                       break;
+               }
+       }
+
+       LinkedList<AltosRecord> make_list() {
+               LinkedList<AltosRecord>         list = new LinkedList<AltosRecord>();
+               Iterator<AltosOrderedRecord>    iterator = records.iterator();
+               AltosOrderedRecord              record = null;
+               AltosRecord                     state = new AltosRecord();
+               boolean                         last_reported = false;
+               EepromState                     eeprom = new EepromState();
+
+               state.state = Altos.ao_flight_pad;
+               state.accel_plus_g = 15758;
+               state.accel_minus_g = 16294;
+
+               /* Pull in static data from the flight and gps_date records */
+               if (flight_record != null)
+                       update_state(state, flight_record, eeprom);
+               if (gps_date_record != null)
+                       update_state(state, gps_date_record, eeprom);
+
+               while (iterator.hasNext()) {
+                       record = iterator.next();
+                       if ((eeprom.seen & seen_basic) == seen_basic && record.tick != state.tick) {
+                               AltosRecord r = new AltosRecord(state);
+                               r.time = (r.tick - eeprom.boost_tick) / 100.0;
+                               list.add(r);
+                       }
+                       update_state(state, record, eeprom);
+               }
+               AltosRecord r = new AltosRecord(state);
+               r.time = (r.tick - eeprom.boost_tick) / 100.0;
+               list.add(r);
+               return list;
+       }
+
+       public Iterator<AltosRecord> iterator() {
+               if (list == null)
+                       list = make_list();
+               return list.iterator();
+       }
+
+       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);
+
+               bad.a = new_hour + (new_minute << 8);
+               bad.b = new_second + (flags << 8);
+       }
+
+       /*
+        * Read the whole file, dumping records into a RB tree so
+        * we can enumerate them in time order -- the eeprom data
+        * are sometimes out of order with GPS data getting timestamps
+        * matching the first packet out of the GPS unit but not
+        * written until the final GPS packet has been received.
+        */
+       public AltosEepromIterable (FileInputStream input) {
+               records = new TreeSet<AltosOrderedRecord>();
+
+               AltosOrderedRecord last_gps_time = null;
+
+               int index = 0;
+               int prev_tick = 0;
+               boolean prev_tick_valid = false;
+               boolean missing_time = false;
+
+               try {
+                       for (;;) {
+                               String line = AltosRecord.gets(input);
+                               if (line == null)
+                                       break;
+                               AltosOrderedRecord record = new AltosOrderedRecord(line, index++, prev_tick, prev_tick_valid);
+                               if (record == null)
+                                       break;
+                               if (record.cmd == Altos.AO_LOG_INVALID)
+                                       continue;
+                               prev_tick = record.tick;
+                               if (record.cmd < Altos.AO_LOG_CONFIG_VERSION)
+                                       prev_tick_valid = true;
+                               if (record.cmd == Altos.AO_LOG_FLIGHT) {
+                                       flight_record = record;
+                                       continue;
+                               }
+
+                               /* Two firmware bugs caused the loss of some GPS data.
+                                * The flight date would never be recorded, and often
+                                * the flight time would get overwritten by another
+                                * record. Detect the loss of the GPS date and fix up the
+                                * missing time records
+                                */
+                               if (record.cmd == Altos.AO_LOG_GPS_DATE) {
+                                       gps_date_record = record;
+                                       continue;
+                               }
+
+                               /* go back and fix up any missing time values */
+                               if (record.cmd == Altos.AO_LOG_GPS_TIME) {
+                                       last_gps_time = record;
+                                       if (missing_time) {
+                                               Iterator<AltosOrderedRecord> iterator = records.iterator();
+                                               while (iterator.hasNext()) {
+                                                       AltosOrderedRecord old = iterator.next();
+                                                       if (old.cmd == Altos.AO_LOG_GPS_TIME &&
+                                                           old.a == -1 && old.b == -1)
+                                                       {
+                                                               update_time(record, old);
+                                                       }
+                                               }
+                                               missing_time = false;
+                                       }
+                               }
+
+                               if (record.cmd == Altos.AO_LOG_GPS_LAT) {
+                                       if (last_gps_time == null || last_gps_time.tick != record.tick) {
+                                               AltosOrderedRecord add_gps_time = new AltosOrderedRecord(Altos.AO_LOG_GPS_TIME,
+                                                                                                        record.tick,
+                                                                                                        -1, -1, index-1);
+                                               if (last_gps_time != null)
+                                                       update_time(last_gps_time, add_gps_time);
+                                               else
+                                                       missing_time = true;
+
+                                               records.add(add_gps_time);
+                                               record.index = index++;
+                                       }
+                               }
+                               records.add(record);
+
+                               /* Bail after reading the 'landed' record; we're all done */
+                               if (record.cmd == Altos.AO_LOG_STATE &&
+                                   record.a == Altos.ao_flight_landed)
+                                       break;
+                       }
+               } catch (IOException io) {
+               } catch (ParseException pe) {
+               }
+               try {
+                       input.close();
+               } catch (IOException ie) {
+               }
+       }
+}
diff --git a/ao-tools/altosui/AltosEepromReader.java b/ao-tools/altosui/AltosEepromReader.java
deleted file mode 100644 (file)
index 03e7381..0000000
+++ /dev/null
@@ -1,406 +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.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;
-
-       int                     n_pad_samples;
-
-       int                     gps_tick;
-
-       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;
-                                       AltosRecord r = new AltosRecord(state);
-                                       r.time = (r.tick - boost_tick) / 100.0;
-                                       return r;
-                               }
-                               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:
-                               /* recorded when first read from the file */
-                               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;
-                               } 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;
-                               AltosGPS old = state.gps;
-                               state.gps = new AltosGPS();
-
-                               /* GPS date doesn't get repeated through the file */
-                               if (old != null) {
-                                       state.gps.year = old.year;
-                                       state.gps.month = old.month;
-                                       state.gps.day = old.day;
-                               }
-                               state.gps.hour = (record.a & 0xff);
-                               state.gps.minute = (record.a >> 8);
-                               state.gps.second = (record.b & 0xff);
-                               int flags = (record.b >> 8);
-                               state.gps.connected = (flags & Altos.AO_GPS_RUNNING) != 0;
-                               state.gps.locked = (flags & Altos.AO_GPS_VALID) != 0;
-                               state.gps.date_valid = (flags & Altos.AO_GPS_DATE_VALID) != 0;
-                               state.gps.nsat = (flags & Altos.AO_GPS_NUM_SAT_MASK) >>
-                                       Altos.AO_GPS_NUM_SAT_SHIFT;
-                               break;
-                       case Altos.AO_LOG_GPS_LAT:
-                               int lat32 = record.a | (record.b << 16);
-                               state.gps.lat = (double) lat32 / 1e7;
-                               break;
-                       case Altos.AO_LOG_GPS_LON:
-                               int lon32 = record.a | (record.b << 16);
-                               state.gps.lon = (double) lon32 / 1e7;
-                               break;
-                       case Altos.AO_LOG_GPS_ALT:
-                               state.gps.alt = record.a;
-                               break;
-                       case Altos.AO_LOG_GPS_SAT:
-                               if (state.tick == gps_tick) {
-                                       int svid = record.a;
-                                       int c_n0 = record.b >> 8;
-                                       state.gps.add_sat(svid, c_n0);
-                               }
-                               break;
-                       case Altos.AO_LOG_GPS_DATE:
-                               state.gps.year = (record.a & 0xff) + 2000;
-                               state.gps.month = record.a >> 8;
-                               state.gps.day = record.b & 0xff;
-                               break;
-
-                       case Altos.AO_LOG_CONFIG_VERSION:
-                               break;
-                       case Altos.AO_LOG_MAIN_DEPLOY:
-                               break;
-                       case Altos.AO_LOG_APOGEE_DELAY:
-                               break;
-                       case Altos.AO_LOG_RADIO_CHANNEL:
-                               break;
-                       case Altos.AO_LOG_CALLSIGN:
-                               state.callsign = record.data;
-                               break;
-                       case Altos.AO_LOG_ACCEL_CAL:
-                               state.accel_plus_g = record.a;
-                               state.accel_minus_g = record.b;
-                               break;
-                       case Altos.AO_LOG_RADIO_CAL:
-                               break;
-                       case Altos.AO_LOG_MANUFACTURER:
-                               break;
-                       case Altos.AO_LOG_PRODUCT:
-                               break;
-                       case Altos.AO_LOG_SERIAL_NUMBER:
-                               state.serial = record.a;
-                               break;
-                       case Altos.AO_LOG_SOFTWARE_VERSION:
-                               break;
-                       }
-                       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);
-
-               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;
-                               if (record.cmd == Altos.AO_LOG_INVALID)
-                                       continue;
-                               tick = record.tick;
-                               if (record.cmd == Altos.AO_LOG_FLIGHT) {
-                                       state.ground_accel = record.a;
-                                       state.flight_accel = record.a;
-                                       state.flight = record.b;
-                                       boost_tick = tick;
-                                       seen |= seen_flight;
-                               }
-
-                               /* 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) {
-                                               Iterator<AltosOrderedRecord> iterator = records.iterator();
-                                               while (iterator.hasNext()) {
-                                                       AltosOrderedRecord old = iterator.next();
-                                                       if (old.cmd == Altos.AO_LOG_GPS_TIME &&
-                                                           old.a == -1 && old.b == -1)
-                                                       {
-                                                               update_time(record, old);
-                                                       }
-                                               }
-                                               missing_time = false;
-                                       }
-                               }
-
-                               if (record.cmd == Altos.AO_LOG_GPS_LAT) {
-                                       if (last_gps_time == null || last_gps_time.tick != record.tick) {
-                                               AltosOrderedRecord add_gps_time = new AltosOrderedRecord(Altos.AO_LOG_GPS_TIME,
-                                                                                                        record.tick,
-                                                                                                        -1, -1, index-1);
-                                               if (last_gps_time != null)
-                                                       update_time(last_gps_time, add_gps_time);
-                                               else
-                                                       missing_time = true;
-
-                                               records.add(add_gps_time);
-                                               record.index = index++;
-                                       }
-                               }
-                               records.add(record);
-
-                               /* Bail after reading the 'landed' record; we're all done */
-                               if (record.cmd == Altos.AO_LOG_STATE &&
-                                   record.a == Altos.ao_flight_landed)
-                                       break;
-                       }
-               } catch (IOException io) {
-               } catch (ParseException pe) {
-               }
-               record_iterator = records.iterator();
-               try {
-                       input.close();
-               } catch (IOException ie) {
-               }
-       }
-}
index 36b51de65c993bece80378fb3d11b2d0bafb5f24..8b9d77d69cba6f70cd0d385b20ace70c03ca569e 100644 (file)
@@ -27,11 +27,6 @@ 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;
@@ -45,7 +40,7 @@ public class AltosLogfileChooser extends JFileChooser {
                return file;
        }
 
-       public AltosReader runDialog() {
+       public AltosRecordIterable runDialog() {
                int     ret;
 
                ret = showOpenDialog(frame);
@@ -59,9 +54,9 @@ public class AltosLogfileChooser extends JFileChooser {
 
                                in = new FileInputStream(file);
                                if (filename.endsWith("eeprom"))
-                                       return new AltosEepromReader(in);
+                                       return new AltosEepromIterable(in);
                                else
-                                       return new AltosTelemetryReader(in);
+                                       return new AltosTelemetryIterable(in);
                        } catch (FileNotFoundException fe) {
                                JOptionPane.showMessageDialog(frame,
                                                              filename,
index 18c6079d01d84ea245c78a55b8f2e2bf28553ced..004847678b71feff904b0295aff4cfb0338b14b2 100644 (file)
@@ -142,7 +142,7 @@ public class AltosRecord {
                return counts_per_g / 9.80665;
        }
        public double acceleration() {
-               return (accel_plus_g - accel) / accel_counts_per_mss();
+               return (ground_accel - accel) / accel_counts_per_mss();
        }
 
        public double accel_speed() {
diff --git a/ao-tools/altosui/AltosRecordIterable.java b/ao-tools/altosui/AltosRecordIterable.java
new file mode 100644 (file)
index 0000000..147ecc1
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+public abstract class AltosRecordIterable implements Iterable<AltosRecord> {
+       public abstract Iterator<AltosRecord> iterator();
+       public void write_comments(PrintStream out) { }
+}
diff --git a/ao-tools/altosui/AltosReplayThread.java b/ao-tools/altosui/AltosReplayThread.java
new file mode 100644 (file)
index 0000000..b418160
--- /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.Altos;
+import altosui.AltosSerial;
+import altosui.AltosSerialMonitor;
+import altosui.AltosRecord;
+import altosui.AltosTelemetry;
+import altosui.AltosState;
+import altosui.AltosDeviceDialog;
+import altosui.AltosPreferences;
+import altosui.AltosLog;
+import altosui.AltosVoice;
+import altosui.AltosFlightInfoTableModel;
+import altosui.AltosChannelMenu;
+import altosui.AltosFlashUI;
+import altosui.AltosLogfileChooser;
+import altosui.AltosCSVUI;
+import altosui.AltosLine;
+import altosui.AltosStatusTable;
+import altosui.AltosInfoTable;
+import altosui.AltosDisplayThread;
+
+/*
+ * Open an existing telemetry file and replay it in realtime
+ */
+
+public class AltosReplayThread extends AltosDisplayThread {
+       Iterator<AltosRecord>   iterator;
+       String                  name;
+
+       public AltosRecord read() {
+               if (iterator.hasNext())
+                       return iterator.next();
+               return null;
+       }
+
+       public void close (boolean interrupted) {
+               if (!interrupted)
+                       report();
+       }
+
+       void update(AltosState state) throws InterruptedException {
+               /* Make it run in realtime after the rocket leaves the pad */
+               if (state.state > Altos.ao_flight_pad)
+                       Thread.sleep((int) (Math.min(state.time_change,10) * 1000));
+       }
+
+       public AltosReplayThread(Frame parent, Iterator<AltosRecord> in_iterator,
+                                String in_name, AltosVoice voice,
+                                AltosStatusTable status, AltosInfoTable info) {
+               super(parent, voice, status, info);
+               iterator = in_iterator;
+               name = in_name;
+       }
+}
diff --git a/ao-tools/altosui/AltosTelemetryIterable.java b/ao-tools/altosui/AltosTelemetryIterable.java
new file mode 100644 (file)
index 0000000..0a125c9
--- /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.io.*;
+import java.util.*;
+import java.text.*;
+import altosui.AltosTelemetry;
+
+public class AltosTelemetryIterable extends AltosRecordIterable {
+       LinkedList<AltosRecord> records;
+
+       public Iterator<AltosRecord> iterator () {
+               return records.iterator();
+       }
+
+       public AltosTelemetryIterable (FileInputStream input) {
+               boolean saw_boost = false;
+               int     current_tick = 0;
+               int     boost_tick = 0;
+
+               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 (records.isEmpty()) {
+                                               current_tick = record.tick;
+                                       } else {
+                                               int tick = record.tick | (current_tick & ~ 0xffff);
+                                               if (tick < current_tick - 0x1000)
+                                                       tick += 0x10000;
+                                               current_tick = tick;
+                                               record.tick = current_tick;
+                                       }
+                                       if (!saw_boost && record.state >= Altos.ao_flight_boost)
+                                       {
+                                               saw_boost = true;
+                                               boost_tick = record.tick;
+                                       }
+                                       records.add(record);
+                               } catch (ParseException pe) {
+                                       System.out.printf("parse exception %s\n", pe.getMessage());
+                               } catch (AltosCRCException ce) {
+                                       System.out.printf("crc error\n");
+                               }
+                       }
+               } catch (IOException io) {
+                       System.out.printf("io exception\n");
+               }
+
+               /* adjust all tick counts to be relative to boost time */
+               for (AltosRecord r : this)
+                       r.time = (r.tick - boost_tick) / 100.0;
+
+               try {
+                       input.close();
+               } catch (IOException ie) {
+               }
+       }
+}
diff --git a/ao-tools/altosui/AltosTelemetryReader.java b/ao-tools/altosui/AltosTelemetryReader.java
deleted file mode 100644 (file)
index 3564a0a..0000000
+++ /dev/null
@@ -1,88 +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.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;
-               int     current_tick = 0;
-
-               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 (records.isEmpty()) {
-                                               current_tick = record.tick;
-                                       } else {
-                                               int tick = record.tick | (current_tick & ~ 0xffff);
-                                               if (tick < current_tick - 0x1000)
-                                                       tick += 0x10000;
-                                               current_tick = tick;
-                                               record.tick = current_tick;
-                                       }
-                                       if (!saw_boost && record.state >= Altos.ao_flight_boost)
-                                       {
-                                               saw_boost = true;
-                                               boost_tick = record.tick;
-                                       }
-                                       records.add(record);
-                               } catch (ParseException pe) {
-                                       System.out.printf("parse exception %s\n", pe.getMessage());
-                               } catch (AltosCRCException ce) {
-                                       System.out.printf("crc error\n");
-                               }
-                       }
-               } catch (IOException io) {
-                       System.out.printf("io exception\n");
-               }
-               record_iterator = records.iterator();
-               try {
-                       input.close();
-               } catch (IOException ie) {
-               }
-       }
-}
index e130de58d703470f61845d3852ccc9b89389370b..de0673a209e396630fb3b166a2e1586bf2b7f7bb 100644 (file)
@@ -46,6 +46,7 @@ import altosui.AltosCSVUI;
 import altosui.AltosLine;
 import altosui.AltosStatusTable;
 import altosui.AltosInfoTable;
+import altosui.AltosDisplayThread;
 
 import libaltosJNI.*;
 
@@ -119,222 +120,7 @@ public class AltosUI extends JFrame {
                voice.speak("Rocket flight monitor ready.");
        }
 
-       void show(AltosState state, int crc_errors) {
-               if (state != null) {
-                       flightStatus.set(state);
-                       flightInfo.show(state, crc_errors);
-               }
-       }
-
-       class IdleThread extends Thread {
-
-               boolean started;
-               private AltosState state;
-               int     reported_landing;
-               int     report_interval;
-               long    report_time;
-
-               public synchronized void report(boolean last) {
-                       if (state == null)
-                               return;
-
-                       /* reset the landing count once we hear about a new flight */
-                       if (state.state < Altos.ao_flight_drogue)
-                               reported_landing = 0;
-
-                       /* Shut up once the rocket is on the ground */
-                       if (reported_landing > 2) {
-                               return;
-                       }
-
-                       /* If the rocket isn't on the pad, then report height */
-                       if (Altos.ao_flight_drogue <= state.state &&
-                           state.state < Altos.ao_flight_landed &&
-                           state.range >= 0)
-                       {
-                               voice.speak("Height %d, bearing %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;
-                       }
-
-                       /* If the rocket is coming down, check to see if it has landed;
-                        * either we've got a landed report or we haven't heard from it in
-                        * a long time
-                        */
-                       if (state.state >= Altos.ao_flight_drogue &&
-                           (last ||
-                            System.currentTimeMillis() - state.report_time >= 15000 ||
-                            state.state == Altos.ao_flight_landed))
-                       {
-                               if (Math.abs(state.baro_speed) < 20 && state.height < 100)
-                                       voice.speak("rocket landed safely");
-                               else
-                                       voice.speak("rocket may have crashed");
-                               if (state.from_pad != null)
-                                       voice.speak("Bearing %d degrees, range %d meters.",
-                                                   (int) (state.from_pad.bearing + 0.5),
-                                                   (int) (state.from_pad.distance + 0.5));
-                               ++reported_landing;
-                       }
-               }
-
-               long now () {
-                       return System.currentTimeMillis();
-               }
-
-               void set_report_time() {
-                       report_time = now() + report_interval;
-               }
-
-               public void run () {
-
-                       reported_landing = 0;
-                       state = null;
-                       report_interval = 10000;
-                       try {
-                               for (;;) {
-                                       set_report_time();
-                                       for (;;) {
-                                               voice.drain();
-                                               synchronized (this) {
-                                                       long    sleep_time = report_time - now();
-                                                       if (sleep_time <= 0)
-                                                               break;
-                                                       wait(sleep_time);
-                                               }
-                                       }
-                                       report(false);
-                               }
-                       } catch (InterruptedException ie) {
-                               try {
-                                       voice.drain();
-                               } catch (InterruptedException iie) { }
-                       }
-               }
-
-               public synchronized void notice(AltosState new_state, boolean spoken) {
-                       AltosState old_state = state;
-                       state = new_state;
-                       if (!started && state.state > Altos.ao_flight_pad) {
-                               started = true;
-                               start();
-                       }
-
-                       if (state.state < Altos.ao_flight_drogue)
-                               report_interval = 10000;
-                       else
-                               report_interval = 20000;
-                       if (old_state != null && old_state.state != state.state) {
-                               report_time = now();
-                               this.notify();
-                       } else if (spoken)
-                               set_report_time();
-               }
-       }
-
-       private boolean tell(AltosState state, AltosState old_state) {
-               boolean ret = false;
-               if (old_state == null || old_state.state != state.state) {
-                       voice.speak(state.data.state());
-                       if ((old_state == null || old_state.state <= Altos.ao_flight_boost) &&
-                           state.state > Altos.ao_flight_boost) {
-                               voice.speak("max speed: %d meters per second.",
-                                           (int) (state.max_speed + 0.5));
-                               ret = true;
-                       } else if ((old_state == null || old_state.state < Altos.ao_flight_drogue) &&
-                                  state.state >= Altos.ao_flight_drogue) {
-                               voice.speak("max height: %d meters.",
-                                           (int) (state.max_height + 0.5));
-                               ret = true;
-                       }
-               }
-               if (old_state == null || old_state.gps_ready != state.gps_ready) {
-                       if (state.gps_ready) {
-                               voice.speak("GPS ready");
-                               ret = true;
-                       }
-                       else if (old_state != null) {
-                               voice.speak("GPS lost");
-                               ret = true;
-                       }
-               }
-               old_state = state;
-               return ret;
-       }
-
-       class DisplayThread extends Thread {
-               IdleThread      idle_thread;
-
-               String          name;
-
-               int             crc_errors;
-
-               void init() { }
-
-               AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; }
-
-               void close(boolean interrupted) { }
-
-               void update(AltosState state) throws InterruptedException { }
-
-               public void run() {
-                       boolean         interrupted = false;
-                       String          line;
-                       AltosState      state = null;
-                       AltosState      old_state = null;
-                       boolean         told;
-
-                       idle_thread = new IdleThread();
-
-                       flightInfo.clear();
-                       try {
-                               for (;;) {
-                                       try {
-                                               AltosRecord record = read();
-                                               if (record == null)
-                                                       break;
-                                               old_state = state;
-                                               state = new AltosState(record, state);
-                                               update(state);
-                                               show(state, crc_errors);
-                                               told = tell(state, old_state);
-                                               idle_thread.notice(state, told);
-                                       } catch (ParseException pp) {
-                                               System.out.printf("Parse error: %d \"%s\"\n", pp.getErrorOffset(), pp.getMessage());
-                                       } catch (AltosCRCException ce) {
-                                               ++crc_errors;
-                                               show(state, crc_errors);
-                                       }
-                               }
-                       } catch (InterruptedException ee) {
-                               interrupted = true;
-                       } catch (IOException ie) {
-                               JOptionPane.showMessageDialog(AltosUI.this,
-                                                             String.format("Error reading from \"%s\"", name),
-                                                             "Telemetry Read Error",
-                                                             JOptionPane.ERROR_MESSAGE);
-                       } finally {
-                               close(interrupted);
-                               idle_thread.interrupt();
-                               try {
-                                       idle_thread.join();
-                               } catch (InterruptedException ie) {}
-                       }
-               }
-
-               public void report() {
-                       if (idle_thread != null)
-                               idle_thread.report(true);
-               }
-       }
-
-       class DeviceThread extends DisplayThread {
+       class DeviceThread extends AltosDisplayThread {
                AltosSerial     serial;
                LinkedBlockingQueue<AltosLine> telem;
 
@@ -350,7 +136,8 @@ public class AltosUI extends JFrame {
                        serial.remove_monitor(telem);
                }
 
-               public DeviceThread(AltosSerial s, String in_name) {
+               public DeviceThread(AltosSerial s, String in_name, AltosVoice voice, AltosStatusTable status, AltosInfoTable info) {
+                       super(AltosUI.this, voice, status, info);
                        serial = s;
                        telem = new LinkedBlockingQueue<AltosLine>();
                        serial.add_monitor(telem);
@@ -366,7 +153,7 @@ public class AltosUI extends JFrame {
                        try {
                                stop_display();
                                serial_line.open(device);
-                               DeviceThread thread = new DeviceThread(serial_line, device.getPath());
+                               DeviceThread thread = new DeviceThread(serial_line, device.getPath(), voice, flightStatus, flightInfo);
                                serial_line.set_channel(AltosPreferences.channel());
                                serial_line.set_callsign(AltosPreferences.callsign());
                                run_display(thread);
@@ -409,41 +196,6 @@ public class AltosUI extends JFrame {
                new AltosFlashUI(AltosUI.this);
        }
 
-       /*
-        * Open an existing telemetry file and replay it in realtime
-        */
-
-       class ReplayThread extends DisplayThread {
-               AltosReader     reader;
-               String          name;
-
-               public AltosRecord read() {
-                       try {
-                               return reader.read();
-                       } catch (IOException ie) {
-                               JOptionPane.showMessageDialog(AltosUI.this,
-                                                             name,
-                                                             "error reading",
-                                                             JOptionPane.ERROR_MESSAGE);
-                       } catch (ParseException pe) {
-                       }
-                       return null;
-               }
-
-               public void close (boolean interrupted) {
-                       if (!interrupted)
-                               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 > Altos.ao_flight_pad)
-                               Thread.sleep((int) (Math.min(state.time_change,10) * 1000));
-               }
-       }
 
        Thread          display_thread;
 
@@ -469,10 +221,13 @@ public class AltosUI extends JFrame {
        private void Replay() {
                AltosLogfileChooser chooser = new AltosLogfileChooser(
                        AltosUI.this);
-               AltosReader reader = chooser.runDialog();
-               if (reader != null)
-                       run_display(new ReplayThread(reader,
-                                                    chooser.filename()));
+               AltosRecordIterable iterable = chooser.runDialog();
+               if (iterable != null)
+                       run_display(new AltosReplayThread(this, iterable.iterator(),
+                                                         chooser.filename(),
+                                                         voice,
+                                                         flightStatus,
+                                                         flightInfo));
        }
 
        /* Connect to TeleMetrum, either directly or through
@@ -678,16 +433,16 @@ public class AltosUI extends JFrame {
                return input.concat(extension);
        }
 
-       static AltosReader open_logfile(String filename) {
+       static AltosRecordIterable open_logfile(String filename) {
                File file = new File (filename);
                try {
                        FileInputStream in;
 
                        in = new FileInputStream(file);
                        if (filename.endsWith("eeprom"))
-                               return new AltosEepromReader(in);
+                               return new AltosEepromIterable(in);
                        else
-                               return new AltosTelemetryReader(in);
+                               return new AltosTelemetryIterable(in);
                } catch (FileNotFoundException fe) {
                        System.out.printf("Cannot open '%s'\n", filename);
                        return null;
@@ -711,14 +466,13 @@ public class AltosUI extends JFrame {
                        return;
                }
                System.out.printf("Processing \"%s\" to \"%s\"\n", input, output);
-               AltosReader reader = open_logfile(input);
-               if (reader == null)
+               AltosRecordIterable iterable = open_logfile(input);
+               if (iterable == null)
                        return;
                AltosCSV writer = open_csv(output);
                if (writer == null)
                        return;
-               writer.write(reader);
-               reader.close();
+               writer.write(iterable);
                writer.close();
        }
 
index 35a1b99035665f21d3de454270a89cb5f8f7ebd1..ab8ca7d4a80163b4ed385a64ac65c12702a136b8 100644 (file)
@@ -20,9 +20,10 @@ altosui_JAVA = \
        AltosDebug.java \
        AltosDeviceDialog.java \
        AltosDevice.java \
+       AltosDisplayThread.java \
        AltosEepromDownload.java \
        AltosEepromMonitor.java \
-       AltosEepromReader.java \
+       AltosEepromIterable.java \
        AltosEepromRecord.java \
        AltosFile.java \
        AltosFlash.java \
@@ -41,6 +42,8 @@ altosui_JAVA = \
        AltosPreferences.java \
        AltosReader.java \
        AltosRecord.java \
+       AltosRecordIterable.java \
+       AltosReplayThread.java \
        AltosRomconfig.java \
        AltosRomconfigUI.java \
        AltosSerial.java \
@@ -48,7 +51,7 @@ altosui_JAVA = \
        AltosState.java \
        AltosStatusTable.java \
        AltosTelemetry.java \
-       AltosTelemetryReader.java \
+       AltosTelemetryIterable.java \
        AltosUI.java \
     AltosCsvReader.java \
     AltosDataPoint.java \