diff options
| author | Keith Packard <keithp@keithp.com> | 2010-09-27 17:11:48 -0700 | 
|---|---|---|
| committer | Keith Packard <keithp@keithp.com> | 2010-09-27 17:11:48 -0700 | 
| commit | c89a34d1eb25155405b0036baeadc7bbfeade1c2 (patch) | |
| tree | 1329c7c89b3d6a182fb68ec8f00c37a6104f5ab9 /ao-tools | |
| parent | e66919aa46193bd8c7a1e86fb32a3367dae121f5 (diff) | |
altosui: Create iterables for log file scanning. Split out display threads
Convert from log file reading paradigm to using iterators which is
more idiomatic for java. Split more code out of AltosUI.java,
including the display update threads for telemetry monitoring and
logfile replay.x
Signed-off-by: Keith Packard <keithp@keithp.com>
Diffstat (limited to 'ao-tools')
| -rw-r--r-- | ao-tools/altosui/AltosCSV.java | 20 | ||||
| -rw-r--r-- | ao-tools/altosui/AltosCSVUI.java | 15 | ||||
| -rw-r--r-- | ao-tools/altosui/AltosDisplayThread.java | 255 | ||||
| -rw-r--r-- | ao-tools/altosui/AltosEepromIterable.java (renamed from ao-tools/altosui/AltosEepromReader.java) | 329 | ||||
| -rw-r--r-- | ao-tools/altosui/AltosLogfileChooser.java | 11 | ||||
| -rw-r--r-- | ao-tools/altosui/AltosRecord.java | 2 | ||||
| -rw-r--r-- | ao-tools/altosui/AltosRecordIterable.java | 42 | ||||
| -rw-r--r-- | ao-tools/altosui/AltosReplayThread.java | 83 | ||||
| -rw-r--r-- | ao-tools/altosui/AltosTelemetryIterable.java (renamed from ao-tools/altosui/AltosTelemetryReader.java) | 25 | ||||
| -rw-r--r-- | ao-tools/altosui/AltosUI.java | 282 | ||||
| -rw-r--r-- | ao-tools/altosui/Makefile.am | 7 | 
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  | 
