diff options
| author | Keith Packard <keithp@keithp.com> | 2013-08-27 21:28:07 -0600 | 
|---|---|---|
| committer | Keith Packard <keithp@keithp.com> | 2013-08-29 06:47:37 -0600 | 
| commit | 04d7d0f829ba953ffeca8ad9887a4b6b2b5d5087 (patch) | |
| tree | 5a0c085740c465adb7f52442f34eaf4ee08ce7dc | |
| parent | dcc51bb18985c24fa35bce0dd42ea3d847b960bf (diff) | |
altoslib: Start restructuring AltosState harder
Make per-packet code update state itself rather than having all state
updates done centrally. Will make adding new packet types easier.
Signed-off-by: Keith Packard <keithp@keithp.com>
29 files changed, 3034 insertions, 612 deletions
| diff --git a/altoslib/AltosCompanion.java b/altoslib/AltosCompanion.java new file mode 100644 index 00000000..1572fdae --- /dev/null +++ b/altoslib/AltosCompanion.java @@ -0,0 +1,38 @@ +/* + * Copyright © 2011 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 org.altusmetrum.altoslib_1; + +public class AltosCompanion { +	public final static int	board_id_telescience = 0x0a; +	public final static int	MAX_CHANNELS = 12; + +	public int	tick; +	public int	board_id; +	public int	update_period; +	public int	channels; +	public int[]	companion_data; + +	public AltosCompanion(int in_channels) { +		channels = in_channels; +		if (channels < 0) +			channels = 0; +		if (channels > MAX_CHANNELS) +			channels = MAX_CHANNELS; +		companion_data = new int[channels]; +	} +} diff --git a/altoslib/AltosEeprom.java b/altoslib/AltosEeprom.java new file mode 100644 index 00000000..31646c7e --- /dev/null +++ b/altoslib/AltosEeprom.java @@ -0,0 +1,89 @@ +/* + * Copyright © 2013 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 org.altusmetrum.altoslib_1; + +import java.io.*; +import java.util.*; +import java.text.*; + +public abstract class AltosEeprom implements AltosStateUpdate { +	public int	cmd; +	public int	tick; +	public int	data8[]; +	public boolean	valid; + +	public final static int header_length = 4; + +	public abstract void update_state(AltosState state); + +	public void write(PrintStream out) { +		out.printf("%c %04x", cmd, tick); +		if (data8 != null) { +			for (int i = 0; i < data8.length; i++) +				out.printf (" %02x", data8[i]); +		} +		out.printf ("\n"); +	} + +	void parse_chunk(AltosEepromChunk chunk, int start, int record_length) throws ParseException { +		cmd = chunk.data(start); + +		int data_length = record_length - header_length; + +		valid = !chunk.erased(start, record_length); +		if (valid) { +			if (AltosConvert.checksum(chunk.data, start, record_length) != 0) +				throw new ParseException(String.format("invalid checksum at 0x%x", +								       chunk.address + start), 0); +		} else { +			cmd = AltosLib.AO_LOG_INVALID; +		} + +		tick = chunk.data16(start+2); + +		data8 = new int[data_length]; +		for (int i = 0; i < data_length; i++) +			data8[i] = chunk.data(start + header_length + i); +	} + +	void parse_string(String line, int record_length) { +		valid = false; +		tick = 0; +		cmd = AltosLib.AO_LOG_INVALID; + +		int data_length = record_length - header_length; + +		if (line == null) +			return; +		try { +			String[] tokens = line.split("\\s+"); + +			if (tokens[0].length() == 1) { +				if (tokens.length == 2 + data_length) { +					cmd = tokens[0].codePointAt(0); +					tick = Integer.parseInt(tokens[1],16); +					valid = true; +					data8 = new int[data_length]; +					for (int i = 0; i < data_length; i++) +						data8[i] = Integer.parseInt(tokens[2 + i],16); +				} +			} +		} catch (NumberFormatException ne) { +		} +	} +} diff --git a/altoslib/AltosEepromBody.java b/altoslib/AltosEepromBody.java new file mode 100644 index 00000000..60aa8881 --- /dev/null +++ b/altoslib/AltosEepromBody.java @@ -0,0 +1,31 @@ +/* + * Copyright © 2013 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 org.altusmetrum.altoslib_1; + +import java.io.*; +import java.util.*; +import java.text.*; + +public class AltosEepromBody implements AltosEeprom, AltosStateUpdate { + +	public void update_state(AltosState state) { +	} + +	public void write(PrintStream out) { +	} +}
\ No newline at end of file diff --git a/altoslib/AltosEepromBodyIterable.java b/altoslib/AltosEepromBodyIterable.java new file mode 100644 index 00000000..33dc0ac8 --- /dev/null +++ b/altoslib/AltosEepromBodyIterable.java @@ -0,0 +1,28 @@ +/* + * Copyright © 2013 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 org.altusmetrum.altoslib_1; + +import java.io.*; +import java.util.*; +import java.text.*; + +public class AltosEepromBodyIterable { +	LinkedList<AltosEepromBody>	bodies; + +	 +}
\ No newline at end of file diff --git a/altoslib/AltosEepromFile.java b/altoslib/AltosEepromFile.java new file mode 100644 index 00000000..48d2543c --- /dev/null +++ b/altoslib/AltosEepromFile.java @@ -0,0 +1,51 @@ +/* + * Copyright © 2013 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 org.altusmetrum.altoslib_1; + +import java.io.*; +import java.util.*; +import java.text.*; + +public class AltosEepromFile { + +	AltosEepromIterable	headers; +	AltosEepromIterable	body; + +	public void write(PrintStream out) { +		headers.write(out); +		body.write(out); +	} + +	public AltosEepromFile(FileInputStream input) { +		headers = new AltosEepromIterable(AltosEepromHeader.read(input)); + +		AltosState	state = headers.state(); + +		switch (state.log_format) { +		case AltosLib.AO_LOG_FORMAT_FULL: +		case AltosLib.AO_LOG_FORMAT_TINY: +		case AltosLib.AO_LOG_FORMAT_TELEMETRY: +		case AltosLib.AO_LOG_FORMAT_TELESCIENCE: +		case AltosLib.AO_LOG_FORMAT_TELEMEGA: +			break; +		case AltosLib.AO_LOG_FORMAT_MINI: +			body = new AltosEepromIterable(AltosEepromMini.read(input)); +			break; +		} +	} +}
\ No newline at end of file diff --git a/altoslib/AltosEepromHeader.java b/altoslib/AltosEepromHeader.java new file mode 100644 index 00000000..b2343dc6 --- /dev/null +++ b/altoslib/AltosEepromHeader.java @@ -0,0 +1,267 @@ +/* + * Copyright © 2013 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 org.altusmetrum.altoslib_1; + +import java.io.*; +import java.util.*; +import java.text.*; + +public class AltosEepromHeader extends AltosEeprom { + +	public int	cmd; +	public String	data; +	public int	config_a, config_b; +	public boolean	last; +	public boolean	valid; + +	public void update_state(AltosState state) { +		switch (cmd) { +		case AltosLib.AO_LOG_CONFIG_VERSION: +			break; +		case AltosLib.AO_LOG_MAIN_DEPLOY: +			break; +		case AltosLib.AO_LOG_APOGEE_DELAY: +			break; +		case AltosLib.AO_LOG_RADIO_CHANNEL: +			break; +		case AltosLib.AO_LOG_CALLSIGN: +			state.callsign = data; +			break; +		case AltosLib.AO_LOG_ACCEL_CAL: +			state.accel_plus_g = config_a; +			state.accel_minus_g = config_b; +			break; +		case AltosLib.AO_LOG_RADIO_CAL: +			break; +		case AltosLib.AO_LOG_MANUFACTURER: +			break; +		case AltosLib.AO_LOG_PRODUCT: +			break; +		case AltosLib.AO_LOG_LOG_FORMAT: +			state.log_format = config_a; +			break; +		case AltosLib.AO_LOG_SERIAL_NUMBER: +			state.serial = config_a; +			break; +		case AltosLib.AO_LOG_BARO_RESERVED: +			state.baro.reserved = config_a; +			break; +		case AltosLib.AO_LOG_BARO_SENS: +			state.baro.sens = config_a; +			break; +		case AltosLib.AO_LOG_BARO_OFF: +			state.baro.off = config_a; +			break; +		case AltosLib.AO_LOG_BARO_TCS: +			state.baro.tcs = config_a; +			break; +		case AltosLib.AO_LOG_BARO_TCO: +			state.baro.tco = config_a; +			break; +		case AltosLib.AO_LOG_BARO_TREF: +			state.baro.tref = config_a; +			break; +		case AltosLib.AO_LOG_BARO_TEMPSENS: +			state.baro.tempsens = config_a; +			break; +		case AltosLib.AO_LOG_BARO_CRC: +			state.baro.crc = config_a; +			break; +		case AltosLib.AO_LOG_SOFTWARE_VERSION: +			break; +		} +	} + +	public void write(PrintStream out) { +		switch (cmd) { +		case AltosLib.AO_LOG_CONFIG_VERSION: +			out.printf("# Config version: %s\n", data); +			break; +		case AltosLib.AO_LOG_MAIN_DEPLOY: +			out.printf("# Main deploy: %s\n", config_a); +			break; +		case AltosLib.AO_LOG_APOGEE_DELAY: +			out.printf("# Apogee delay: %s\n", config_a); +			break; +		case AltosLib.AO_LOG_RADIO_CHANNEL: +			out.printf("# Radio channel: %s\n", config_a); +			break; +		case AltosLib.AO_LOG_CALLSIGN: +			out.printf("# Callsign: %s\n", data); +			break; +		case AltosLib.AO_LOG_ACCEL_CAL: +			out.printf ("# Accel cal: %d %d\n", config_a, config_b); +			break; +		case AltosLib.AO_LOG_RADIO_CAL: +			out.printf ("# Radio cal: %d\n", config_a); +			break; +		case AltosLib.AO_LOG_MAX_FLIGHT_LOG: +			out.printf ("# Max flight log: %d\n", config_a); +			break; +		case AltosLib.AO_LOG_MANUFACTURER: +			out.printf ("# Manufacturer: %s\n", data); +			break; +		case AltosLib.AO_LOG_PRODUCT: +			out.printf ("# Product: %s\n", data); +			break; +		case AltosLib.AO_LOG_SERIAL_NUMBER: +			out.printf ("# Serial number: %d\n", config_a); +			break; +		case AltosLib.AO_LOG_SOFTWARE_VERSION: +			out.printf ("# Software version: %s\n", data); +			break; +		case AltosLib.AO_LOG_BARO_RESERVED: +			out.printf ("# Baro reserved: %d\n", config_a); +			break; +		case AltosLib.AO_LOG_BARO_SENS: +			out.printf ("# Baro sens: %d\n", config_a); +			break; +		case AltosLib.AO_LOG_BARO_OFF: +			out.printf ("# Baro off: %d\n", config_a); +			break; +		case AltosLib.AO_LOG_BARO_TCS: +			out.printf ("# Baro tcs: %d\n", config_a); +			break; +		case AltosLib.AO_LOG_BARO_TCO: +			out.printf ("# Baro tco: %d\n", config_a); +			break; +		case AltosLib.AO_LOG_BARO_TREF: +			out.printf ("# Baro tref: %d\n", config_a); +			break; +		case AltosLib.AO_LOG_BARO_TEMPSENS: +			out.printf ("# Baro tempsens: %d\n", config_a); +			break; +		case AltosLib.AO_LOG_BARO_CRC: +			out.printf ("# Baro crc: %d\n", config_a); +			break; +		} +	} +	 +	public AltosEepromHeader (String[] tokens) { +		last = false; +		valid = true; +		try { +			if (tokens[0].equals("Config") && tokens[1].equals("version:")) { +				cmd = AltosLib.AO_LOG_CONFIG_VERSION; +				data = tokens[2]; +			} else if (tokens[0].equals("Main") && tokens[1].equals("deploy:")) { +				cmd = AltosLib.AO_LOG_MAIN_DEPLOY; +				config_a = Integer.parseInt(tokens[2]); +			} else if (tokens[0].equals("Apogee") && tokens[1].equals("delay:")) { +				cmd = AltosLib.AO_LOG_APOGEE_DELAY; +				config_a = Integer.parseInt(tokens[2]); +			} else if (tokens[0].equals("Radio") && tokens[1].equals("channel:")) { +				cmd = AltosLib.AO_LOG_RADIO_CHANNEL; +				config_a = Integer.parseInt(tokens[2]); +			} else if (tokens[0].equals("Callsign:")) { +				cmd = AltosLib.AO_LOG_CALLSIGN; +				data = tokens[1].replaceAll("\"",""); +			} else if (tokens[0].equals("Accel") && tokens[1].equals("cal")) { +				cmd = AltosLib.AO_LOG_ACCEL_CAL; +				config_a = Integer.parseInt(tokens[3]); +				config_b = Integer.parseInt(tokens[5]); +			} else if (tokens[0].equals("Radio") && tokens[1].equals("cal:")) { +				cmd = AltosLib.AO_LOG_RADIO_CAL; +				config_a = Integer.parseInt(tokens[2]); +			} else if (tokens[0].equals("Max") && tokens[1].equals("flight") && tokens[2].equals("log:")) { +				cmd = AltosLib.AO_LOG_MAX_FLIGHT_LOG; +				config_a = Integer.parseInt(tokens[3]); +			} else if (tokens[0].equals("manufacturer")) { +				cmd = AltosLib.AO_LOG_MANUFACTURER; +				data = tokens[1]; +			} else if (tokens[0].equals("product")) { +				cmd = AltosLib.AO_LOG_PRODUCT; +				data = tokens[1]; +			} else if (tokens[0].equals("serial-number")) { +				cmd = AltosLib.AO_LOG_SERIAL_NUMBER; +				config_a = Integer.parseInt(tokens[1]); +			} else if (tokens[0].equals("log-format")) { +				cmd = AltosLib.AO_LOG_LOG_FORMAT; +				config_a = Integer.parseInt(tokens[1]); +			} else if (tokens[0].equals("software-version")) { +				cmd = AltosLib.AO_LOG_SOFTWARE_VERSION; +				data = tokens[1]; +				last = true; +			} else if (tokens[0].equals("ms5607")) { +				if (tokens[1].equals("reserved:")) { +					cmd = AltosLib.AO_LOG_BARO_RESERVED; +					config_a = Integer.parseInt(tokens[2]); +				} else if (tokens[1].equals("sens:")) { +					cmd = AltosLib.AO_LOG_BARO_SENS; +					config_a = Integer.parseInt(tokens[2]); +				} else if (tokens[1].equals("off:")) { +					cmd = AltosLib.AO_LOG_BARO_OFF; +					config_a = Integer.parseInt(tokens[2]); +				} else if (tokens[1].equals("tcs:")) { +					cmd = AltosLib.AO_LOG_BARO_TCS; +					config_a = Integer.parseInt(tokens[2]); +				} else if (tokens[1].equals("tco:")) { +					cmd = AltosLib.AO_LOG_BARO_TCO; +					config_a = Integer.parseInt(tokens[2]); +				} else if (tokens[1].equals("tref:")) { +					cmd = AltosLib.AO_LOG_BARO_TREF; +					config_a = Integer.parseInt(tokens[2]); +				} else if (tokens[1].equals("tempsens:")) { +					cmd = AltosLib.AO_LOG_BARO_TEMPSENS; +					config_a = Integer.parseInt(tokens[2]); +				} else if (tokens[1].equals("crc:")) { +					cmd = AltosLib.AO_LOG_BARO_CRC; +					config_a = Integer.parseInt(tokens[2]); +				} else { +					cmd = AltosLib.AO_LOG_INVALID; +					data = tokens[2]; +				} +			} else +				valid = false; +		} catch (Exception e) { +			valid = false; +		} +	} + +	static public LinkedList<AltosEeprom> read(FileInputStream input) { +		LinkedList<AltosEeprom> headers = new LinkedList<AltosEeprom>(); + +		for (;;) { +			try { +				String line = AltosLib.gets(input); +				if (line == null) +					break; +				AltosEepromHeader header = new AltosEepromHeader(line); +				headers.add(header); +				if (header.last) +					break; +			} catch (IOException ie) { +				break; +			} +		} + +		return headers; +	} + +	static public void write (PrintStream out, LinkedList<AltosEepromHeader> headers) { +		out.printf("# Comments\n"); +		for (AltosEepromHeader header : headers) { +			header.write(out); +		} +		 +	} + +	public AltosEepromHeader (String line) { +		this(line.split("\\s+")); +	} +} diff --git a/altoslib/AltosEepromHeaderIterable.java b/altoslib/AltosEepromHeaderIterable.java new file mode 100644 index 00000000..fe9e05d9 --- /dev/null +++ b/altoslib/AltosEepromHeaderIterable.java @@ -0,0 +1,48 @@ +/* + * Copyright © 2013 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 org.altusmetrum.altoslib_1; + +import java.io.*; +import java.util.*; +import java.text.*; + +public class AltosEepromHeaderIterable implements Iterable<AltosEepromHeader> { +	public LinkedList<AltosEepromHeader> headers; + +	public void write(PrintStream out) { +		AltosEepromHeader.write(out, headers); +	} + +	public AltosState state() { +		AltosState	state = new AltosState(null); + +		for (AltosEepromHeader header : headers) +			header.update_state(state); +		return state; +	} + +	public AltosEepromHeaderIterable(FileInputStream input) { +		headers = AltosEepromHeader.read(input); +	} + +	public Iterator<AltosEepromHeader> iterator() { +		if (headers == null) +			headers = new LinkedList<AltosEepromHeader>(); +		return headers.iterator(); +	} +}
\ No newline at end of file diff --git a/altoslib/AltosEepromIterable.java b/altoslib/AltosEepromIterable.java index b84574ef..470a7a8a 100644 --- a/altoslib/AltosEepromIterable.java +++ b/altoslib/AltosEepromIterable.java @@ -1,5 +1,5 @@  /* - * Copyright © 2010 Keith Packard <keithp@keithp.com> + * Copyright © 2013 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 @@ -21,415 +21,29 @@ import java.io.*;  import java.util.*;  import java.text.*; -public class AltosEepromIterable extends AltosRecordIterable { +public class AltosEepromIterable implements Iterable<AltosEeprom> { +	public LinkedList<AltosEeprom> eeproms; -	static final int	seen_basic = AltosRecord.seen_flight|AltosRecord.seen_sensor; - -	boolean			has_accel; -	boolean			has_gps; -	boolean			has_ignite; - -	AltosEepromRecord	flight_record; -	AltosEepromRecord	gps_date_record; - -	TreeSet<AltosOrderedRecord>	records; - -	LinkedList<AltosRecord>	list; - -	class EepromState { -		int	seen; -		int	n_pad_samples; -		double	ground_pres; -		int	gps_tick; -		int	boost_tick; -		int	sensor_tick; - -		EepromState() { -			seen = 0; -			n_pad_samples = 0; -			ground_pres = 0.0; -			gps_tick = 0; -		} -	} - -	void update_state(AltosRecordTM state, AltosEepromRecord record, EepromState eeprom) { -		state.tick = record.tick; -		switch (record.cmd) { -		case AltosLib.AO_LOG_FLIGHT: -			eeprom.seen |= AltosRecord.seen_flight; -			state.ground_accel = record.a; -			state.flight_accel = record.a; -			state.flight = record.b; -			eeprom.boost_tick = record.tick; -			break; -		case AltosLib.AO_LOG_SENSOR: -			state.accel = record.a; -			state.pres = record.b; -			if (state.state < AltosLib.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; -			if ((eeprom.seen & AltosRecord.seen_sensor) == 0) -				eeprom.sensor_tick = record.tick - 1; -			state.flight_vel += (state.accel_plus_g - state.accel) * (record.tick - eeprom.sensor_tick); -			eeprom.seen |= AltosRecord.seen_sensor; -			eeprom.sensor_tick = record.tick; -			has_accel = true; -			break; -		case AltosLib.AO_LOG_PRESSURE: -			state.pres = record.b; -			state.flight_pres = state.pres; -			if (eeprom.n_pad_samples == 0) { -				eeprom.n_pad_samples++; -				state.ground_pres = state.pres; -			} -			eeprom.seen |= AltosRecord.seen_sensor; -			break; -		case AltosLib.AO_LOG_TEMP_VOLT: -			state.temp = record.a; -			state.batt = record.b; -			eeprom.seen |= AltosRecord.seen_temp_volt; -			break; -		case AltosLib.AO_LOG_DEPLOY: -			state.drogue = record.a; -			state.main = record.b; -			eeprom.seen |= AltosRecord.seen_deploy; -			has_ignite = true; -			break; -		case AltosLib.AO_LOG_STATE: -			state.state = record.a; -			break; -		case AltosLib.AO_LOG_GPS_TIME: -			eeprom.gps_tick = state.tick; -			eeprom.seen |= AltosRecord.seen_gps_time; -			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 & AltosLib.AO_GPS_RUNNING) != 0; -			state.gps.locked = (flags & AltosLib.AO_GPS_VALID) != 0; -			state.gps.nsat = (flags & AltosLib.AO_GPS_NUM_SAT_MASK) >> -				AltosLib.AO_GPS_NUM_SAT_SHIFT; -			state.gps_sequence++; -			has_gps = true; -			break; -		case AltosLib.AO_LOG_GPS_LAT: -			eeprom.seen |= AltosRecord.seen_gps_lat; -			int lat32 = record.a | (record.b << 16); -			if (state.gps == null) -				state.gps = new AltosGPS(); -			state.gps.lat = (double) lat32 / 1e7; -			break; -		case AltosLib.AO_LOG_GPS_LON: -			eeprom.seen |= AltosRecord.seen_gps_lon; -			int lon32 = record.a | (record.b << 16); -			if (state.gps == null) -				state.gps = new AltosGPS(); -			state.gps.lon = (double) lon32 / 1e7; -			break; -		case AltosLib.AO_LOG_GPS_ALT: -			if (state.gps == null) -				state.gps = new AltosGPS(); -			state.gps.alt = record.a; -			break; -		case AltosLib.AO_LOG_GPS_SAT: -			if (state.tick == eeprom.gps_tick) { -				int svid = record.a; -				int c_n0 = record.b >> 8; -				if (state.gps == null) -					state.gps = new AltosGPS(); -				state.gps.add_sat(svid, c_n0); -			} -			break; -		case AltosLib.AO_LOG_GPS_DATE: -			if (state.gps == null) -				state.gps = new AltosGPS(); -			state.gps.year = (record.a & 0xff) + 2000; -			state.gps.month = record.a >> 8; -			state.gps.day = record.b & 0xff; -			break; - -		case AltosLib.AO_LOG_CONFIG_VERSION: -			break; -		case AltosLib.AO_LOG_MAIN_DEPLOY: -			break; -		case AltosLib.AO_LOG_APOGEE_DELAY: -			break; -		case AltosLib.AO_LOG_RADIO_CHANNEL: -			break; -		case AltosLib.AO_LOG_CALLSIGN: -			state.callsign = record.data; -			break; -		case AltosLib.AO_LOG_ACCEL_CAL: -			state.accel_plus_g = record.a; -			state.accel_minus_g = record.b; -			break; -		case AltosLib.AO_LOG_RADIO_CAL: -			break; -		case AltosLib.AO_LOG_MANUFACTURER: -			break; -		case AltosLib.AO_LOG_PRODUCT: -			break; -		case AltosLib.AO_LOG_SERIAL_NUMBER: -			state.serial = record.a; -			break; -		case AltosLib.AO_LOG_SOFTWARE_VERSION: -			break; -		} -		state.seen |= eeprom.seen; +	public void write(PrintStream out) { +		for (AltosEeprom eeprom : eeproms) +			eeprom.write(out);  	} -	LinkedList<AltosRecord> make_list() { -		LinkedList<AltosRecord>		list = new LinkedList<AltosRecord>(); -		Iterator<AltosOrderedRecord>	iterator = records.iterator(); -		AltosOrderedRecord		record = null; -		AltosRecordTM			state = new AltosRecordTM(); -		//boolean				last_reported = false; -		EepromState			eeprom = new EepromState(); - -		state.state = AltosLib.ao_flight_pad; -		state.accel_plus_g = 15758; -		state.accel_minus_g = 16294; -		state.flight_vel = 0; +	public AltosState state() { +		AltosState	state = new AltosState(null); -		/* Pull in static data from the flight and gps_date records */ -		if (flight_record != null) -			update_state(state, flight_record, eeprom); -		if (gps_date_record != null) -			update_state(state, gps_date_record, eeprom); - -		while (iterator.hasNext()) { -			record = iterator.next(); -			if ((eeprom.seen & seen_basic) == seen_basic && record.tick != state.tick) { -				AltosRecordTM r = state.clone(); -				r.time = (r.tick - eeprom.boost_tick) / 100.0; -				list.add(r); -			} -			update_state(state, record, eeprom); -		} -		AltosRecordTM r = state.clone(); -		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(); +		for (AltosEeprom header : eeproms) +			header.update_state(state); +		return state;  	} -	public boolean has_gps() { return has_gps; } -	public boolean has_accel() { return has_accel; } -	public boolean has_ignite() { return has_ignite; } - -	public void write_comments(PrintStream out) { -		Iterator<AltosOrderedRecord>	iterator = records.iterator(); -		out.printf("# Comments\n"); -		while (iterator.hasNext()) { -			AltosOrderedRecord	record = iterator.next(); -			switch (record.cmd) { -			case AltosLib.AO_LOG_CONFIG_VERSION: -				out.printf("# Config version: %s\n", record.data); -				break; -			case AltosLib.AO_LOG_MAIN_DEPLOY: -				out.printf("# Main deploy: %s\n", record.a); -				break; -			case AltosLib.AO_LOG_APOGEE_DELAY: -				out.printf("# Apogee delay: %s\n", record.a); -				break; -			case AltosLib.AO_LOG_RADIO_CHANNEL: -				out.printf("# Radio channel: %s\n", record.a); -				break; -			case AltosLib.AO_LOG_CALLSIGN: -				out.printf("# Callsign: %s\n", record.data); -				break; -			case AltosLib.AO_LOG_ACCEL_CAL: -				out.printf ("# Accel cal: %d %d\n", record.a, record.b); -				break; -			case AltosLib.AO_LOG_RADIO_CAL: -				out.printf ("# Radio cal: %d\n", record.a); -				break; -			case AltosLib.AO_LOG_MAX_FLIGHT_LOG: -				out.printf ("# Max flight log: %d\n", record.a); -				break; -			case AltosLib.AO_LOG_MANUFACTURER: -				out.printf ("# Manufacturer: %s\n", record.data); -				break; -			case AltosLib.AO_LOG_PRODUCT: -				out.printf ("# Product: %s\n", record.data); -				break; -			case AltosLib.AO_LOG_SERIAL_NUMBER: -				out.printf ("# Serial number: %d\n", record.a); -				break; -			case AltosLib.AO_LOG_SOFTWARE_VERSION: -				out.printf ("# Software version: %s\n", record.data); -				break; -			case AltosLib.AO_LOG_BARO_RESERVED: -				out.printf ("# Baro reserved: %d\n", record.a); -				break; -			case AltosLib.AO_LOG_BARO_SENS: -				out.printf ("# Baro sens: %d\n", record.a); -				break; -			case AltosLib.AO_LOG_BARO_OFF: -				out.printf ("# Baro off: %d\n", record.a); -				break; -			case AltosLib.AO_LOG_BARO_TCS: -				out.printf ("# Baro tcs: %d\n", record.a); -				break; -			case AltosLib.AO_LOG_BARO_TCO: -				out.printf ("# Baro tco: %d\n", record.a); -				break; -			case AltosLib.AO_LOG_BARO_TREF: -				out.printf ("# Baro tref: %d\n", record.a); -				break; -			case AltosLib.AO_LOG_BARO_TEMPSENS: -				out.printf ("# Baro tempsens: %d\n", record.a); -				break; -			case AltosLib.AO_LOG_BARO_CRC: -				out.printf ("# Baro crc: %d\n", record.a); -				break; -			} -		} +	public AltosEepromIterable(LinkedList<AltosEeprom> eeproms) { +		this.eeproms = eeproms;  	} -	/* -	 * Given an AO_LOG_GPS_TIME record with correct time, and one -	 * missing time, rewrite the missing time values with the good -	 * ones, assuming that the difference between them is 'diff' seconds -	 */ -	void update_time(AltosOrderedRecord good, AltosOrderedRecord bad) { - -		int diff = (bad.tick - good.tick + 50) / 100; - -		int hour = (good.a & 0xff); -		int minute = (good.a >> 8); -		int second = (good.b & 0xff); -		int flags = (good.b >> 8); -		int seconds = hour * 3600 + minute * 60 + second; - -		/* Make sure this looks like a good GPS value */ -		if ((flags & AltosLib.AO_GPS_NUM_SAT_MASK) >> AltosLib.AO_GPS_NUM_SAT_SHIFT < 4) -			flags = (flags & ~AltosLib.AO_GPS_NUM_SAT_MASK) | (4 << AltosLib.AO_GPS_NUM_SAT_SHIFT); -		flags |= AltosLib.AO_GPS_RUNNING; -		flags |= AltosLib.AO_GPS_VALID; - -		int new_seconds = seconds + diff; -		if (new_seconds < 0) -			new_seconds += 24 * 3600; -		int new_second = (new_seconds % 60); -		int new_minutes = (new_seconds / 60); -		int new_minute = (new_minutes % 60); -		int new_hours = (new_minutes / 60); -		int new_hour = (new_hours % 24); - -		bad.a = new_hour + (new_minute << 8); -		bad.b = new_second + (flags << 8); -	} - -	/* -	 * Read the whole file, dumping records into a RB tree so -	 * we can enumerate them in time order -- the eeprom data -	 * are sometimes out of order with GPS data getting timestamps -	 * matching the first packet out of the GPS unit but not -	 * written until the final GPS packet has been received. -	 */ -	public AltosEepromIterable (FileInputStream input) { -		records = new TreeSet<AltosOrderedRecord>(); - -		AltosOrderedRecord last_gps_time = null; - -		int index = 0; -		int prev_tick = 0; -		boolean prev_tick_valid = false; -		boolean missing_time = false; - -		try { -			for (;;) { -				String line = AltosLib.gets(input); -				if (line == null) -					break; -				AltosOrderedRecord record = new AltosOrderedRecord(line, index++, prev_tick, prev_tick_valid); -				if (record.cmd == AltosLib.AO_LOG_INVALID) -					continue; -				prev_tick = record.tick; -				if (record.cmd < AltosLib.AO_LOG_CONFIG_VERSION) -					prev_tick_valid = true; -				if (record.cmd == AltosLib.AO_LOG_FLIGHT) { -					flight_record = record; -					continue; -				} - -				/* Two firmware bugs caused the loss of some GPS data. -				 * The flight date would never be recorded, and often -				 * the flight time would get overwritten by another -				 * record. Detect the loss of the GPS date and fix up the -				 * missing time records -				 */ -				if (record.cmd == AltosLib.AO_LOG_GPS_DATE) { -					gps_date_record = record; -					continue; -				} - -				/* go back and fix up any missing time values */ -				if (record.cmd == AltosLib.AO_LOG_GPS_TIME) { -					last_gps_time = record; -					if (missing_time) { -						Iterator<AltosOrderedRecord> iterator = records.iterator(); -						while (iterator.hasNext()) { -							AltosOrderedRecord old = iterator.next(); -							if (old.cmd == AltosLib.AO_LOG_GPS_TIME && -							    old.a == -1 && old.b == -1) -							{ -								update_time(record, old); -							} -						} -						missing_time = false; -					} -				} - -				if (record.cmd == AltosLib.AO_LOG_GPS_LAT) { -					if (last_gps_time == null || last_gps_time.tick != record.tick) { -						AltosOrderedRecord add_gps_time = new AltosOrderedRecord(AltosLib.AO_LOG_GPS_TIME, -													 record.tick, -													 -1, -1, index-1); -						if (last_gps_time != null) -							update_time(last_gps_time, add_gps_time); -						else -							missing_time = true; - -						records.add(add_gps_time); -						record.index = index++; -					} -				} -				records.add(record); - -				/* Bail after reading the 'landed' record; we're all done */ -				if (record.cmd == AltosLib.AO_LOG_STATE && -				    record.a == AltosLib.ao_flight_landed) -					break; -			} -		} catch (IOException io) { -		} catch (ParseException pe) { -		} -		try { -			input.close(); -		} catch (IOException ie) { -		} +	public Iterator<AltosEeprom> iterator() { +		if (eeproms == null) +			eeproms = new LinkedList<AltosEeprom>(); +		return eeproms.iterator();  	} -} +}
\ No newline at end of file diff --git a/altoslib/AltosEepromMetrum.java b/altoslib/AltosEepromMetrum.java new file mode 100644 index 00000000..72887032 --- /dev/null +++ b/altoslib/AltosEepromMetrum.java @@ -0,0 +1,214 @@ +/* + * Copyright © 2013 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 org.altusmetrum.altoslib_1; + +import java.text.*; + +public class AltosEepromMetrum { +	public int	cmd; +	public int	tick; +	public boolean	valid; +	public String	data; +	public int	config_a, config_b; + +	public int	data8[]; + +	public static final int	record_length = 16; +	static final int	header_length = 4; +	static final int	data_length = record_length - header_length; + +	public int data8(int i) { +		return data8[i]; +	} + +	public int data16(int i) { +		return ((data8[i] | (data8[i+1] << 8)) << 16) >> 16; +	} + +	public int data32(int i) { +		return data8[i] | (data8[i+1] << 8) | (data8[i+2] << 16) | (data8[i+3] << 24); +	} + +	/* AO_LOG_FLIGHT elements */ +	public int flight() { return data16(0); } +	public int ground_accel() { return data16(2); } +	public int ground_pres() { return data32(4); } +	public int ground_temp() { return data32(8); } + +	/* AO_LOG_STATE elements */ +	public int state() { return data16(0); } +	public int reason() { return data16(2); } + +	/* AO_LOG_SENSOR elements */ +	public int pres() { return data32(0); } +	public int temp() { return data32(4); } +	public int accel() { return data16(8); } + +	/* AO_LOG_TEMP_VOLT elements */ +	public int v_batt() { return data16(0); } +	public int sense_a() { return data16(2); } +	public int sense_m() { return data16(4); } + +	/* AO_LOG_GPS_POS elements */ +	public int latitude() { return data32(0); } +	public int longitude() { return data32(4); } +	public int altitude() { return data16(8); } + +	/* AO_LOG_GPS_TIME elements */ +	public int hour() { return data8(0); } +	public int minute() { return data8(1); } +	public int second() { return data8(2); } +	public int flags() { return data8(3); } +	public int year() { return data8(4); } +	public int month() { return data8(5); } +	public int day() { return data8(6); } +	 +	/* AO_LOG_GPS_SAT elements */ +	public int channels() { return data8(0); } +	public int more() { return data8(1); } +	public int svid(int n) { return data8(2 + n * 2); } +	public int c_n(int n) { return data8(2 + n * 2 + 1); } + +	public AltosEepromMetrum (AltosEepromChunk chunk, int start) throws ParseException { +		cmd = chunk.data(start); + +		valid = !chunk.erased(start, record_length); +		if (valid) { +			if (AltosConvert.checksum(chunk.data, start, record_length) != 0) +				throw new ParseException(String.format("invalid checksum at 0x%x", +								       chunk.address + start), 0); +		} else { +			cmd = AltosLib.AO_LOG_INVALID; +		} + +		tick = chunk.data16(start+2); + +		data8 = new int[data_length]; +		for (int i = 0; i < data_length; i++) +			data8[i] = chunk.data(start + header_length + i); +	} + +	public AltosEepromMetrum (String line) { +		valid = false; +		tick = 0; + +		if (line == null) { +			cmd = AltosLib.AO_LOG_INVALID; +			line = ""; +		} else { +			try { +				String[] tokens = line.split("\\s+"); + +				if (tokens[0].length() == 1) { +					if (tokens.length != 2 + data_length) { +						cmd = AltosLib.AO_LOG_INVALID; +						data = line; +					} else { +						cmd = tokens[0].codePointAt(0); +						tick = Integer.parseInt(tokens[1],16); +						valid = true; +						data8 = new int[data_length]; +						for (int i = 0; i < data_length; i++) +							data8[i] = Integer.parseInt(tokens[2 + i],16); +					} +				} else if (tokens[0].equals("Config") && tokens[1].equals("version:")) { +					cmd = AltosLib.AO_LOG_CONFIG_VERSION; +					data = tokens[2]; +				} else if (tokens[0].equals("Main") && tokens[1].equals("deploy:")) { +					cmd = AltosLib.AO_LOG_MAIN_DEPLOY; +					config_a = Integer.parseInt(tokens[2]); +				} else if (tokens[0].equals("Apogee") && tokens[1].equals("delay:")) { +					cmd = AltosLib.AO_LOG_APOGEE_DELAY; +					config_a = Integer.parseInt(tokens[2]); +				} else if (tokens[0].equals("Radio") && tokens[1].equals("channel:")) { +					cmd = AltosLib.AO_LOG_RADIO_CHANNEL; +					config_a = Integer.parseInt(tokens[2]); +				} else if (tokens[0].equals("Callsign:")) { +					cmd = AltosLib.AO_LOG_CALLSIGN; +					data = tokens[1].replaceAll("\"",""); +				} else if (tokens[0].equals("Accel") && tokens[1].equals("cal")) { +					cmd = AltosLib.AO_LOG_ACCEL_CAL; +					config_a = Integer.parseInt(tokens[3]); +					config_b = Integer.parseInt(tokens[5]); +				} else if (tokens[0].equals("Radio") && tokens[1].equals("cal:")) { +					cmd = AltosLib.AO_LOG_RADIO_CAL; +					config_a = Integer.parseInt(tokens[2]); +				} else if (tokens[0].equals("Max") && tokens[1].equals("flight") && tokens[2].equals("log:")) { +					cmd = AltosLib.AO_LOG_MAX_FLIGHT_LOG; +					config_a = Integer.parseInt(tokens[3]); +				} else if (tokens[0].equals("manufacturer")) { +					cmd = AltosLib.AO_LOG_MANUFACTURER; +					data = tokens[1]; +				} else if (tokens[0].equals("product")) { +					cmd = AltosLib.AO_LOG_PRODUCT; +					data = tokens[1]; +				} else if (tokens[0].equals("serial-number")) { +					cmd = AltosLib.AO_LOG_SERIAL_NUMBER; +					config_a = Integer.parseInt(tokens[1]); +				} else if (tokens[0].equals("log-format")) { +					cmd = AltosLib.AO_LOG_LOG_FORMAT; +					config_a = Integer.parseInt(tokens[1]); +				} else if (tokens[0].equals("software-version")) { +					cmd = AltosLib.AO_LOG_SOFTWARE_VERSION; +					data = tokens[1]; +				} else if (tokens[0].equals("ms5607")) { +					if (tokens[1].equals("reserved:")) { +						cmd = AltosLib.AO_LOG_BARO_RESERVED; +						config_a = Integer.parseInt(tokens[2]); +					} else if (tokens[1].equals("sens:")) { +						cmd = AltosLib.AO_LOG_BARO_SENS; +						config_a = Integer.parseInt(tokens[2]); +					} else if (tokens[1].equals("off:")) { +						cmd = AltosLib.AO_LOG_BARO_OFF; +						config_a = Integer.parseInt(tokens[2]); +					} else if (tokens[1].equals("tcs:")) { +						cmd = AltosLib.AO_LOG_BARO_TCS; +						config_a = Integer.parseInt(tokens[2]); +					} else if (tokens[1].equals("tco:")) { +						cmd = AltosLib.AO_LOG_BARO_TCO; +						config_a = Integer.parseInt(tokens[2]); +					} else if (tokens[1].equals("tref:")) { +						cmd = AltosLib.AO_LOG_BARO_TREF; +						config_a = Integer.parseInt(tokens[2]); +					} else if (tokens[1].equals("tempsens:")) { +						cmd = AltosLib.AO_LOG_BARO_TEMPSENS; +						config_a = Integer.parseInt(tokens[2]); +					} else if (tokens[1].equals("crc:")) { +						cmd = AltosLib.AO_LOG_BARO_CRC; +						config_a = Integer.parseInt(tokens[2]); +					} else { +						cmd = AltosLib.AO_LOG_INVALID; +						data = line; +					} +				} else { +					cmd = AltosLib.AO_LOG_INVALID; +					data = line; +				} +			} catch (NumberFormatException ne) { +				cmd = AltosLib.AO_LOG_INVALID; +				data = line; +			} +		} +	} + +	public AltosEepromMetrum(int in_cmd, int in_tick) { +		cmd = in_cmd; +		tick = in_tick; +		valid = true; +	} +} diff --git a/altoslib/AltosEepromMetrumIterable.java b/altoslib/AltosEepromMetrumIterable.java new file mode 100644 index 00000000..0387319e --- /dev/null +++ b/altoslib/AltosEepromMetrumIterable.java @@ -0,0 +1,358 @@ +/* + * Copyright © 2013 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 org.altusmetrum.altoslib_1; + +import java.io.*; +import java.util.*; +import java.text.*; + +public class AltosEepromMetrumIterable extends AltosRecordIterable { + +	static final int	seen_flight = 1; +	static final int	seen_sensor = 2; +	static final int	seen_temp_volt = 4; +	static final int	seen_deploy = 8; +	static final int	seen_gps_time = 16; +	static final int	seen_gps_lat = 32; +	static final int	seen_gps_lon = 64; + +	static final int	seen_basic = seen_flight|seen_sensor; + +	boolean			has_accel; +	boolean			has_gps; +	boolean			has_ignite; + +	AltosEepromMetrum	flight_record; +	AltosEepromMetrum	gps_date_record; + +	TreeSet<AltosOrderedMetrumRecord>	records; + +	AltosMs5607		baro; + +	LinkedList<AltosRecord>	list; + +	class EepromState { +		int	seen; +		int	n_pad_samples; +		double	ground_pres; +		int	gps_tick; +		int	boost_tick; +		int	sensor_tick; + +		EepromState() { +			seen = 0; +			n_pad_samples = 0; +			ground_pres = 0.0; +			gps_tick = 0; +		} +	} + +	void update_state(AltosRecordTM2 state, AltosEepromMetrum record, EepromState eeprom) { +		state.tick = record.tick; +		switch (record.cmd) { +		case AltosLib.AO_LOG_FLIGHT: +			eeprom.seen |= seen_flight; +			state.ground_accel = record.ground_accel(); +			state.flight_accel = record.ground_accel(); +			state.ground_pres = baro.set(record.ground_pres(), record.ground_temp()); +			state.flight_pres = state.ground_pres; +			state.flight = record.data16(0); +			eeprom.boost_tick = record.tick; +			break; +		case AltosLib.AO_LOG_STATE: +			state.state = record.state(); +			break; +		case AltosLib.AO_LOG_SENSOR: +			state.accel = record.accel(); +			baro.set(record.pres(), record.temp()); +			state.pres = baro.pa; +			state.temp = baro.cc; +			if (state.state < AltosLib.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; +			if ((eeprom.seen & seen_sensor) == 0) +				eeprom.sensor_tick = record.tick - 1; +			state.flight_vel += (state.accel_plus_g - state.accel) * (record.tick - eeprom.sensor_tick); +			eeprom.seen |= seen_sensor; +			eeprom.sensor_tick = record.tick; +			has_accel = true; +			break; +		case AltosLib.AO_LOG_TEMP_VOLT: +			state.v_batt = record.v_batt(); +			state.sense_a = record.sense_a(); +			state.sense_m = record.sense_m(); +			eeprom.seen |= seen_temp_volt; +			break; +		case AltosLib.AO_LOG_GPS_POS: +			eeprom.gps_tick = state.tick; +			state.gps = new AltosGPS(); + +			state.gps.lat = record.latitude() / 1e7; +			state.gps.lon = record.longitude() / 1e7; +			state.gps.alt = record.altitude(); +			break; + +		case AltosLib.AO_LOG_GPS_TIME: +			state.gps.year = record.year() + 2000; +			state.gps.month = record.month(); +			state.gps.day = record.day(); + +			state.gps.hour = record.hour(); +			state.gps.minute = record.minute(); +			state.gps.second = record.second(); + +			int flags = record.flags(); +			state.gps.connected = (flags & AltosLib.AO_GPS_RUNNING) != 0; +			state.gps.locked = (flags & AltosLib.AO_GPS_VALID) != 0; +			state.gps.nsat = (flags & AltosLib.AO_GPS_NUM_SAT_MASK) >> +				AltosLib.AO_GPS_NUM_SAT_SHIFT; +			state.gps_sequence++; +			has_gps = true; +			eeprom.seen |= seen_gps_time | seen_gps_lat | seen_gps_lon; +			break; +		case AltosLib.AO_LOG_GPS_SAT: +			if (state.tick == eeprom.gps_tick) { +				int nsat = record.channels(); +				for (int i = 0; i < nsat; i++) +					state.gps.add_sat(record.svid(i), record.c_n(i)); +			} +			break; +		case AltosLib.AO_LOG_CONFIG_VERSION: +			break; +		case AltosLib.AO_LOG_MAIN_DEPLOY: +			break; +		case AltosLib.AO_LOG_APOGEE_DELAY: +			break; +		case AltosLib.AO_LOG_RADIO_CHANNEL: +			break; +		case AltosLib.AO_LOG_CALLSIGN: +			state.callsign = record.data; +			break; +		case AltosLib.AO_LOG_ACCEL_CAL: +			state.accel_plus_g = record.config_a; +			state.accel_minus_g = record.config_b; +			break; +		case AltosLib.AO_LOG_RADIO_CAL: +			break; +		case AltosLib.AO_LOG_MANUFACTURER: +			break; +		case AltosLib.AO_LOG_PRODUCT: +			break; +		case AltosLib.AO_LOG_SERIAL_NUMBER: +			state.serial = record.config_a; +			break; +		case AltosLib.AO_LOG_SOFTWARE_VERSION: +			break; +		case AltosLib.AO_LOG_BARO_RESERVED: +			baro.reserved = record.config_a; +			break; +		case AltosLib.AO_LOG_BARO_SENS: +			baro.sens =record.config_a; +			break; +		case AltosLib.AO_LOG_BARO_OFF: +			baro.off =record.config_a; +			break; +		case AltosLib.AO_LOG_BARO_TCS: +			baro.tcs =record.config_a; +			break; +		case AltosLib.AO_LOG_BARO_TCO: +			baro.tco =record.config_a; +			break; +		case AltosLib.AO_LOG_BARO_TREF: +			baro.tref =record.config_a; +			break; +		case AltosLib.AO_LOG_BARO_TEMPSENS: +			baro.tempsens =record.config_a; +			break; +		case AltosLib.AO_LOG_BARO_CRC: +			baro.crc =record.config_a; +			break; +		} +		state.seen |= eeprom.seen; +	} + +	LinkedList<AltosRecord> make_list() { +		LinkedList<AltosRecord>			list = new LinkedList<AltosRecord>(); +		Iterator<AltosOrderedMetrumRecord>	iterator = records.iterator(); +		AltosOrderedMetrumRecord		record = null; +		AltosRecordTM2				state = new AltosRecordTM2(); +		//boolean				last_reported = false; +		EepromState				eeprom = new EepromState(); + +		state.state = AltosLib.ao_flight_pad; +		state.accel_plus_g = 15758; +		state.accel_minus_g = 16294; + +		/* Pull in static data from the flight and gps_date records */ +		if (flight_record != null) +			update_state(state, flight_record, eeprom); +		if (gps_date_record != null) +			update_state(state, gps_date_record, eeprom); + +		while (iterator.hasNext()) { +			record = iterator.next(); +			if ((eeprom.seen & seen_basic) == seen_basic && record.tick != state.tick) { +				AltosRecordTM2 r = state.clone(); +				r.time = (r.tick - eeprom.boost_tick) / 100.0; +				list.add(r); +			} +			update_state(state, record, eeprom); +		} +		AltosRecordTM2 r = state.clone(); +		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 boolean has_gps() { return has_gps; } +	public boolean has_accel() { return has_accel; } +	public boolean has_ignite() { return has_ignite; } + +	public void write_comments(PrintStream out) { +		Iterator<AltosOrderedMetrumRecord>	iterator = records.iterator(); +		out.printf("# Comments\n"); +		while (iterator.hasNext()) { +			AltosOrderedMetrumRecord	record = iterator.next(); +			switch (record.cmd) { +			case AltosLib.AO_LOG_CONFIG_VERSION: +				out.printf("# Config version: %s\n", record.data); +				break; +			case AltosLib.AO_LOG_MAIN_DEPLOY: +				out.printf("# Main deploy: %s\n", record.config_a); +				break; +			case AltosLib.AO_LOG_APOGEE_DELAY: +				out.printf("# Apogee delay: %s\n", record.config_a); +				break; +			case AltosLib.AO_LOG_RADIO_CHANNEL: +				out.printf("# Radio channel: %s\n", record.config_a); +				break; +			case AltosLib.AO_LOG_CALLSIGN: +				out.printf("# Callsign: %s\n", record.data); +				break; +			case AltosLib.AO_LOG_ACCEL_CAL: +				out.printf ("# Accel cal: %d %d\n", record.config_a, record.config_b); +				break; +			case AltosLib.AO_LOG_RADIO_CAL: +				out.printf ("# Radio cal: %d\n", record.config_a); +				break; +			case AltosLib.AO_LOG_MAX_FLIGHT_LOG: +				out.printf ("# Max flight log: %d\n", record.config_a); +				break; +			case AltosLib.AO_LOG_MANUFACTURER: +				out.printf ("# Manufacturer: %s\n", record.data); +				break; +			case AltosLib.AO_LOG_PRODUCT: +				out.printf ("# Product: %s\n", record.data); +				break; +			case AltosLib.AO_LOG_SERIAL_NUMBER: +				out.printf ("# Serial number: %d\n", record.config_a); +				break; +			case AltosLib.AO_LOG_SOFTWARE_VERSION: +				out.printf ("# Software version: %s\n", record.data); +				break; +			case AltosLib.AO_LOG_BARO_RESERVED: +				out.printf ("# Baro reserved: %d\n", record.config_a); +				break; +			case AltosLib.AO_LOG_BARO_SENS: +				out.printf ("# Baro sens: %d\n", record.config_a); +				break; +			case AltosLib.AO_LOG_BARO_OFF: +				out.printf ("# Baro off: %d\n", record.config_a); +				break; +			case AltosLib.AO_LOG_BARO_TCS: +				out.printf ("# Baro tcs: %d\n", record.config_a); +				break; +			case AltosLib.AO_LOG_BARO_TCO: +				out.printf ("# Baro tco: %d\n", record.config_a); +				break; +			case AltosLib.AO_LOG_BARO_TREF: +				out.printf ("# Baro tref: %d\n", record.config_a); +				break; +			case AltosLib.AO_LOG_BARO_TEMPSENS: +				out.printf ("# Baro tempsens: %d\n", record.config_a); +				break; +			case AltosLib.AO_LOG_BARO_CRC: +				out.printf ("# Baro crc: %d\n", record.config_a); +				break; +			} +		} +	} + +	/* +	 * Read the whole file, dumping records into a RB tree so +	 * we can enumerate them in time order -- the eeprom data +	 * are sometimes out of order with GPS data getting timestamps +	 * matching the first packet out of the GPS unit but not +	 * written until the final GPS packet has been received. +	 */ +	public AltosEepromMetrumIterable (FileInputStream input) { +		records = new TreeSet<AltosOrderedMetrumRecord>(); + +		AltosOrderedMetrumRecord last_gps_time = null; + +		baro = new AltosMs5607(); + +		int index = 0; +		int prev_tick = 0; +		boolean prev_tick_valid = false; +		boolean missing_time = false; + +		try { +			for (;;) { +				String line = AltosLib.gets(input); +				if (line == null) +					break; +				AltosOrderedMetrumRecord record = new AltosOrderedMetrumRecord(line, index++, prev_tick, prev_tick_valid); +				if (record.cmd == AltosLib.AO_LOG_INVALID) +					continue; +				prev_tick = record.tick; +				if (record.cmd < AltosLib.AO_LOG_CONFIG_VERSION) +					prev_tick_valid = true; +				if (record.cmd == AltosLib.AO_LOG_FLIGHT) { +					flight_record = record; +					continue; +				} + +				records.add(record); + +				/* Bail after reading the 'landed' record; we're all done */ +				if (record.cmd == AltosLib.AO_LOG_STATE && +				    record.state() == AltosLib.ao_flight_landed) +					break; +			} +		} catch (IOException io) { +		} catch (ParseException pe) { +		} +		try { +			input.close(); +		} catch (IOException ie) { +		} +	} +} diff --git a/altoslib/AltosEepromMini.java b/altoslib/AltosEepromMini.java index 215cd3d9..ced87680 100644 --- a/altoslib/AltosEepromMini.java +++ b/altoslib/AltosEepromMini.java @@ -17,20 +17,12 @@  package org.altusmetrum.altoslib_1; +import java.io.*; +import java.util.*;  import java.text.*; -public class AltosEepromMini { -	public int	cmd; -	public int	tick; -	public boolean	valid; -	public String	data; -	public int	config_a, config_b; - -	public int	data8[]; - +public class AltosEepromMini extends AltosEeprom {  	public static final int	record_length = 16; -	static final int	header_length = 4; -	static final int	data_length = record_length - header_length;  	public int data8(int i) {  		return data8[i]; @@ -63,126 +55,23 @@ public class AltosEepromMini {  	public int sense_m() { return data16(8); }  	public int v_batt() { return data16(10); } -	public AltosEepromMini (AltosEepromChunk chunk, int start) throws ParseException { -		cmd = chunk.data(start); - -		valid = !chunk.erased(start, record_length); -		if (valid) { -			if (AltosConvert.checksum(chunk.data, start, record_length) != 0) -				throw new ParseException(String.format("invalid checksum at 0x%x", -								       chunk.address + start), 0); -		} else { -			cmd = AltosLib.AO_LOG_INVALID; +	public void update_state(AltosState state) { +		switch (cmd) { +		case AltosLib.AO_LOG_FLIGHT: +			break; +		case AltosLib.AO_LOG_STATE: +			break; +		case AltosLib.AO_LOG_SENSOR: +			break;  		} +	} -		tick = chunk.data16(start+2); - -		data8 = new int[data_length]; -		for (int i = 0; i < data_length; i++) -			data8[i] = chunk.data(start + header_length + i); +	public AltosEepromMini (AltosEepromChunk chunk, int start) throws ParseException { +		parse_chunk(chunk, start, record_length);  	}  	public AltosEepromMini (String line) { -		valid = false; -		tick = 0; - -		if (line == null) { -			cmd = AltosLib.AO_LOG_INVALID; -			line = ""; -		} else { -			try { -				String[] tokens = line.split("\\s+"); - -				if (tokens[0].length() == 1) { -					if (tokens.length != 2 + data_length) { -						cmd = AltosLib.AO_LOG_INVALID; -						data = line; -					} else { -						cmd = tokens[0].codePointAt(0); -						tick = Integer.parseInt(tokens[1],16); -						valid = true; -						data8 = new int[data_length]; -						for (int i = 0; i < data_length; i++) -							data8[i] = Integer.parseInt(tokens[2 + i],16); -					} -				} else if (tokens[0].equals("Config") && tokens[1].equals("version:")) { -					cmd = AltosLib.AO_LOG_CONFIG_VERSION; -					data = tokens[2]; -				} else if (tokens[0].equals("Main") && tokens[1].equals("deploy:")) { -					cmd = AltosLib.AO_LOG_MAIN_DEPLOY; -					config_a = Integer.parseInt(tokens[2]); -				} else if (tokens[0].equals("Apogee") && tokens[1].equals("delay:")) { -					cmd = AltosLib.AO_LOG_APOGEE_DELAY; -					config_a = Integer.parseInt(tokens[2]); -				} else if (tokens[0].equals("Radio") && tokens[1].equals("channel:")) { -					cmd = AltosLib.AO_LOG_RADIO_CHANNEL; -					config_a = Integer.parseInt(tokens[2]); -				} else if (tokens[0].equals("Callsign:")) { -					cmd = AltosLib.AO_LOG_CALLSIGN; -					data = tokens[1].replaceAll("\"",""); -				} else if (tokens[0].equals("Accel") && tokens[1].equals("cal")) { -					cmd = AltosLib.AO_LOG_ACCEL_CAL; -					config_a = Integer.parseInt(tokens[3]); -					config_b = Integer.parseInt(tokens[5]); -				} else if (tokens[0].equals("Radio") && tokens[1].equals("cal:")) { -					cmd = AltosLib.AO_LOG_RADIO_CAL; -					config_a = Integer.parseInt(tokens[2]); -				} else if (tokens[0].equals("Max") && tokens[1].equals("flight") && tokens[2].equals("log:")) { -					cmd = AltosLib.AO_LOG_MAX_FLIGHT_LOG; -					config_a = Integer.parseInt(tokens[3]); -				} else if (tokens[0].equals("manufacturer")) { -					cmd = AltosLib.AO_LOG_MANUFACTURER; -					data = tokens[1]; -				} else if (tokens[0].equals("product")) { -					cmd = AltosLib.AO_LOG_PRODUCT; -					data = tokens[1]; -				} else if (tokens[0].equals("serial-number")) { -					cmd = AltosLib.AO_LOG_SERIAL_NUMBER; -					config_a = Integer.parseInt(tokens[1]); -				} else if (tokens[0].equals("log-format")) { -					cmd = AltosLib.AO_LOG_LOG_FORMAT; -					config_a = Integer.parseInt(tokens[1]); -				} else if (tokens[0].equals("software-version")) { -					cmd = AltosLib.AO_LOG_SOFTWARE_VERSION; -					data = tokens[1]; -				} else if (tokens[0].equals("ms5607")) { -					if (tokens[1].equals("reserved:")) { -						cmd = AltosLib.AO_LOG_BARO_RESERVED; -						config_a = Integer.parseInt(tokens[2]); -					} else if (tokens[1].equals("sens:")) { -						cmd = AltosLib.AO_LOG_BARO_SENS; -						config_a = Integer.parseInt(tokens[2]); -					} else if (tokens[1].equals("off:")) { -						cmd = AltosLib.AO_LOG_BARO_OFF; -						config_a = Integer.parseInt(tokens[2]); -					} else if (tokens[1].equals("tcs:")) { -						cmd = AltosLib.AO_LOG_BARO_TCS; -						config_a = Integer.parseInt(tokens[2]); -					} else if (tokens[1].equals("tco:")) { -						cmd = AltosLib.AO_LOG_BARO_TCO; -						config_a = Integer.parseInt(tokens[2]); -					} else if (tokens[1].equals("tref:")) { -						cmd = AltosLib.AO_LOG_BARO_TREF; -						config_a = Integer.parseInt(tokens[2]); -					} else if (tokens[1].equals("tempsens:")) { -						cmd = AltosLib.AO_LOG_BARO_TEMPSENS; -						config_a = Integer.parseInt(tokens[2]); -					} else if (tokens[1].equals("crc:")) { -						cmd = AltosLib.AO_LOG_BARO_CRC; -						config_a = Integer.parseInt(tokens[2]); -					} else { -						cmd = AltosLib.AO_LOG_INVALID; -						data = line; -					} -				} else { -					cmd = AltosLib.AO_LOG_INVALID; -					data = line; -				} -			} catch (NumberFormatException ne) { -				cmd = AltosLib.AO_LOG_INVALID; -				data = line; -			} -		} +		parse_string(line, record_length);  	}  	public AltosEepromMini(int in_cmd, int in_tick) { @@ -190,4 +79,22 @@ public class AltosEepromMini {  		tick = in_tick;  		valid = true;  	} + +	static public LinkedList<AltosEeprom> read(FileInputStream input) { +		LinkedList<AltosEeprom> minis = new LinkedList<AltosEeprom>(); + +		for (;;) { +			try { +				String line = AltosLib.gets(input); +				if (line == null) +					break; +				AltosEepromMini mini = new AltosEepromMini(line); +				minis.add(mini); +			} catch (IOException ie) { +				break; +			} +		} + +		return minis; +	}  } diff --git a/altoslib/AltosEepromOldIterable.java b/altoslib/AltosEepromOldIterable.java new file mode 100644 index 00000000..ef82828b --- /dev/null +++ b/altoslib/AltosEepromOldIterable.java @@ -0,0 +1,435 @@ +/* + * 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 org.altusmetrum.altoslib_1; + +import java.io.*; +import java.util.*; +import java.text.*; + +public class AltosEepromOldIterable extends AltosRecordIterable { + +	static final int	seen_basic = AltosRecord.seen_flight|AltosRecord.seen_sensor; + +	boolean			has_accel; +	boolean			has_gps; +	boolean			has_ignite; + +	AltosEepromRecord	flight_record; +	AltosEepromRecord	gps_date_record; + +	TreeSet<AltosOrderedRecord>	records; + +	LinkedList<AltosRecord>	list; + +	class EepromState { +		int	seen; +		int	n_pad_samples; +		double	ground_pres; +		int	gps_tick; +		int	boost_tick; +		int	sensor_tick; + +		EepromState() { +			seen = 0; +			n_pad_samples = 0; +			ground_pres = 0.0; +			gps_tick = 0; +		} +	} + +	void update_state(AltosRecordTM state, AltosEepromRecord record, EepromState eeprom) { +		state.tick = record.tick; +		switch (record.cmd) { +		case AltosLib.AO_LOG_FLIGHT: +			eeprom.seen |= AltosRecord.seen_flight; +			state.ground_accel = record.a; +			state.flight_accel = record.a; +			state.flight = record.b; +			eeprom.boost_tick = record.tick; +			break; +		case AltosLib.AO_LOG_SENSOR: +			state.accel = record.a; +			state.pres = record.b; +			if (state.state < AltosLib.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; +			if ((eeprom.seen & AltosRecord.seen_sensor) == 0) +				eeprom.sensor_tick = record.tick - 1; +			state.flight_vel += (state.accel_plus_g - state.accel) * (record.tick - eeprom.sensor_tick); +			eeprom.seen |= AltosRecord.seen_sensor; +			eeprom.sensor_tick = record.tick; +			has_accel = true; +			break; +		case AltosLib.AO_LOG_PRESSURE: +			state.pres = record.b; +			state.flight_pres = state.pres; +			if (eeprom.n_pad_samples == 0) { +				eeprom.n_pad_samples++; +				state.ground_pres = state.pres; +			} +			eeprom.seen |= AltosRecord.seen_sensor; +			break; +		case AltosLib.AO_LOG_TEMP_VOLT: +			state.temp = record.a; +			state.batt = record.b; +			eeprom.seen |= AltosRecord.seen_temp_volt; +			break; +		case AltosLib.AO_LOG_DEPLOY: +			state.drogue = record.a; +			state.main = record.b; +			eeprom.seen |= AltosRecord.seen_deploy; +			has_ignite = true; +			break; +		case AltosLib.AO_LOG_STATE: +			state.state = record.a; +			break; +		case AltosLib.AO_LOG_GPS_TIME: +			eeprom.gps_tick = state.tick; +			eeprom.seen |= AltosRecord.seen_gps_time; +			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 & AltosLib.AO_GPS_RUNNING) != 0; +			state.gps.locked = (flags & AltosLib.AO_GPS_VALID) != 0; +			state.gps.nsat = (flags & AltosLib.AO_GPS_NUM_SAT_MASK) >> +				AltosLib.AO_GPS_NUM_SAT_SHIFT; +			state.gps_sequence++; +			has_gps = true; +			break; +		case AltosLib.AO_LOG_GPS_LAT: +			eeprom.seen |= AltosRecord.seen_gps_lat; +			int lat32 = record.a | (record.b << 16); +			if (state.gps == null) +				state.gps = new AltosGPS(); +			state.gps.lat = (double) lat32 / 1e7; +			break; +		case AltosLib.AO_LOG_GPS_LON: +			eeprom.seen |= AltosRecord.seen_gps_lon; +			int lon32 = record.a | (record.b << 16); +			if (state.gps == null) +				state.gps = new AltosGPS(); +			state.gps.lon = (double) lon32 / 1e7; +			break; +		case AltosLib.AO_LOG_GPS_ALT: +			if (state.gps == null) +				state.gps = new AltosGPS(); +			state.gps.alt = record.a; +			break; +		case AltosLib.AO_LOG_GPS_SAT: +			if (state.tick == eeprom.gps_tick) { +				int svid = record.a; +				int c_n0 = record.b >> 8; +				if (state.gps == null) +					state.gps = new AltosGPS(); +				state.gps.add_sat(svid, c_n0); +			} +			break; +		case AltosLib.AO_LOG_GPS_DATE: +			if (state.gps == null) +				state.gps = new AltosGPS(); +			state.gps.year = (record.a & 0xff) + 2000; +			state.gps.month = record.a >> 8; +			state.gps.day = record.b & 0xff; +			break; + +		case AltosLib.AO_LOG_CONFIG_VERSION: +			break; +		case AltosLib.AO_LOG_MAIN_DEPLOY: +			break; +		case AltosLib.AO_LOG_APOGEE_DELAY: +			break; +		case AltosLib.AO_LOG_RADIO_CHANNEL: +			break; +		case AltosLib.AO_LOG_CALLSIGN: +			state.callsign = record.data; +			break; +		case AltosLib.AO_LOG_ACCEL_CAL: +			state.accel_plus_g = record.a; +			state.accel_minus_g = record.b; +			break; +		case AltosLib.AO_LOG_RADIO_CAL: +			break; +		case AltosLib.AO_LOG_MANUFACTURER: +			break; +		case AltosLib.AO_LOG_PRODUCT: +			break; +		case AltosLib.AO_LOG_SERIAL_NUMBER: +			state.serial = record.a; +			break; +		case AltosLib.AO_LOG_SOFTWARE_VERSION: +			break; +		} +		state.seen |= eeprom.seen; +	} + +	LinkedList<AltosRecord> make_list() { +		LinkedList<AltosRecord>		list = new LinkedList<AltosRecord>(); +		Iterator<AltosOrderedRecord>	iterator = records.iterator(); +		AltosOrderedRecord		record = null; +		AltosRecordTM			state = new AltosRecordTM(); +		//boolean				last_reported = false; +		EepromState			eeprom = new EepromState(); + +		state.state = AltosLib.ao_flight_pad; +		state.accel_plus_g = 15758; +		state.accel_minus_g = 16294; +		state.flight_vel = 0; + +		/* Pull in static data from the flight and gps_date records */ +		if (flight_record != null) +			update_state(state, flight_record, eeprom); +		if (gps_date_record != null) +			update_state(state, gps_date_record, eeprom); + +		while (iterator.hasNext()) { +			record = iterator.next(); +			if ((eeprom.seen & seen_basic) == seen_basic && record.tick != state.tick) { +				AltosRecordTM r = state.clone(); +				r.time = (r.tick - eeprom.boost_tick) / 100.0; +				list.add(r); +			} +			update_state(state, record, eeprom); +		} +		AltosRecordTM r = state.clone(); +		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 boolean has_gps() { return has_gps; } +	public boolean has_accel() { return has_accel; } +	public boolean has_ignite() { return has_ignite; } + +	public void write_comments(PrintStream out) { +		Iterator<AltosOrderedRecord>	iterator = records.iterator(); +		out.printf("# Comments\n"); +		while (iterator.hasNext()) { +			AltosOrderedRecord	record = iterator.next(); +			switch (record.cmd) { +			case AltosLib.AO_LOG_CONFIG_VERSION: +				out.printf("# Config version: %s\n", record.data); +				break; +			case AltosLib.AO_LOG_MAIN_DEPLOY: +				out.printf("# Main deploy: %s\n", record.a); +				break; +			case AltosLib.AO_LOG_APOGEE_DELAY: +				out.printf("# Apogee delay: %s\n", record.a); +				break; +			case AltosLib.AO_LOG_RADIO_CHANNEL: +				out.printf("# Radio channel: %s\n", record.a); +				break; +			case AltosLib.AO_LOG_CALLSIGN: +				out.printf("# Callsign: %s\n", record.data); +				break; +			case AltosLib.AO_LOG_ACCEL_CAL: +				out.printf ("# Accel cal: %d %d\n", record.a, record.b); +				break; +			case AltosLib.AO_LOG_RADIO_CAL: +				out.printf ("# Radio cal: %d\n", record.a); +				break; +			case AltosLib.AO_LOG_MAX_FLIGHT_LOG: +				out.printf ("# Max flight log: %d\n", record.a); +				break; +			case AltosLib.AO_LOG_MANUFACTURER: +				out.printf ("# Manufacturer: %s\n", record.data); +				break; +			case AltosLib.AO_LOG_PRODUCT: +				out.printf ("# Product: %s\n", record.data); +				break; +			case AltosLib.AO_LOG_SERIAL_NUMBER: +				out.printf ("# Serial number: %d\n", record.a); +				break; +			case AltosLib.AO_LOG_SOFTWARE_VERSION: +				out.printf ("# Software version: %s\n", record.data); +				break; +			case AltosLib.AO_LOG_BARO_RESERVED: +				out.printf ("# Baro reserved: %d\n", record.a); +				break; +			case AltosLib.AO_LOG_BARO_SENS: +				out.printf ("# Baro sens: %d\n", record.a); +				break; +			case AltosLib.AO_LOG_BARO_OFF: +				out.printf ("# Baro off: %d\n", record.a); +				break; +			case AltosLib.AO_LOG_BARO_TCS: +				out.printf ("# Baro tcs: %d\n", record.a); +				break; +			case AltosLib.AO_LOG_BARO_TCO: +				out.printf ("# Baro tco: %d\n", record.a); +				break; +			case AltosLib.AO_LOG_BARO_TREF: +				out.printf ("# Baro tref: %d\n", record.a); +				break; +			case AltosLib.AO_LOG_BARO_TEMPSENS: +				out.printf ("# Baro tempsens: %d\n", record.a); +				break; +			case AltosLib.AO_LOG_BARO_CRC: +				out.printf ("# Baro crc: %d\n", record.a); +				break; +			} +		} +	} + +	/* +	 * Given an AO_LOG_GPS_TIME record with correct time, and one +	 * missing time, rewrite the missing time values with the good +	 * ones, assuming that the difference between them is 'diff' seconds +	 */ +	void update_time(AltosOrderedRecord good, AltosOrderedRecord bad) { + +		int diff = (bad.tick - good.tick + 50) / 100; + +		int hour = (good.a & 0xff); +		int minute = (good.a >> 8); +		int second = (good.b & 0xff); +		int flags = (good.b >> 8); +		int seconds = hour * 3600 + minute * 60 + second; + +		/* Make sure this looks like a good GPS value */ +		if ((flags & AltosLib.AO_GPS_NUM_SAT_MASK) >> AltosLib.AO_GPS_NUM_SAT_SHIFT < 4) +			flags = (flags & ~AltosLib.AO_GPS_NUM_SAT_MASK) | (4 << AltosLib.AO_GPS_NUM_SAT_SHIFT); +		flags |= AltosLib.AO_GPS_RUNNING; +		flags |= AltosLib.AO_GPS_VALID; + +		int new_seconds = seconds + diff; +		if (new_seconds < 0) +			new_seconds += 24 * 3600; +		int new_second = (new_seconds % 60); +		int new_minutes = (new_seconds / 60); +		int new_minute = (new_minutes % 60); +		int new_hours = (new_minutes / 60); +		int new_hour = (new_hours % 24); + +		bad.a = new_hour + (new_minute << 8); +		bad.b = new_second + (flags << 8); +	} + +	/* +	 * Read the whole file, dumping records into a RB tree so +	 * we can enumerate them in time order -- the eeprom data +	 * are sometimes out of order with GPS data getting timestamps +	 * matching the first packet out of the GPS unit but not +	 * written until the final GPS packet has been received. +	 */ +	public AltosEepromOldIterable (FileInputStream input) { +		records = new TreeSet<AltosOrderedRecord>(); + +		AltosOrderedRecord last_gps_time = null; + +		int index = 0; +		int prev_tick = 0; +		boolean prev_tick_valid = false; +		boolean missing_time = false; + +		try { +			for (;;) { +				String line = AltosLib.gets(input); +				if (line == null) +					break; +				AltosOrderedRecord record = new AltosOrderedRecord(line, index++, prev_tick, prev_tick_valid); +				if (record.cmd == AltosLib.AO_LOG_INVALID) +					continue; +				prev_tick = record.tick; +				if (record.cmd < AltosLib.AO_LOG_CONFIG_VERSION) +					prev_tick_valid = true; +				if (record.cmd == AltosLib.AO_LOG_FLIGHT) { +					flight_record = record; +					continue; +				} + +				/* Two firmware bugs caused the loss of some GPS data. +				 * The flight date would never be recorded, and often +				 * the flight time would get overwritten by another +				 * record. Detect the loss of the GPS date and fix up the +				 * missing time records +				 */ +				if (record.cmd == AltosLib.AO_LOG_GPS_DATE) { +					gps_date_record = record; +					continue; +				} + +				/* go back and fix up any missing time values */ +				if (record.cmd == AltosLib.AO_LOG_GPS_TIME) { +					last_gps_time = record; +					if (missing_time) { +						Iterator<AltosOrderedRecord> iterator = records.iterator(); +						while (iterator.hasNext()) { +							AltosOrderedRecord old = iterator.next(); +							if (old.cmd == AltosLib.AO_LOG_GPS_TIME && +							    old.a == -1 && old.b == -1) +							{ +								update_time(record, old); +							} +						} +						missing_time = false; +					} +				} + +				if (record.cmd == AltosLib.AO_LOG_GPS_LAT) { +					if (last_gps_time == null || last_gps_time.tick != record.tick) { +						AltosOrderedRecord add_gps_time = new AltosOrderedRecord(AltosLib.AO_LOG_GPS_TIME, +													 record.tick, +													 -1, -1, index-1); +						if (last_gps_time != null) +							update_time(last_gps_time, add_gps_time); +						else +							missing_time = true; + +						records.add(add_gps_time); +						record.index = index++; +					} +				} +				records.add(record); + +				/* Bail after reading the 'landed' record; we're all done */ +				if (record.cmd == AltosLib.AO_LOG_STATE && +				    record.a == AltosLib.ao_flight_landed) +					break; +			} +		} catch (IOException io) { +		} catch (ParseException pe) { +		} +		try { +			input.close(); +		} catch (IOException ie) { +		} +	} +} diff --git a/altoslib/AltosEepromTM.java b/altoslib/AltosEepromTM.java new file mode 100644 index 00000000..fc7ec321 --- /dev/null +++ b/altoslib/AltosEepromTM.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 org.altusmetrum.altoslib_1; + +import java.text.*; + +public class AltosEepromTM implements AltosStateUpdate { +	public int	cmd; +	public int	tick; +	public int	a; +	public int	b; +	public String	data; +	public boolean	tick_valid; + +	public static final int	record_length = 8; + +	public void update_state(AltosState state) { +		state.set_tick(tick); +		switch (cmd) { +		case AltosLib.AO_LOG_FLIGHT: +			state.ground_accel = a; +			state.flight = b; +			state.set_boost_tick(tick); +			state.time = 0; +			break; +		case AltosLib.AO_LOG_SENSOR: +			state.set_telemetrum(a, b); +			break; +		case AltosLib.AO_LOG_PRESSURE: +			state.set_telemetrum(AltosState.MISSING, b); +			break; +		case AltosLib.AO_LOG_TEMP_VOLT: +/* +			 +			record.temp = a; +			record.batt = b; +			eeprom_state.seen |= AltosRecord.seen_temp_volt; +*/ +			break; +		case AltosLib.AO_LOG_DEPLOY: +/* +			record.drogue = a; +			record.main = b; +			eeprom_state.seen |= AltosRecord.seen_deploy; +			has_ignite = true; +*/ +			break; +		case AltosLib.AO_LOG_STATE: +			state.state = a; +			break; +//		case AltosLib.AO_LOG_GPS_TIME: +//			eeprom_state.gps_tick = record.tick; +//			eeprom_state.seen |= AltosRecord.seen_gps_time; +//			AltosGPS old = state.gps; +//			AltosGPS gps = new AltosGPS(); +// +//			/* GPS date doesn't get repeated through the file */ +//			if (old != null) { +//				gps.year = old.year; +//				gps.month = old.month; +//				gps.day = old.day; +//			} +//			gps.hour = (a & 0xff); +//			gps.minute = (a >> 8); +//			gps.second = (b & 0xff); +// +//			int flags = (b >> 8); +//			gps.connected = (flags & AltosLib.AO_GPS_RUNNING) != 0; +//			gps.locked = (flags & AltosLib.AO_GPS_VALID) != 0; +//			gps.nsat = (flags & AltosLib.AO_GPS_NUM_SAT_MASK) >> +//				AltosLib.AO_GPS_NUM_SAT_SHIFT; +//			state.temp_gps = gps; +//			break; +//		case AltosLib.AO_LOG_GPS_LAT: +//			int lat32 = a | (b << 16); +//			if (state.temp_gps == null) +//				state.temp_gps = new AltosGPS(); +//			state.temp_gps.lat = (double) lat32 / 1e7; +//			break; +//		case AltosLib.AO_LOG_GPS_LON: +//			int lon32 = a | (b << 16); +//			if (state.temp_gps == null) +//				state.temp_gps = new AltosGPS(); +//			state.temp_gps.lon = (double) lon32 / 1e7; +//			break; +//		case AltosLib.AO_LOG_GPS_ALT: +//			if (state.temp_gps == null) +//				state.temp_gps = new AltosGPS(); +//			state.temp_gps.alt = a; +//			break; +//		case AltosLib.AO_LOG_GPS_SAT: +//			if (record.tick == eeprom_state.gps_tick) { +//				int svid = a; +//				int c_n0 = b >> 8; +//				if (record.gps == null) +//					record.gps = new AltosGPS(); +//				record.gps.add_sat(svid, c_n0); +//			} +//			break; +//		case AltosLib.AO_LOG_GPS_DATE: +//			if (record.gps == null) +//				record.gps = new AltosGPS(); +//			record.gps.year = (a & 0xff) + 2000; +//			record.gps.month = a >> 8; +//			record.gps.day = b & 0xff; +//			break; + +		case AltosLib.AO_LOG_CONFIG_VERSION: +			break; +		case AltosLib.AO_LOG_MAIN_DEPLOY: +			break; +		case AltosLib.AO_LOG_APOGEE_DELAY: +			break; +		case AltosLib.AO_LOG_RADIO_CHANNEL: +			break; +		case AltosLib.AO_LOG_CALLSIGN: +			state.callsign = data; +			break; +		case AltosLib.AO_LOG_ACCEL_CAL: +			state.accel_plus_g = a; +			state.accel_minus_g = b; +			break; +		case AltosLib.AO_LOG_RADIO_CAL: +			break; +		case AltosLib.AO_LOG_MANUFACTURER: +			break; +		case AltosLib.AO_LOG_PRODUCT: +			break; +		case AltosLib.AO_LOG_SERIAL_NUMBER: +			state.serial = a; +			break; +		case AltosLib.AO_LOG_SOFTWARE_VERSION: +			break; +		} +	} + +	public AltosEepromTM (AltosEepromChunk chunk, int start) throws ParseException { + +		cmd = chunk.data(start); +		tick_valid = true; + +		tick_valid = !chunk.erased(start, record_length); +		if (tick_valid) { +			if (AltosConvert.checksum(chunk.data, start, record_length) != 0) +				throw new ParseException(String.format("invalid checksum at 0x%x", +								       chunk.address + start), 0); +		} else { +			cmd = AltosLib.AO_LOG_INVALID; +		} + +		tick = chunk.data16(start + 2); +		a = chunk.data16(start + 4); +		b = chunk.data16(start + 6); + +		data = null; +	} + +	public AltosEepromTM (String line) { +		tick_valid = false; +		tick = 0; +		a = 0; +		b = 0; +		data = null; +		if (line == null) { +			cmd = AltosLib.AO_LOG_INVALID; +			data = ""; +		} else { +			try { +				String[] tokens = line.split("\\s+"); + +				if (tokens[0].length() == 1) { +					if (tokens.length != 4) { +						cmd = AltosLib.AO_LOG_INVALID; +						data = line; +					} else { +						cmd = tokens[0].codePointAt(0); +						tick = Integer.parseInt(tokens[1],16); +						tick_valid = true; +						a = Integer.parseInt(tokens[2],16); +						b = Integer.parseInt(tokens[3],16); +					} +				} else if (tokens[0].equals("Config") && tokens[1].equals("version:")) { +					cmd = AltosLib.AO_LOG_CONFIG_VERSION; +					data = tokens[2]; +				} else if (tokens[0].equals("Main") && tokens[1].equals("deploy:")) { +					cmd = AltosLib.AO_LOG_MAIN_DEPLOY; +					a = Integer.parseInt(tokens[2]); +				} else if (tokens[0].equals("Apogee") && tokens[1].equals("delay:")) { +					cmd = AltosLib.AO_LOG_APOGEE_DELAY; +					a = Integer.parseInt(tokens[2]); +				} else if (tokens[0].equals("Radio") && tokens[1].equals("channel:")) { +					cmd = AltosLib.AO_LOG_RADIO_CHANNEL; +					a = Integer.parseInt(tokens[2]); +				} else if (tokens[0].equals("Callsign:")) { +					cmd = AltosLib.AO_LOG_CALLSIGN; +					data = tokens[1].replaceAll("\"",""); +				} else if (tokens[0].equals("Accel") && tokens[1].equals("cal")) { +					cmd = AltosLib.AO_LOG_ACCEL_CAL; +					a = Integer.parseInt(tokens[3]); +					b = Integer.parseInt(tokens[5]); +				} else if (tokens[0].equals("Radio") && tokens[1].equals("cal:")) { +					cmd = AltosLib.AO_LOG_RADIO_CAL; +					a = Integer.parseInt(tokens[2]); +				} else if (tokens[0].equals("Max") && tokens[1].equals("flight") && tokens[2].equals("log:")) { +					cmd = AltosLib.AO_LOG_MAX_FLIGHT_LOG; +					a = Integer.parseInt(tokens[3]); +				} else if (tokens[0].equals("manufacturer")) { +					cmd = AltosLib.AO_LOG_MANUFACTURER; +					data = tokens[1]; +				} else if (tokens[0].equals("product")) { +					cmd = AltosLib.AO_LOG_PRODUCT; +					data = tokens[1]; +				} else if (tokens[0].equals("serial-number")) { +					cmd = AltosLib.AO_LOG_SERIAL_NUMBER; +					a = Integer.parseInt(tokens[1]); +				} else if (tokens[0].equals("log-format")) { +					cmd = AltosLib.AO_LOG_LOG_FORMAT; +					a = Integer.parseInt(tokens[1]); +				} else if (tokens[0].equals("software-version")) { +					cmd = AltosLib.AO_LOG_SOFTWARE_VERSION; +					data = tokens[1]; +				} else { +					cmd = AltosLib.AO_LOG_INVALID; +					data = line; +				} +			} catch (NumberFormatException ne) { +v				cmd = AltosLib.AO_LOG_INVALID; +				data = line; +			} +		} +	} + +	public AltosEepromTM(int in_cmd, int in_tick, int in_a, int in_b) { +		tick_valid = true; +		cmd = in_cmd; +		tick = in_tick; +		a = in_a; +		b = in_b; +	} +} diff --git a/altoslib/AltosGPS.java b/altoslib/AltosGPS.java index f23842f3..f7929a4c 100644 --- a/altoslib/AltosGPS.java +++ b/altoslib/AltosGPS.java @@ -19,7 +19,7 @@ package org.altusmetrum.altoslib_1;  import java.text.*; -public class AltosGPS { +public class AltosGPS implements Cloneable {  	public final static int MISSING = AltosRecord.MISSING; @@ -216,6 +216,39 @@ public class AltosGPS {  		cc_gps_sat = null;  	} +	public AltosGPS clone() { +		AltosGPS	g = new AltosGPS(); + +		g.nsat = nsat; +		g.locked = locked; +		g.connected = connected; +		g.lat = lat;		g./* degrees (+N -S) */ +		g.lon = lon;		/* degrees (+E -W) */ +		g.alt = alt;		/* m */ +		g.year = year; +		g.month = month; +		g.day = day; +		g.hour = hour; +		g.minute = minute; +		g.second = second; + +		g.ground_speed = ground_speed;	/* m/s */ +		g.course = course;		/* degrees */ +		g.climb_rate = climb_rate;	/* m/s */ +		g.hdop = hdop;		/* unitless? */ +		g.h_error = h_error;	/* m */ +		g.v_error = v_error;	/* m */ + +		if (cc_gps_sat != null) { +			g.cc_gps_sat = new AltosGPSSat[cc_gps_sat.length]; +			for (int i = 0; i < cc_gps_sat.length; i++) { +				g.cc_gps_sat[i] = new AltosGPSSat(); +				g.cc_gps_sat[i].svid = cc_gps_sat[i].svid; +				g.cc_gps_sat[i].c_n0 = cc_gps_sat[i].c_n0; +			} +		} +	} +  	public AltosGPS(AltosGPS old) {  		if (old != null) {  			nsat = old.nsat; diff --git a/altoslib/AltosGreatCircle.java b/altoslib/AltosGreatCircle.java index f1cf0ae9..770c3c6c 100644 --- a/altoslib/AltosGreatCircle.java +++ b/altoslib/AltosGreatCircle.java @@ -19,7 +19,7 @@ package org.altusmetrum.altoslib_1;  import java.lang.Math; -public class AltosGreatCircle { +public class AltosGreatCircle implements Cloneable {  	public double	distance;  	public double	bearing;  	public double	range; @@ -95,6 +95,16 @@ public class AltosGreatCircle {  		elevation = Math.atan2(height_diff, distance) * 180 / Math.PI;  	} +	public AltosGreatCircle clone() { +		AltosGreatCircle n = new AltosGreatCircle(); + +		n.distance = distance; +		n.bearing = bearing; +		n.range = range; +		n.elevation = elevation; +		return n; +	} +  	public AltosGreatCircle (double start_lat, double start_lon,  				 double end_lat, double end_lon) {  		this(start_lat, start_lon, 0, end_lat, end_lon, 0); diff --git a/altoslib/AltosIMU.java b/altoslib/AltosIMU.java index 8f6731fa..46df35bf 100644 --- a/altoslib/AltosIMU.java +++ b/altoslib/AltosIMU.java @@ -17,7 +17,7 @@  package org.altusmetrum.altoslib_1; -public class AltosIMU { +public class AltosIMU implements Cloneable {  	public int		accel_x;  	public int		accel_y;  	public int		accel_z; @@ -25,5 +25,17 @@ public class AltosIMU {  	public int		gyro_x;  	public int		gyro_y;  	public int		gyro_z; + +	public AltosIMU clone() { +		AltosIMU	n = new AltosIMU(); + +		n.accel_x = accel_x; +		n.accel_y = accel_y; +		n.accel_z = accel_z; + +		n.gyro_x = gyro_x; +		n.gyro_y = gyro_y; +		n.gyro_z = gyro_z; +		return n;  }  	
\ No newline at end of file diff --git a/altoslib/AltosMag.java b/altoslib/AltosMag.java index b3bbd92f..cb6826f3 100644 --- a/altoslib/AltosMag.java +++ b/altoslib/AltosMag.java @@ -17,9 +17,18 @@  package org.altusmetrum.altoslib_1; -public class AltosMag { +public class AltosMag implements Cloneable {  	public int		x;  	public int		y;  	public int		z; + +	public AltosMag clone() { +		AltosMag n = new AltosMag(); + +		n.x = x; +		n.y = y; +		n.z = z; +		return n; +	}  }  	
\ No newline at end of file diff --git a/altoslib/AltosOrderedMetrumRecord.java b/altoslib/AltosOrderedMetrumRecord.java new file mode 100644 index 00000000..02cdf1fe --- /dev/null +++ b/altoslib/AltosOrderedMetrumRecord.java @@ -0,0 +1,52 @@ +/* + * Copyright © 2013 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 org.altusmetrum.altoslib_1; + +import java.text.ParseException; + +/* + * AltosRecords with an index field so they can be sorted by tick while preserving + * the original ordering for elements with matching ticks + */ +class AltosOrderedMetrumRecord extends AltosEepromMetrum implements Comparable<AltosOrderedMetrumRecord> { + +	public int	index; + +	public AltosOrderedMetrumRecord(String line, int in_index, int prev_tick, boolean prev_tick_valid) +		throws ParseException { +		super(line); +		if (prev_tick_valid) { +			tick |= (prev_tick & ~0xffff); +			if (tick < prev_tick) { +				if (prev_tick - tick > 0x8000) +					tick += 0x10000; +			} else { +				if (tick - prev_tick > 0x8000) +					tick -= 0x10000; +			} +		} +		index = in_index; +	} + +	public int compareTo(AltosOrderedMetrumRecord o) { +		int	tick_diff = tick - o.tick; +		if (tick_diff != 0) +			return tick_diff; +		return index - o.index; +	} +} diff --git a/altoslib/AltosRecordMini.java b/altoslib/AltosRecordMini.java index 253f3804..dacd89b8 100644 --- a/altoslib/AltosRecordMini.java +++ b/altoslib/AltosRecordMini.java @@ -31,6 +31,8 @@ public class AltosRecordMini extends AltosRecord {  	public int	flight_accel;  	public int	flight_vel; +	public int	flight_height; +  	public int	flight_pres;  	static double adc(int raw) { @@ -89,6 +91,7 @@ public class AltosRecordMini extends AltosRecord {  		flight_accel = old.flight_accel;  		flight_vel = old.flight_vel; +		flight_height = old.flight_height;  		flight_pres = old.flight_pres;  	} @@ -110,6 +113,7 @@ public class AltosRecordMini extends AltosRecord {  		flight_accel = 0;  		flight_vel = 0; +		flight_height = 0;  		flight_pres = 0;  	} diff --git a/altoslib/AltosRecordTM2.java b/altoslib/AltosRecordTM2.java new file mode 100644 index 00000000..0cd54f2c --- /dev/null +++ b/altoslib/AltosRecordTM2.java @@ -0,0 +1,156 @@ +/* + * Copyright © 2012 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 org.altusmetrum.altoslib_1; + +public class AltosRecordTM2 extends AltosRecord { + +	/* Sensor values */ +	public int	accel; +	public int	pres; +	public int	temp; +	 +	public int	v_batt; +	public int	sense_a; +	public int	sense_m; + +	public int      ground_accel; +	public int      ground_pres; +	public int      accel_plus_g; +	public int      accel_minus_g; + +	public int	flight_accel; +	public int	flight_vel; +	public int	flight_pres; + +	static double adc(int raw) { +		return raw / 4095.0; +	} + +	public double pressure() { +		if (pres != MISSING) +			return pres; +		return MISSING; +	} + +	public double ground_pressure() { +		if (ground_pres != MISSING) +			return ground_pres; +		return MISSING; +	} + +	public double battery_voltage() { +		if (v_batt != MISSING) +			return 3.3 * adc(v_batt) * (15.0 + 27.0) / 27.0; +		return MISSING; +	} + +	static double pyro(int raw) { +		if (raw != MISSING) +			return 3.3 * adc(raw) * (100.0 + 27.0) / 27.0; +		return MISSING; +	} + +	public double main_voltage() { +		return pyro(sense_m); +	} + +	public double drogue_voltage() { +		return pyro(sense_a); +	} + +	public double temperature() { +		if (temp != MISSING) +			return temp / 100.0; +		return MISSING; +	} +	 +	double accel_counts_per_mss() { +		double	counts_per_g = Math.abs(accel_minus_g - accel_plus_g) / 2; + +		return counts_per_g / 9.80665; +	} + +	public double acceleration() { +		if (ground_accel == MISSING || accel == MISSING) +			return MISSING; + +		if (accel_minus_g == MISSING || accel_plus_g == MISSING) +			return MISSING; + +		return (ground_accel - accel) / accel_counts_per_mss(); +	} + +	public void copy (AltosRecordTM2 old) { +		super.copy(old); + +		accel = old.accel; +		pres = old.pres; +		temp = old.temp; + +		v_batt = old.v_batt; +		sense_a = old.sense_a; +		sense_m = old.sense_m; + +		ground_accel = old.ground_accel; +		ground_pres = old.ground_pres; +		accel_plus_g = old.accel_plus_g; +		accel_minus_g = old.accel_minus_g; +		 +		flight_accel = old.flight_accel; +		flight_vel = old.flight_vel; +		flight_pres = old.flight_pres; +	} + +	public AltosRecordTM2 clone() { +		return new AltosRecordTM2(this); +	} + +	void make_missing() { + +		accel = MISSING; +		pres = MISSING; +		temp = MISSING; + +		v_batt = MISSING; +		sense_a = MISSING; +		sense_m = MISSING; + +		ground_accel = MISSING; +		ground_pres = MISSING; +		accel_plus_g = MISSING; +		accel_minus_g = MISSING; + +		flight_accel = 0; +		flight_vel = 0; +		flight_pres = 0; +	} + +	public AltosRecordTM2(AltosRecord old) { +		super.copy(old); +		make_missing(); +	} + +	public AltosRecordTM2(AltosRecordTM2 old) { +		copy(old); +	} + +	public AltosRecordTM2() { +		super(); +		make_missing(); +	} +} diff --git a/altoslib/AltosSelfFlash.java b/altoslib/AltosSelfFlash.java new file mode 100644 index 00000000..07917d5d --- /dev/null +++ b/altoslib/AltosSelfFlash.java @@ -0,0 +1,149 @@ +/* + * Copyright © 2013 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 org.altusmetrum.altoslib_1; + +import java.io.*; + +public class AltosSelfFlash { +	File			file; +	FileInputStream		input; +	AltosHexfile		image; +	AltosLink		link; +	boolean			aborted; +	AltosFlashListener	listener; +	byte[]			read_block, write_block; + +	void action(String s, int percent) { +		if (listener != null && !aborted) +			listener.position(s, percent); +	} + +	void action(int part, int total) { +		int percent = 100 * part / total; +		action(String.format("%d/%d (%d%%)", +				     part, total, percent), +		       percent); +	} + +	void read_block(long addr) { +		link.printf("R %x\n", addr); +		 +	} + +	void read_memory(long addr, int len) { +	} +		 +	void write_memory(long addr, byte[] data, int start, int len) { +		 +	} + +	void reboot() { +	} + +	public void flash() { +		try { +			int remain = image.data.length; +			long flash_addr = image.address; +			int image_start = 0; + +			action("start", 0); +			action(0, image.data.length); +			while (remain > 0 && !aborted) { +				int this_time = remain; +				if (this_time > 0x100) +					this_time = 0x100; + +				if (link != null) { +					/* write the data */ +					write_memory(flash_addr, image.data, image_start, this_time); + +					byte[] check = read_memory(flash_addr, this_time); +					for (int i = 0; i < this_time; i++) +						if (check[i] != image.data[image_start + i]) +							throw new IOException(String.format("Flash write failed at 0x%x (%02x != %02x)", +											    image.address + image_start + i, +											    check[i], image.data[image_start + i])); +				} else { +					Thread.sleep(100); +				} + +				remain -= this_time; +				flash_addr += this_time; +				image_start += this_time; + +				action(image.data.length - remain, image.data.length); +			} +			if (!aborted) { +				action("done", 100); +				if (link != null) { +					reboot(); +				} +			} +			if (link != null) +				link.close(); +		} catch (IOException ie) { +			action(ie.getMessage(), -1); +			abort(); +		} catch (InterruptedException ie) { +			abort(); +		} +	} + +	public void close() { +		if (link != null) +			link.close(); +	} + +	synchronized public void abort() { +		aborted = true; +		close(); +	} + +	public boolean check_rom_config() { +		if (link == null) +			return true; +		if (rom_config == null) +			rom_config = debug.romconfig(); +		return rom_config != null && rom_config.valid(); +	} + +	public void set_romconfig (AltosRomconfig romconfig) { +		rom_config = romconfig; +	} + +	public AltosRomconfig romconfig() { +		if (!check_rom_config()) +			return null; +		return rom_config; +	} + +	public AltosFlash(File file, AltosLink link, AltosFlashListener listener) +		throws IOException, FileNotFoundException, InterruptedException { +		this.file = file; +		this.link = link; +		this.listener = listener; +		this.read_block = new byte[256]; +		this.write_block = new byte[256]; +		input = new FileInputStream(file); +		image = new AltosHexfile(input); +		if (link != null) { +			debug.close(); +			throw new IOException("Debug port not connected"); +		} +	} +}
\ No newline at end of file diff --git a/altoslib/AltosSensorMetrum.java b/altoslib/AltosSensorMetrum.java new file mode 100644 index 00000000..686c78a8 --- /dev/null +++ b/altoslib/AltosSensorMetrum.java @@ -0,0 +1,55 @@ +/* + * Copyright © 2013 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 org.altusmetrum.altoslib_1; + +import java.util.concurrent.TimeoutException; + +class AltosSensorMetrum { +	int		tick; +	int		sense_a; +	int		sense_m; +	int		v_batt; + +	public AltosSensorMetrum(AltosLink link) throws InterruptedException, TimeoutException { +		String[] items = link.adc(); +		for (int i = 0; i < items.length;) { +			if (items[i].equals("tick:")) { +				tick = Integer.parseInt(items[i+1]); +				i += 2; +				continue; +			} +			if (items[i].equals("drogue:")) { +				sense_a = Integer.parseInt(items[i+1]); +				i += 2; +				continue; +			} +			if (items[i].equals("main:")) { +				sense_m = Integer.parseInt(items[i+1]); +				i += 2; +				continue; +			} +			if (items[i].equals("batt:")) { +				v_batt = Integer.parseInt(items[i+1]); +				i += 2; +				continue; +			} +			i++; +		} +	} +} + diff --git a/altoslib/AltosState.java b/altoslib/AltosState.java index e0d9bb1f..b40b744f 100644 --- a/altoslib/AltosState.java +++ b/altoslib/AltosState.java @@ -21,7 +21,7 @@  package org.altusmetrum.altoslib_1; -public class AltosState { +public class AltosState implements Cloneable {  	public AltosRecord data;  	/* derived data */ @@ -35,24 +35,29 @@ public class AltosState {  	public int	state;  	public boolean	landed;  	public boolean	ascent;	/* going up? */ -	public boolean boost;	/* under power */ +	public boolean	boost;	/* under power */  	public double	ground_altitude;  	public double	altitude;  	public double	height;  	public double	pressure;  	public double	acceleration; -	public double	battery; +	public double	battery_voltage; +	public double	pyro_voltage;  	public double	temperature; -	public double	main_sense; -	public double	drogue_sense; -	public double	accel_speed; -	public double	baro_speed; +	public double	apogee_voltage; +	public double	main_voltage; +	public double	speed; + +	public double	prev_height; +	public double	prev_speed; +	public double	prev_acceleration;  	public double	max_height;  	public double	max_acceleration; -	public double	max_accel_speed; -	public double	max_baro_speed; +	public double	max_speed; + +	public double	kalman_height, kalman_speed, kalman_acceleration;  	public AltosGPS	gps;  	public int gps_sequence; @@ -63,10 +68,11 @@ public class AltosState {  	public static final int MIN_PAD_SAMPLES = 10;  	public int	npad; -	public int	ngps;  	public int	gps_waiting;  	public boolean	gps_ready; +	public int	ngps; +  	public AltosGreatCircle from_pad;  	public double	elevation;	/* from pad */  	public double	range;		/* total distance */ @@ -78,80 +84,434 @@ public class AltosState {  	public int	speak_tick;  	public double	speak_altitude; +	public String	callsign; +	public double	accel_plus_g; +	public double	accel_minus_g; +	public double	accel; +	public double	ground_accel; + +	public int	log_format; +	public int	serial; + +	public AltosMs5607	baro; +  	public double speed() { -		if (ascent) -			return accel_speed; -		else -			return baro_speed; +		return speed;  	}  	public double max_speed() { -		if (max_accel_speed != 0) -			return max_accel_speed; -		return max_baro_speed; +		return max_speed; +	} + +	public void set_npad(int npad) { +		this.npad = npad; +		gps_waiting = MIN_PAD_SAMPLES - npad; +		if (this.gps_waiting < 0) +			gps_waiting = 0; +		gps_ready = gps_waiting == 0; +	} + +	public void init() { +		data = new AltosRecord(); + +		report_time = System.currentTimeMillis(); +		time = AltosRecord.MISSING; +		time_change = AltosRecord.MISSING; +		tick = AltosRecord.MISSING; +		state = AltosLib.ao_flight_invalid; +		landed = false; +		boost = false; + +		ground_altitude = AltosRecord.MISSING; +		altitude = AltosRecord.MISSING; +		height = AltosRecord.MISSING; +		pressure = AltosRecord.MISSING; +		acceleration = AltosRecord.MISSING; +		temperature = AltosRecord.MISSING; + +		prev_height = AltosRecord.MISSING; +		prev_speed = AltosRecord.MISSING; +		prev_acceleration = AltosRecord.MISSING; + +		battery_voltage = AltosRecord.MISSING; +		pyro_voltage = AltosRecord.MISSING; +		apogee_voltage = AltosRecord.MISSING; +		main_voltage = AltosRecord.MISSING; + + +		accel_speed = AltosRecord.MISSING; +		baro_speed = AltosRecord.MISSING; + +		kalman_height = AltosRecord.MISSING; +		kalman_speed = AltosRecord.MISSING; +		kalman_acceleration = AltosRecord.MISSING; + +		max_baro_speed = 0; +		max_accel_speed = 0; +		max_height = 0; +		max_acceleration = 0; + +		gps = null; +		gps_sequence = 0; + +		imu = null; +		mag = null; + +		set_npad(0); +		ngps = 0; + +		from_pad = null; +		elevation = AltosRecord.MISSING; +		range = AltosRecord.MISSING; +		gps_height = AltosRecord.MISSING; + +		pat_lat = AltosRecord.MISSING; +		pad_lon = AltosRecord.MISSING; +		pad_alt = AltosRecord.MISSING; + +		speak_tick = AltosRecord.MISSING; +		speak_altitude = AltosRecord.MISSING; + +		callsign = null; + +		accel_plus_g = AltosRecord.MISSING; +		accel_minus_g = AltosRecord.MISSING; +		log_format = AltosRecord.MISSING; +		serial = AltosRecord.MISSING; + +		baro = null; +	} + +	void copy(AltosState old) { + +		data = null; + +		if (old == null) { +			init(); +			return; +		} + +		report_time = old.report_time; +		time = old.time; +		time_change = old.time_change; +		tick = old.tick; + +		state = old.state; +		landed = old.landed; +		ascent = old.ascent; +		boost = old.boost; + +		ground_altitude = old.ground_altitude; +		altitude = old.altitude; +		height = old.height; +		pressure = old.pressure; +		acceleration = old.acceleration; +		battery_voltage = old.battery_voltage; +		pyro_voltage = old.pyro_voltage; +		temperature = old.temperature; +		apogee_voltage = old.apogee_voltage; +		main_voltage = old.main_voltage; +		accel_speed = old.accel_speed; +		baro_speed = old.baro_speed; + +		prev_height = old.height; +		prev_speed = old.speed; +		prev_acceleration = old.acceleration; + +		max_height = old.max_height; +		max_acceleration = old.max_acceleration; +		max_accel_speed = old.max_accel_speed; +		max_baro_speed = old.max_baro_speed; + +		kalman_height = old.kalman_height; +		kalman_speed = old.kalman_speed; +		kalman_acceleration = old.kalman_acceleration; + +		if (old.gps != null) +			gps = old.gps.clone(); +		else +			gps = null; +		gps_sequence = old.gps_sequence; + +		if (old.imu != null) +			imu = old.imu.clone(); +		else +			imu = null; + +		if (old.mag != null) +			mag = old.mag.clone(); +		else +			mag = null; + +		npad = old.npad; +		gps_waiting = old.gps_waiting; +		gps_ready = old.gps_ready; +		ngps = old.ngps; + +		if (old.from_pad != null) +			from_pad = old.from_pad.clone(); +		else +			from_pad = null; + +		elevation = old.elevation; +		range = old.range; + +		gps_height = old.gps_height; +		pad_lat = old.pad_lat; +		pad_lon = old.pad_lon; +		pad_alt = old.pad_alt; + +		speak_tick = old.speak_tick; +		speak_altitude = old.speak_altitude; + +		callsign = old.callsign; + +		accel_plus_g = old.accel_plus_g; +		accel_minus_g = old.accel_minus_g; +		log_format = old.log_format; +		serial = old.serial; + +		baro = old.baro; +	} + +	double ground_altitude() { +		 +	} + +	double altitude() { +		if (altitude != AltosRecord.MISSING) +			return altitude; +		if (gps != null) +			return gps.alt; +		return AltosRecord.MISSING; +	} + +	void update_vertical_pos() { + +		double	alt = altitude(); +		if (state == AltosLib.ao_flight_pad) { +			 +		} + +		if (kalman_height != AltosRecord.MISSING) +			height = kalman_height; +		else if (altitude != AltosRecord.MISSING && ground_altitude != AltosRecord.MISSING) +			height = altitude - ground_altitude; +		else +			height = AltosRecord.MISSING; + +		update_speed(); +	} + +	double motion_filter_value() { +		return 1/ Math.exp(time_change/10.0); +	} + +	void update_speed() { +		if (kalman_speed != AltosRecord.MISSING) +			speed = kalman_speed; +		else if (state != AltosLib.ao_flight_invalid && +			 time_change != AltosRecord.MISSING) +		{ +			if (ascent && acceleration != AltosRecord.MISSING) +			{ +				if (prev_speed == AltosRecord.MISSING) +					speed = acceleration * time_change; +				else +					speed = prev_speed + acceleration * time_change; +			} +			else if (height != AltosRecord.MISSING && +				 prev_height != AltosRecord.MISSING && +				 time_change != 0) +			{ +				double	new_speed = (height - prev_height) / time_change; + +				if (prev_speed == AltosRecord.MISSING) +					speed = new_speed; +				else { +					double	filter = motion_filter_value(); + +					speed = prev_speed * filter + new_speed * (1-filter); +				} +			} +		} +		if (acceleration == AltosRecord.MISSING) { +			if (prev_speed != AltosRecord.MISSING && time_change != 0) { +				double	new_acceleration = (speed - prev_speed) / time_change; + +				if (prev_acceleration == AltosRecord.MISSING) +					acceleration = new_acceleration; +				else { +					double filter = motion_filter_value(); + +					acceleration = prev_acceleration * filter + new_acceleration * (1-filter); +				} +			} +		} +	} +	 +	void update_accel() { +		if (accel == AltosRecord.MISSING) +			return; +		if (ground_Accel == AltosRecord.MISSING) +			return; +		if (accel_plus_g == AltosRecord.MISSING) +			return; +		if (accel_minus_g == AltosRecord.MISSING) +			return; + +		double counts_per_g = (accel_minus_g - accel_plus_g) / 2.0; +		double counts_per_mss = counts_per_g / 9.80665; + +		acceleration = (ground_accel - accel) / counts_per_mss; +		update_speed(); +	} + +	public void set_tick(int tick) { +		if (tick != AltosRecord.MISSING) { +			if (this.tick != AltosRecord.MISSING) { +				while (tick < this.tick) +					tick += 65536; +				time_change = (tick - this.tick) / 100.0; +			} else +				time_change = 0; +			this.tick = tick; +			update_time(); +		} +	} + +	public void set_state(int state) { +		if (state != AltosLib.ao_flight_invalid) { +			this.state = state; +			ascent = (AltosLib.ao_flight_boost <= state && +				  state <= AltosLib.ao_flight_coast); +			boost = (AltosLib.ao_flight_boost == state); +		} + +	} + +	public void set_altitude(double altitude) { +		if (altitude != AltosRecord.MISSING) { +			this.altitude = altitude; +			update_vertical_pos(); +		} +	} + +	public void set_ground_altitude(double ground_altitude) { +		if (ground_altitude != AltosRecord.MISSING) { +			this.ground_altitude = ground_altitude; +			update_vertical_pos(); +		} +	} + +	public void set_gps(AltosGPS gps, int sequence) { +		if (gps != null) { +			this.gps = gps.clone(); +			gps_sequence = sequence; +			update_vertical_pos(); +		} +	} + +	public void set_kalman(double height, double speed, double acceleration) { +		if (height != AltosRecord.MISSING) { +			kalman_height = height; +			kalman_speed = speed; +			kalman_acceleration = acceleration; +			baro_speed = accel_speed = speed; +			update_vertical_pos(); +		} +	} + +	public void set_pressure(double pressure) { +		if (pressure != AltosRecord.MISSING) { +			this.pressure = pressure; +			set_altitude(AltosConvert.pressure_to_altitude(pressure)); +		} +	} + +	public void set_accel_g(double accel_plus_g, double accel_minus_g) { +		if (accel_plus_g != AltosRecord.MISSING) { +			this.accel_plus_g = accel_plus_g; +			this.accel_minus_g = accel_minus_g; +			update_accel(); +		} +	} +	public void set_ground_accel(double ground_accel) { +		if (ground_accel != AltosRecord.MISSING) { +			this.ground_accel = ground_accel; +			update_accel(); +		} +	} + +	public void set_accel(double accel) { +		if (accel != AltosRecord.MISSING) { +			this.accel = accel; +		} +		update_accel(); +	} + +	public void set_temperature(double temperature) { +		if (temperature != AltosRecord.MISSING) +			this.temperature = temperature; +	} + +	public void set_battery_voltage(double battery_voltage) { +		if (battery_voltage != AltosRecord.MISSING) +			this.battery_voltage = battery_voltage; +	} + +	public void set_pyro_voltage(double pyro_voltage) { +		if (pyro_voltage != AltosRecord.MISSING) +			this.pyro_voltage = pyro_voltage; +	} + +	public void set_apogee_voltage(double apogee_voltage) { +		if (apogee_voltage != AltosRecord.MISSING) +			this.apogee_voltage = apogee_voltage; +	} + +	public void set_main_voltage(double main_voltage) { +		if (main_voltage != AltosRecord.MISSING) +			this.main_voltage = main_voltage;  	}  	public void init (AltosRecord cur, AltosState prev_state) { + +		if (cur == null) +			cur = new AltosRecord(); +  		data = cur;  		/* Discard previous state if it was for a different board */ -		if (prev_state != null && prev_state.data.serial != data.serial) +		if (prev_state != null && prev_state.serial != cur.serial)  			prev_state = null; -		ground_altitude = data.ground_altitude(); -		altitude = data.altitude(); -		if (altitude == AltosRecord.MISSING && data.gps != null) -			altitude = data.gps.alt; +		copy(prev_state); -		height = AltosRecord.MISSING; -		if (data.kalman_height != AltosRecord.MISSING) -			height = data.kalman_height; -		else { -			if (altitude != AltosRecord.MISSING && ground_altitude != AltosRecord.MISSING) { -				double	cur_height = altitude - ground_altitude; -				if (prev_state == null || prev_state.height == AltosRecord.MISSING) -					height = cur_height; -				else -					height = (prev_state.height * 15 + cur_height) / 16.0; -			} -		} +		set_ground_altitude(data.ground_altitude()); +		set_altitude(data.altitude()); + +		set_kalman(data.kalman_height, data.kalman_speed, data.kalman_acceleration);  		report_time = System.currentTimeMillis(); -		if (data.kalman_acceleration != AltosRecord.MISSING) -			acceleration = data.kalman_acceleration; -		else -			acceleration = data.acceleration(); -		temperature = data.temperature(); -		drogue_sense = data.drogue_voltage(); -		main_sense = data.main_voltage(); -		battery = data.battery_voltage(); -		pressure = data.pressure(); -		tick = data.tick; -		state = data.state; +		set_temperature(data.temperature()); +		set_apogee_voltage(data.drogue_voltage()); +		set_main_voltage(data.main_voltage()); +		set_battery_voltage(data.battery_voltage()); -		if (prev_state != null) { +		set_pressure(data.pressure()); -			/* Preserve any existing gps data */ -			npad = prev_state.npad; -			ngps = prev_state.ngps; -			gps = prev_state.gps; -			gps_sequence = prev_state.gps_sequence; -			pad_lat = prev_state.pad_lat; -			pad_lon = prev_state.pad_lon; -			pad_alt = prev_state.pad_alt; -			max_height = prev_state.max_height; -			max_acceleration = prev_state.max_acceleration; -			max_accel_speed = prev_state.max_accel_speed; -			max_baro_speed = prev_state.max_baro_speed; -			imu = prev_state.imu; -			mag = prev_state.mag; - -			/* make sure the clock is monotonic */ -			while (tick < prev_state.tick) -				tick += 65536; - -			time_change = (tick - prev_state.tick) / 100.0; +		set_tick(data.tick); +		set_state(data.state); + +		set_accel_g (data.accel_minus_g, data.accel_plus_g); +		set_ground_accel(data.ground_accel); +		set_accel (data.accel); + +		set_gps(data.gps, data.gps_sequence); + +		if (prev_state != null) {  			if (data.kalman_speed != AltosRecord.MISSING) {  				baro_speed = accel_speed = data.kalman_speed; @@ -200,6 +560,12 @@ public class AltosState {  			max_height = 0;  			max_acceleration = 0;  			time_change = 0; +			baro = new AltosMs5607(); +			callsign = ""; +			accel_plus_g = AltosRecord.MISSING; +			accel_minus_g = AltosRecord.MISSING; +			log_format = AltosRecord.MISSING; +			serial = AltosRecord.MISSING;  		}  		time = tick / 100.0; @@ -208,9 +574,9 @@ public class AltosState {  			/* Track consecutive 'good' gps reports, waiting for 10 of them */  			if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) -				npad++; +				set_npad(npad+1);  			else -				npad = 0; +				set_npad(0);  			/* Average GPS data while on the pad */  			if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) { @@ -233,16 +599,6 @@ public class AltosState {  		gps_sequence = data.gps_sequence; -		gps_waiting = MIN_PAD_SAMPLES - npad; -		if (gps_waiting < 0) -			gps_waiting = 0; - -		gps_ready = gps_waiting == 0; - -		ascent = (AltosLib.ao_flight_boost <= state && -			  state <= AltosLib.ao_flight_coast); -		boost = (AltosLib.ao_flight_boost == state); -  		/* Only look at accelerometer data under boost */  		if (boost && acceleration != AltosRecord.MISSING && (max_acceleration == AltosRecord.MISSING || acceleration > max_acceleration))  			max_acceleration = acceleration; @@ -270,6 +626,11 @@ public class AltosState {  		}  	} +	public AltosState clone() { +		AltosState s = new AltosState(data, this); +		return s; +	} +  	public AltosState(AltosRecord cur) {  		init(cur, null);  	} diff --git a/altoslib/AltosStateUpdate.java b/altoslib/AltosStateUpdate.java new file mode 100644 index 00000000..50460e21 --- /dev/null +++ b/altoslib/AltosStateUpdate.java @@ -0,0 +1,22 @@ +/* + * Copyright © 2013 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 org.altusmetrum.altoslib_1; + +public interface AltosStateUpdate { +	public void	update_state(AltosState state); +}
\ No newline at end of file diff --git a/altoslib/AltosTelemetryRecord.java b/altoslib/AltosTelemetryRecord.java index fdc3c88e..a744e61a 100644 --- a/altoslib/AltosTelemetryRecord.java +++ b/altoslib/AltosTelemetryRecord.java @@ -44,6 +44,7 @@ public abstract class AltosTelemetryRecord {  	final static int packet_type_companion = 0x07;  	final static int packet_type_MM_sensor = 0x08;  	final static int packet_type_MM_data = 0x09; +	final static int packet_type_Mini = 0x10;  	static AltosTelemetryRecord parse_hex(String hex)  throws ParseException, AltosCRCException {  		AltosTelemetryRecord	r; diff --git a/altoslib/AltosTelemetryRecordMetrumData.java b/altoslib/AltosTelemetryRecordMetrumData.java new file mode 100644 index 00000000..70179b28 --- /dev/null +++ b/altoslib/AltosTelemetryRecordMetrumData.java @@ -0,0 +1,54 @@ +/* + * Copyright © 2011 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 org.altusmetrum.altoslib_1; + + +public class AltosTelemetryRecordMetrumData extends AltosTelemetryRecordRaw { + +	int	ground_pres; +	int	ground_accel; +	int	accel_plus_g; +	int	accel_minus_g; + +	public AltosTelemetryRecordMetrumData(int[] in_bytes, int rssi) { +		super(in_bytes, rssi); + +		ground_pres = int32(8); +		ground_accel = int16(12); +		accel_plus_g = int16(14); +		accel_minus_g = int16(16); +	} + +	public AltosRecord update_state(AltosRecord previous) { +		AltosRecord	n = super.update_state(previous); + +		AltosRecordTM2	next; +		if (!(n instanceof AltosRecordTM2)) { +			next = new AltosRecordTM2(n); +		} else { +			next = (AltosRecordTM2) n; +		} + +		next.ground_accel = ground_accel; +		next.ground_pres = ground_pres; +		next.accel_plus_g = accel_plus_g; +		next.accel_minus_g = accel_minus_g; + +		return next; +	} +} diff --git a/altoslib/AltosTelemetryRecordMetrumSensor.java b/altoslib/AltosTelemetryRecordMetrumSensor.java new file mode 100644 index 00000000..e41242c5 --- /dev/null +++ b/altoslib/AltosTelemetryRecordMetrumSensor.java @@ -0,0 +1,81 @@ +/* + * Copyright © 2011 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 org.altusmetrum.altoslib_1; + + +public class AltosTelemetryRecordMetrumSensor extends AltosTelemetryRecordRaw { +	int	state; + +	int	accel; +	int	pres; +	int	temp; + +	int	acceleration; +	int	speed; +	int	height; + +	int	v_batt; +	int	sense_a; +	int	sense_m; + +	public AltosTelemetryRecordMetrumSensor(int[] in_bytes, int rssi) { +		super(in_bytes, rssi); + +		state	      = int8(5); +		accel         = int16(6); +		pres          = int32(8); +		temp          = int16(12); + +		acceleration  = int16(14); +		speed         = int16(16); +		height        = int16(18); + +		v_batt        = int16(20); +		sense_a       = int16(22); +		sense_m       = int16(24); +	} + +	public AltosRecord update_state(AltosRecord previous) { +		AltosRecord	n = super.update_state(previous); + +		AltosRecordTM2	next; +		if (!(n instanceof AltosRecordTM2)) { +			next = new AltosRecordTM2(n); +		} else { +			next = (AltosRecordTM2) n; +		} + +		next.state = state; + +		next.accel = accel; +		next.pres = pres; +		next.temp = temp; + +		next.kalman_acceleration = acceleration / 16.0; +		next.kalman_speed = speed / 16.0; +		next.kalman_height = height; + +		next.v_batt = v_batt; +		next.sense_a = sense_a; +		next.sense_m = sense_m; + +		next.seen |= AltosRecord.seen_sensor | AltosRecord.seen_temp_volt;; + +		return next; +	} +} diff --git a/altoslib/AltosTelemetryRecordMini.java b/altoslib/AltosTelemetryRecordMini.java new file mode 100644 index 00000000..75a66c16 --- /dev/null +++ b/altoslib/AltosTelemetryRecordMini.java @@ -0,0 +1,82 @@ +/* + * Copyright © 2011 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 org.altusmetrum.altoslib_1; + + +public class AltosTelemetryRecordMini extends AltosTelemetryRecordRaw { +	int	state; + +	int	accel; +	int	pres; +	int	temp; + +	int	v_batt; +	int	sense_a; +	int	sense_m; + +	int	acceleration; +	int	speed; +	int	height; + +	int	ground_pres; + +	public AltosTelemetryRecordMini(int[] in_bytes, int rssi) { +		super(in_bytes, rssi); + +		state	      = int8(5); +		v_batt	      = int16(6); +		sense_a	      = int16(8); +		sense_m	      = int16(10); + +		pres	      = int32(12); +		temp	      = int16(16); + +		acceleration  = int16(18); +		speed        = int16(20); +		height        = int16(22); + +		ground_pres   = int32(24); +	} + +	public AltosRecord update_state(AltosRecord previous) { +		AltosRecord	n = super.update_state(previous); + +		AltosRecordMini	next; +		if (!(n instanceof AltosRecordMini)) { +			next = new AltosRecordMini(n); +		} else { +			next = (AltosRecordMini) n; +		} + +		next.pres = pres; +		next.temp = temp; + +		next.sense_a = sense_a; +		next.sense_m = sense_m; + +		next.ground_pres = ground_pres; +		next.flight_accel = acceleration; +		next.flight_vel = speed; +		next.flight_height = height; +		next.flight_pres = pres; + +		next.seen |= AltosRecord.seen_sensor; + +		return next; +	} +} diff --git a/altoslib/Makefile.am b/altoslib/Makefile.am index 8c1cf2ad..8a41b90c 100644 --- a/altoslib/Makefile.am +++ b/altoslib/Makefile.am @@ -17,7 +17,10 @@ altoslib_JAVA = \  	AltosConvert.java \  	AltosCRCException.java \  	AltosDebug.java \ +	AltosEeprom.java \  	AltosEepromChunk.java \ +	AltosEepromFile.java \ +	AltosEepromHeader.java \  	AltosEepromIterable.java \  	AltosEepromLog.java \  	AltosEepromMega.java \ @@ -26,6 +29,7 @@ altoslib_JAVA = \  	AltosEepromTeleScience.java \  	AltosEepromMini.java \  	AltosEepromMiniIterable.java \ +	AltosEepromOldIterable.java \  	AltosFile.java \  	AltosFlash.java \  	AltosFlashListener.java \ @@ -65,6 +69,7 @@ altoslib_JAVA = \  	AltosSensorMM.java \  	AltosSensorTM.java \  	AltosState.java \ +	AltosStateUpdate.java \  	AltosTelemetry.java \  	AltosTelemetryIterable.java \  	AltosTelemetryMap.java \ @@ -80,6 +85,7 @@ altoslib_JAVA = \  	AltosTelemetryRecordSensor.java \  	AltosTelemetryRecordMegaSensor.java \  	AltosTelemetryRecordMegaData.java \ +	AltosTelemetryRecordMini.java \  	AltosUnitsListener.java \  	AltosMs5607.java \  	AltosIMU.java \ | 
