diff options
| -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 | 
