summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ao-tools/altosui/AltosCSV.java20
-rw-r--r--ao-tools/altosui/AltosCSVUI.java15
-rw-r--r--ao-tools/altosui/AltosDisplayThread.java255
-rw-r--r--ao-tools/altosui/AltosEepromIterable.java (renamed from ao-tools/altosui/AltosEepromReader.java)329
-rw-r--r--ao-tools/altosui/AltosLogfileChooser.java11
-rw-r--r--ao-tools/altosui/AltosRecord.java2
-rw-r--r--ao-tools/altosui/AltosRecordIterable.java42
-rw-r--r--ao-tools/altosui/AltosReplayThread.java83
-rw-r--r--ao-tools/altosui/AltosTelemetryIterable.java (renamed from ao-tools/altosui/AltosTelemetryReader.java)25
-rw-r--r--ao-tools/altosui/AltosUI.java282
-rw-r--r--ao-tools/altosui/Makefile.am7
11 files changed, 599 insertions, 472 deletions
diff --git a/ao-tools/altosui/AltosCSV.java b/ao-tools/altosui/AltosCSV.java
index f7b3c03c..7f14adad 100644
--- a/ao-tools/altosui/AltosCSV.java
+++ b/ao-tools/altosui/AltosCSV.java
@@ -207,22 +207,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 {
diff --git a/ao-tools/altosui/AltosCSVUI.java b/ao-tools/altosui/AltosCSVUI.java
index 643d4112..4eb72de8 100644
--- a/ao-tools/altosui/AltosCSVUI.java
+++ b/ao-tools/altosui/AltosCSVUI.java
@@ -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
index 00000000..9cc3d5ce
--- /dev/null
+++ b/ao-tools/altosui/AltosDisplayThread.java
@@ -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/AltosEepromReader.java b/ao-tools/altosui/AltosEepromIterable.java
index 03e73812..d4ac3f3e 100644
--- a/ao-tools/altosui/AltosEepromReader.java
+++ b/ao-tools/altosui/AltosEepromIterable.java
@@ -69,7 +69,7 @@ class AltosOrderedRecord extends AltosEepromRecord implements Comparable<AltosOr
}
}
-public class AltosEepromReader extends AltosReader {
+public class AltosEepromIterable extends AltosRecordIterable {
static final int seen_flight = 1;
static final int seen_sensor = 2;
@@ -81,158 +81,178 @@ public class AltosEepromReader extends AltosReader {
static final int seen_basic = seen_flight|seen_sensor|seen_temp_volt|seen_deploy;
- AltosRecord state;
- AltosOrderedRecord record;
+ AltosEepromRecord flight_record;
+ AltosEepromRecord gps_date_record;
TreeSet<AltosOrderedRecord> records;
- Iterator<AltosOrderedRecord> record_iterator;
+ LinkedList<AltosRecord> list;
- int seen;
+ class EepromState {
+ int seen;
+ int n_pad_samples;
+ double ground_pres;
+ int gps_tick;
+ int boost_tick;
- int index;
-
- boolean last_reported;
-
- double ground_pres;
-
- int n_pad_samples;
-
- int gps_tick;
-
- int boost_tick;
-
- boolean saw_gps_date;
+ EepromState() {
+ seen = 0;
+ n_pad_samples = 0;
+ ground_pres = 0.0;
+ gps_tick = 0;
+ }
+ }
- boolean missing_gps_time;
+ 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;
+ }
+ }
- 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();
+ 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();
- 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.state = Altos.ao_flight_pad;
+ state.accel_plus_g = 15758;
+ state.accel_minus_g = 16294;
- 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;
+ /* 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);
- 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;
+ 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);
}
- record = null;
+ 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) {
@@ -313,18 +333,13 @@ public class AltosEepromReader extends AltosReader {
* 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;
+ public AltosEepromIterable (FileInputStream input) {
records = new TreeSet<AltosOrderedRecord>();
AltosOrderedRecord last_gps_time = null;
int index = 0;
- int tick = 0;
+ int prev_tick = 0;
boolean missing_time = false;
@@ -333,18 +348,15 @@ public class AltosEepromReader extends AltosReader {
String line = AltosRecord.gets(input);
if (line == null)
break;
- AltosOrderedRecord record = new AltosOrderedRecord(line, index++, tick);
+ AltosOrderedRecord record = new AltosOrderedRecord(line, index++, prev_tick);
if (record == null)
break;
if (record.cmd == Altos.AO_LOG_INVALID)
continue;
- tick = record.tick;
+ prev_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;
+ flight_record = record;
+ continue;
}
/* Two firmware bugs caused the loss of some GPS data.
@@ -353,8 +365,10 @@ public class AltosEepromReader extends AltosReader {
* 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;
+ 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) {
@@ -397,7 +411,6 @@ public class AltosEepromReader extends AltosReader {
} catch (IOException io) {
} catch (ParseException pe) {
}
- record_iterator = records.iterator();
try {
input.close();
} catch (IOException ie) {
diff --git a/ao-tools/altosui/AltosLogfileChooser.java b/ao-tools/altosui/AltosLogfileChooser.java
index 36b51de6..8b9d77d6 100644
--- a/ao-tools/altosui/AltosLogfileChooser.java
+++ b/ao-tools/altosui/AltosLogfileChooser.java
@@ -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,
diff --git a/ao-tools/altosui/AltosRecord.java b/ao-tools/altosui/AltosRecord.java
index 18c6079d..00484767 100644
--- a/ao-tools/altosui/AltosRecord.java
+++ b/ao-tools/altosui/AltosRecord.java
@@ -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
index 00000000..147ecc14
--- /dev/null
+++ b/ao-tools/altosui/AltosRecordIterable.java
@@ -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
index 00000000..b418160a
--- /dev/null
+++ b/ao-tools/altosui/AltosReplayThread.java
@@ -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/AltosTelemetryReader.java b/ao-tools/altosui/AltosTelemetryIterable.java
index 3564a0a5..0a125c98 100644
--- a/ao-tools/altosui/AltosTelemetryReader.java
+++ b/ao-tools/altosui/AltosTelemetryIterable.java
@@ -22,26 +22,17 @@ import java.util.*;
import java.text.*;
import altosui.AltosTelemetry;
-public class AltosTelemetryReader extends AltosReader {
+public class AltosTelemetryIterable extends AltosRecordIterable {
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 Iterator<AltosRecord> iterator () {
+ return records.iterator();
}
- public AltosTelemetryReader (FileInputStream input) {
+ public AltosTelemetryIterable (FileInputStream input) {
boolean saw_boost = false;
int current_tick = 0;
+ int boost_tick = 0;
records = new LinkedList<AltosRecord> ();
@@ -79,7 +70,11 @@ public class AltosTelemetryReader extends AltosReader {
} catch (IOException io) {
System.out.printf("io exception\n");
}
- record_iterator = records.iterator();
+
+ /* 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/AltosUI.java b/ao-tools/altosui/AltosUI.java
index 1adeeccf..29eda2ec 100644
--- a/ao-tools/altosui/AltosUI.java
+++ b/ao-tools/altosui/AltosUI.java
@@ -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
@@ -663,16 +418,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;
@@ -696,14 +451,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();
}
diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am
index 56ac0520..7070af22 100644
--- a/ao-tools/altosui/Makefile.am
+++ b/ao-tools/altosui/Makefile.am
@@ -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 \
AltosVoice.java