--- /dev/null
+/*
+ * 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];
+ }
+}
return ignite / 32767 * 15.0;
}
+ public static double
+ barometer_to_pressure(double count)
+ {
+ return ((count / 16.0) / 2047.0 + 0.095) / 0.009 * 1000.0;
+ }
+
public static double radio_to_frequency(int freq, int setting, int cal, int channel) {
double f;
--- /dev/null
+/*
+ * 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) {
+ }
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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.*;
+
+class AltosEepromIterator implements Iterator<AltosState> {
+ AltosState state;
+ Iterator<AltosEeprom> body;
+ AltosEeprom next;
+ boolean seen;
+
+ public boolean hasNext() {
+ return !seen || body.hasNext();
+ }
+
+ public AltosState next() {
+ if (seen) {
+ AltosState n = state.clone();
+ AltosEeprom e = body.next();
+
+ e.update_state(n);
+ state = n;
+ }
+ seen = true;
+ return state;
+ }
+
+ public void remove () {
+ }
+
+ public AltosEepromIterator(AltosState start, Iterator<AltosEeprom> body) {
+ this.state = start;
+ this.body = body;
+ this.seen = false;
+ }
+}
+
+public class AltosEepromFile extends AltosStateIterable {
+
+ AltosEepromIterable headers;
+ AltosEepromIterable body;
+ AltosState start;
+
+ public void write_comments(PrintStream out) {
+ headers.write(out);
+ }
+
+ public void write(PrintStream out) {
+ headers.write(out);
+ body.write(out);
+ }
+
+ public AltosEepromFile(FileInputStream input) {
+ headers = new AltosEepromIterable(AltosEepromHeader.read(input));
+
+ start = headers.state();
+
+ switch (start.log_format) {
+ case AltosLib.AO_LOG_FORMAT_FULL:
+ body = new AltosEepromIterable(AltosEepromTM.read(input));
+ break;
+ 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_TELEMINI:
+ case AltosLib.AO_LOG_FORMAT_EASYMINI:
+ body = new AltosEepromIterable(AltosEepromMini.read(input));
+ break;
+ }
+
+ /* Find boost tick */
+ AltosState state = start.clone();
+ for (AltosEeprom eeprom : body) {
+ eeprom.update_state(state);
+ if (state.state >= AltosLib.ao_flight_boost) {
+ start.set_boost_tick(state.tick);
+ break;
+ }
+ }
+ }
+
+ public Iterator<AltosState> iterator() {
+ AltosState state = start.clone();
+ Iterator<AltosEeprom> i = body.iterator();
+
+ while (i.hasNext() && !state.valid())
+ i.next().update_state(state);
+ return new AltosEepromIterator(state, i);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.set_accel_g(config_a, 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.set_serial(config_a);
+ break;
+ case AltosLib.AO_LOG_BARO_RESERVED:
+ state.make_baro();
+ state.baro.reserved = config_a;
+ break;
+ case AltosLib.AO_LOG_BARO_SENS:
+ state.make_baro();
+ state.baro.sens = config_a;
+ break;
+ case AltosLib.AO_LOG_BARO_OFF:
+ state.make_baro();
+ state.baro.off = config_a;
+ break;
+ case AltosLib.AO_LOG_BARO_TCS:
+ state.make_baro();
+ state.baro.tcs = config_a;
+ break;
+ case AltosLib.AO_LOG_BARO_TCO:
+ state.make_baro();
+ state.baro.tco = config_a;
+ break;
+ case AltosLib.AO_LOG_BARO_TREF:
+ state.make_baro();
+ state.baro.tref = config_a;
+ break;
+ case AltosLib.AO_LOG_BARO_TEMPSENS:
+ state.make_baro();
+ state.baro.tempsens = config_a;
+ break;
+ case AltosLib.AO_LOG_BARO_CRC:
+ state.make_baro();
+ 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+"));
+ }
+}
--- /dev/null
+/*
+ * 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();
+
+ 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
/*
- * 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
import java.util.*;
import java.text.*;
-public class AltosEepromIterable extends AltosRecordIterable {
+class AltosEepromOrdered implements Comparable<AltosEepromOrdered> {
+ AltosEeprom eeprom;
+ int index;
+ int tick;
- 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;
- }
+ int cmdi() {
+ if (eeprom.cmd == AltosLib.AO_LOG_FLIGHT)
+ return 0;
+ return 1;
}
- 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();
+ public int compareTo(AltosEepromOrdered o) {
+ int cmd_diff = cmdi() - o.cmdi();
- /* 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);
+ if (cmd_diff != 0)
+ return cmd_diff;
- 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;
+ int tick_diff = tick - o.tick;
- 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;
+ if (tick_diff != 0)
+ return tick_diff;
+ return index - o.index;
}
- 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);
+ AltosEepromOrdered (AltosEeprom eeprom, int index, int tick) {
+ this.eeprom = eeprom;
+ this.index = index;
+ this.tick = tick;
+ }
+}
- 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);
+class AltosEepromOrderedIterator implements Iterator<AltosEeprom> {
+ TreeSet<AltosEepromOrdered> olist;
+ Iterator<AltosEepromOrdered> oiterator;
+
+ public AltosEepromOrderedIterator(Iterable<AltosEeprom> eeproms) {
+ olist = new TreeSet<AltosEepromOrdered>();
+
+ int tick = 0;
+ int index = 0;
+ boolean first = true;
+
+ for (AltosEeprom e : eeproms) {
+ int t = e.tick;
+ if (first)
+ tick = t;
+ else {
+ while (t < tick - 32767)
+ t += 65536;
+ tick = t;
}
- update_state(state, record, eeprom);
+ olist.add(new AltosEepromOrdered(e, index++, tick));
}
- AltosRecordTM r = state.clone();
- r.time = (r.tick - eeprom.boost_tick) / 100.0;
- list.add(r);
- return list;
+ oiterator = olist.iterator();
}
- public Iterator<AltosRecord> iterator() {
- if (list == null)
- list = make_list();
- return list.iterator();
+ public boolean hasNext() {
+ return oiterator.hasNext();
}
- 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 AltosEeprom next() {
+ return oiterator.next().eeprom;
}
- /*
- * 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);
+ public void remove () {
}
+}
- /*
- * 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;
- }
+public class AltosEepromIterable implements Iterable<AltosEeprom> {
+ public LinkedList<AltosEeprom> eeproms;
- /* 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;
- }
+ public void write(PrintStream out) {
+ for (AltosEeprom eeprom : eeproms)
+ eeprom.write(out);
+ }
- /* 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;
- }
- }
+ public AltosState state() {
+ AltosState state = new AltosState();
- 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;
+ for (AltosEeprom header : eeproms)
+ header.update_state(state);
+ return state;
+ }
- records.add(add_gps_time);
- record.index = index++;
- }
- }
- records.add(record);
+ public AltosEepromIterable(LinkedList<AltosEeprom> eeproms) {
+ this.eeproms = eeproms;
+ }
- /* 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 new AltosEepromOrderedIterator(eeproms);
}
-}
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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) {
+ }
+ }
+}
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];
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;
- }
+ double voltage(AltosState state, int sensor) {
+ double supply;
- tick = chunk.data16(start+2);
+ if (state.log_format == AltosLib.AO_LOG_FORMAT_EASYMINI)
+ supply = 3.0;
+ else
+ supply = 3.3;
+ return sensor / 32767.0 * supply * 127/27;
+ }
- data8 = new int[data_length];
- for (int i = 0; i < data_length; i++)
- data8[i] = chunk.data(start + header_length + i);
+ public void update_state(AltosState state) {
+ switch (cmd) {
+ case AltosLib.AO_LOG_FLIGHT:
+ state.set_flight(flight());
+ state.set_ground_pressure(ground_pres());
+ break;
+ case AltosLib.AO_LOG_STATE:
+ state.set_state(state());
+ break;
+ case AltosLib.AO_LOG_SENSOR:
+ state.set_ms5607(pres(), temp());
+ state.set_apogee_voltage(voltage(state, sense_a()));
+ state.set_main_voltage(voltage(state, sense_m()));
+ state.set_battery_voltage(voltage(state, v_batt()));
+ break;
+ }
}
- public AltosEepromMini (String line) {
- valid = false;
- tick = 0;
+ public AltosEepromMini (AltosEepromChunk chunk, int start) throws ParseException {
+ parse_chunk(chunk, start, record_length);
+ }
- 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 AltosEepromMini (String line) {
+ parse_string(line, record_length);
}
public AltosEepromMini(int in_cmd, int in_tick) {
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;
+ }
}
import java.util.*;
import java.text.*;
-public class AltosEepromMiniIterable extends AltosRecordIterable {
+public class AltosEepromMiniIterable implements Iterable<AltosEepromMini> {
static final int seen_flight = 1;
static final int seen_sensor = 2;
--- /dev/null
+/*
+ * 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) {
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 AltosEepromTM extends AltosEeprom {
+ public int cmd;
+ public int tick;
+ public int a;
+ public int b;
+ public boolean tick_valid;
+
+ public static final int record_length = 8;
+
+ static double
+ thermometer_to_temperature(double thermo)
+ {
+ return (thermo - 19791.268) / 32728.0 * 1.25 / 0.00247;
+ }
+
+ public void write(PrintStream out) {
+ out.printf("%c %4x %4x %4x\n", cmd, tick, a, b);
+ }
+
+ public void update_state(AltosState state) {
+ AltosGPS gps;
+
+ /* Flush any pending GPS changes */
+ if (state.gps_pending) {
+ switch (cmd) {
+ case AltosLib.AO_LOG_GPS_LAT:
+ case AltosLib.AO_LOG_GPS_LON:
+ case AltosLib.AO_LOG_GPS_ALT:
+ case AltosLib.AO_LOG_GPS_SAT:
+ case AltosLib.AO_LOG_GPS_DATE:
+ break;
+ default:
+ state.set_temp_gps();
+ break;
+ }
+ }
+
+ switch (cmd) {
+ case AltosLib.AO_LOG_FLIGHT:
+ state.set_state(AltosLib.ao_flight_pad);
+ state.set_ground_accel(a);
+ state.set_flight(b);
+ state.set_boost_tick(tick);
+ break;
+ case AltosLib.AO_LOG_SENSOR:
+ state.set_tick(tick);
+ state.set_accel(a);
+ double pressure = AltosConvert.barometer_to_pressure(b);
+ state.set_pressure(pressure);
+ break;
+ case AltosLib.AO_LOG_PRESSURE:
+ state.set_tick(tick);
+ state.set_pressure(AltosConvert.barometer_to_pressure(b));
+ break;
+ case AltosLib.AO_LOG_TEMP_VOLT:
+ state.set_tick(tick);
+ state.set_temperature(thermometer_to_temperature(a));
+ state.set_battery_voltage(AltosConvert.cc_battery_to_voltage(b));
+ break;
+ case AltosLib.AO_LOG_DEPLOY:
+ state.set_tick(tick);
+ state.set_apogee_voltage(AltosConvert.cc_ignitor_to_voltage(a));
+ state.set_main_voltage(AltosConvert.cc_ignitor_to_voltage(b));
+ break;
+ case AltosLib.AO_LOG_STATE:
+ state.set_tick(tick);
+ state.set_state(a);
+ break;
+ case AltosLib.AO_LOG_GPS_TIME:
+ gps = state.make_temp_gps();
+
+ 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;
+ break;
+ case AltosLib.AO_LOG_GPS_LAT:
+ gps = state.make_temp_gps();
+
+ int lat32 = a | (b << 16);
+ gps.lat = (double) lat32 / 1e7;
+ break;
+ case AltosLib.AO_LOG_GPS_LON:
+ gps = state.make_temp_gps();
+
+ int lon32 = a | (b << 16);
+ gps.lon = (double) lon32 / 1e7;
+ break;
+ case AltosLib.AO_LOG_GPS_ALT:
+ gps = state.make_temp_gps();
+ gps.alt = a;
+ break;
+ case AltosLib.AO_LOG_GPS_SAT:
+ gps = state.make_temp_gps();
+ int svid = a;
+ int c_n0 = b >> 8;
+ gps.add_sat(svid, c_n0);
+ break;
+ case AltosLib.AO_LOG_GPS_DATE:
+ gps = state.make_temp_gps();
+ gps.year = (a & 0xff) + 2000;
+ gps.month = a >> 8;
+ gps.day = b & 0xff;
+ 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);
+ }
+
+ public AltosEepromTM (String line) {
+ tick_valid = false;
+ tick = 0;
+ a = 0;
+ b = 0;
+ if (line == null) {
+ cmd = AltosLib.AO_LOG_INVALID;
+ } else {
+ try {
+ String[] tokens = line.split("\\s+");
+
+ if (tokens[0].length() == 1) {
+ if (tokens.length != 4) {
+ cmd = AltosLib.AO_LOG_INVALID;
+ } 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 {
+ cmd = AltosLib.AO_LOG_INVALID;
+ }
+ } catch (NumberFormatException ne) {
+ cmd = AltosLib.AO_LOG_INVALID;
+ }
+ }
+ }
+
+ 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;
+ }
+
+ static public LinkedList<AltosEeprom> read(FileInputStream input) {
+ LinkedList<AltosEeprom> tms = new LinkedList<AltosEeprom>();
+
+ for (;;) {
+ try {
+ String line = AltosLib.gets(input);
+ if (line == null)
+ break;
+ AltosEepromTM tm = new AltosEepromTM(line);
+ tms.add(tm);
+ } catch (IOException ie) {
+ break;
+ }
+ }
+
+ return tms;
+ }
+
+}
public class AltosFile extends File {
+ static String number(int n) {
+ if (n == AltosRecord.MISSING)
+ return "unk";
+ else
+ return String.format("%03d", n);
+ }
+
public AltosFile(int year, int month, int day, int serial, int flight, String extension) {
super (AltosPreferences.logdir(),
- String.format("%04d-%02d-%02d-serial-%03d-flight-%03d.%s",
- year, month, day, serial, flight, extension));
+ String.format("%04d-%02d-%02d-serial-%s-flight-%s.%s",
+ year, month, day, number(serial), number(flight), extension));
}
public AltosFile(int serial, int flight, String extension) {
extension);
}
- public AltosFile(AltosRecord telem) {
- this(telem.serial, telem.flight, "telem");
+ public AltosFile(AltosState state) {
+ this(state.serial, state.flight, "telem");
}
}
public void init() { }
- public AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; }
+ public AltosState read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; }
public void close(boolean interrupted) { }
import java.text.*;
-public class AltosGPS {
+public class AltosGPS implements Cloneable {
public final static int MISSING = AltosRecord.MISSING;
public boolean connected;
public double lat; /* degrees (+N -S) */
public double lon; /* degrees (+E -W) */
- public int alt; /* m */
+ public double alt; /* m */
public int year;
public int month;
public int day;
}
public AltosGPS(AltosTelemetryMap map) throws ParseException {
- String state = map.get_string(AltosTelemetry.AO_TELEM_GPS_STATE,
- AltosTelemetry.AO_TELEM_GPS_STATE_ERROR);
+ String state = map.get_string(AltosTelemetryLegacy.AO_TELEM_GPS_STATE,
+ AltosTelemetryLegacy.AO_TELEM_GPS_STATE_ERROR);
- nsat = map.get_int(AltosTelemetry.AO_TELEM_GPS_NUM_SAT, 0);
- if (state.equals(AltosTelemetry.AO_TELEM_GPS_STATE_LOCKED)) {
+ nsat = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_NUM_SAT, 0);
+ if (state.equals(AltosTelemetryLegacy.AO_TELEM_GPS_STATE_LOCKED)) {
connected = true;
locked = true;
- lat = map.get_double(AltosTelemetry.AO_TELEM_GPS_LATITUDE, MISSING, 1.0e-7);
- lon = map.get_double(AltosTelemetry.AO_TELEM_GPS_LONGITUDE, MISSING, 1.0e-7);
- alt = map.get_int(AltosTelemetry.AO_TELEM_GPS_ALTITUDE, MISSING);
- year = map.get_int(AltosTelemetry.AO_TELEM_GPS_YEAR, MISSING);
+ lat = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_LATITUDE, MISSING, 1.0e-7);
+ lon = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_LONGITUDE, MISSING, 1.0e-7);
+ alt = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_ALTITUDE, MISSING);
+ year = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_YEAR, MISSING);
if (year != MISSING)
year += 2000;
- month = map.get_int(AltosTelemetry.AO_TELEM_GPS_MONTH, MISSING);
- day = map.get_int(AltosTelemetry.AO_TELEM_GPS_DAY, MISSING);
+ month = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_MONTH, MISSING);
+ day = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_DAY, MISSING);
- hour = map.get_int(AltosTelemetry.AO_TELEM_GPS_HOUR, 0);
- minute = map.get_int(AltosTelemetry.AO_TELEM_GPS_MINUTE, 0);
- second = map.get_int(AltosTelemetry.AO_TELEM_GPS_SECOND, 0);
+ hour = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_HOUR, 0);
+ minute = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_MINUTE, 0);
+ second = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_SECOND, 0);
- ground_speed = map.get_double(AltosTelemetry.AO_TELEM_GPS_HORIZONTAL_SPEED,
+ ground_speed = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_HORIZONTAL_SPEED,
AltosRecord.MISSING, 1/100.0);
- course = map.get_int(AltosTelemetry.AO_TELEM_GPS_COURSE,
+ course = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_COURSE,
AltosRecord.MISSING);
- hdop = map.get_double(AltosTelemetry.AO_TELEM_GPS_HDOP, MISSING, 1.0);
- vdop = map.get_double(AltosTelemetry.AO_TELEM_GPS_VDOP, MISSING, 1.0);
- h_error = map.get_int(AltosTelemetry.AO_TELEM_GPS_HERROR, MISSING);
- v_error = map.get_int(AltosTelemetry.AO_TELEM_GPS_VERROR, MISSING);
- } else if (state.equals(AltosTelemetry.AO_TELEM_GPS_STATE_UNLOCKED)) {
+ hdop = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_HDOP, MISSING, 1.0);
+ vdop = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_VDOP, MISSING, 1.0);
+ h_error = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_HERROR, MISSING);
+ v_error = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_VERROR, MISSING);
+ } else if (state.equals(AltosTelemetryLegacy.AO_TELEM_GPS_STATE_UNLOCKED)) {
connected = true;
locked = false;
} else {
cc_gps_sat = null;
}
+ public AltosGPS clone() {
+ AltosGPS g = new AltosGPS();
+
+ g.nsat = nsat;
+ g.locked = locked;
+ g.connected = connected;
+ g.lat = lat; /* 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(cc_gps_sat[i].svid,
+ cc_gps_sat[i].c_n0);
+ }
+ }
+ return g;
+ }
+
public AltosGPS(AltosGPS old) {
if (old != null) {
nsat = old.nsat;
import java.lang.Math;
-public class AltosGreatCircle {
+public class AltosGreatCircle implements Cloneable {
public double distance;
public double bearing;
public double range;
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);
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;
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
public static final int AO_LOG_FORMAT_TELEMETRY = 3;
public static final int AO_LOG_FORMAT_TELESCIENCE = 4;
public static final int AO_LOG_FORMAT_TELEMEGA = 5;
- public static final int AO_LOG_FORMAT_MINI = 6;
+ public static final int AO_LOG_FORMAT_EASYMINI = 6;
+ public static final int AO_LOG_FORMAT_TELEMETRUM = 7;
+ public static final int AO_LOG_FORMAT_TELEMINI = 8;
public static final int AO_LOG_FORMAT_NONE = 127;
public static boolean isspace(int c) {
return file;
}
- boolean open (AltosRecord telem) throws IOException {
- AltosFile a = new AltosFile(telem);
+ boolean open (AltosState state) throws IOException {
+ AltosFile a = new AltosFile(state);
log_file = new FileWriter(a, true);
if (log_file != null) {
public void run () {
try {
- AltosRecord previous = null;
+ AltosState state = null;
for (;;) {
AltosLine line = input_queue.take();
if (line.line == null)
continue;
try {
- AltosRecord telem = AltosTelemetry.parse(line.line, previous);
- if ((telem.seen & AltosRecord.seen_flight) != 0 &&
- (telem.serial != serial || telem.flight != flight || log_file == null))
+ AltosTelemetry telem = new AltosTelemetryLegacy(line.line);
+ if (state != null)
+ state = state.clone();
+ else
+ state = new AltosState();
+ telem.update_state(state);
+ if (state.serial != serial || state.flight != flight || log_file == null)
{
close_log_file();
- serial = telem.serial;
- flight = telem.flight;
- open(telem);
+ serial = state.serial;
+ flight = state.flight;
+ open(state);
}
- previous = telem;
} catch (ParseException pe) {
} catch (AltosCRCException ce) {
}
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
--- /dev/null
+/*
+ * 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;
+ }
+}
public int flight_log_max;
public String firmware_version;
+ public double accel_plus_g, accel_minus_g;
+ public double ground_accel;
+ public double accel;
+
public AltosRecordCompanion companion;
/* Telemetry sources have these values recorded from the flight computer */
kalman_acceleration = MISSING;
kalman_speed = MISSING;
kalman_height = MISSING;
+
+ accel_plus_g = MISSING;
+ accel_minus_g = MISSING;
+
}
}
public int flight_accel;
public int flight_vel;
+ public int flight_height;
+
public int flight_pres;
static double adc(int raw) {
flight_accel = old.flight_accel;
flight_vel = old.flight_vel;
+ flight_height = old.flight_height;
flight_pres = old.flight_pres;
}
flight_accel = 0;
flight_vel = 0;
+ flight_height = 0;
flight_pres = 0;
}
--- /dev/null
+/*
+ * 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();
+ }
+}
*/
public class AltosReplayReader extends AltosFlightReader {
- Iterator<AltosRecord> iterator;
+ Iterator<AltosState> iterator;
File file;
- public AltosRecord read() {
+ public AltosState read() {
if (iterator.hasNext())
return iterator.next();
return null;
public File backing_file() { return file; }
- public AltosReplayReader(Iterator<AltosRecord> in_iterator, File in_file) {
+ public AltosReplayReader(Iterator<AltosState> in_iterator, File in_file) {
iterator = in_iterator;
file = in_file;
name = file.getName();
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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++;
+ }
+ }
+}
+
package org.altusmetrum.altoslib_1;
-public class AltosState {
- public AltosRecord data;
+public class AltosState implements Cloneable {
+ public AltosRecord record;
+
+ public static final int set_position = 1;
+ public static final int set_gps = 2;
+ public static final int set_data = 4;
+
+ public int set;
/* derived data */
public long report_time;
public double time;
+ public double prev_time;
public double time_change;
public int tick;
+ public int boost_tick;
public int state;
+ public int flight;
+ public int serial;
public boolean landed;
public boolean ascent; /* going up? */
- public boolean boost; /* under power */
+ public boolean boost; /* under power */
+ public int rssi;
+ public int status;
public double ground_altitude;
+ public double ground_pressure;
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 AltosGPS temp_gps;
+ public boolean gps_pending;
public int gps_sequence;
public AltosIMU imu;
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 */
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 double ground_accel_avg;
+
+ public int log_format;
+
+ public AltosMs5607 baro;
+
+ public AltosRecordCompanion companion;
+
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 init (AltosRecord cur, AltosState prev_state) {
- data = cur;
+ 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;
+ }
- /* Discard previous state if it was for a different board */
- if (prev_state != null && prev_state.data.serial != data.serial)
- prev_state = null;
- ground_altitude = data.ground_altitude();
+ public void init() {
+ record = null;
- altitude = data.altitude();
- if (altitude == AltosRecord.MISSING && data.gps != null)
- altitude = data.gps.alt;
+ set = 0;
+ report_time = System.currentTimeMillis();
+ time = AltosRecord.MISSING;
+ time_change = AltosRecord.MISSING;
+ prev_time = AltosRecord.MISSING;
+ tick = AltosRecord.MISSING;
+ boost_tick = AltosRecord.MISSING;
+ state = AltosLib.ao_flight_invalid;
+ flight = AltosRecord.MISSING;
+ landed = false;
+ boost = false;
+ rssi = AltosRecord.MISSING;
+ status = 0;
+
+ ground_altitude = AltosRecord.MISSING;
+ ground_pressure = AltosRecord.MISSING;
+ altitude = AltosRecord.MISSING;
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;
- }
+ 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;
+
+ speed = AltosRecord.MISSING;
+
+ kalman_height = AltosRecord.MISSING;
+ kalman_speed = AltosRecord.MISSING;
+ kalman_acceleration = AltosRecord.MISSING;
+
+ max_speed = 0;
+ max_height = 0;
+ max_acceleration = 0;
+
+ gps = null;
+ temp_gps = null;
+ gps_sequence = 0;
+ gps_pending = false;
+
+ imu = null;
+ mag = null;
+
+ set_npad(0);
+ ngps = 0;
+
+ from_pad = null;
+ elevation = AltosRecord.MISSING;
+ range = AltosRecord.MISSING;
+ gps_height = AltosRecord.MISSING;
+
+ pad_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;
+ accel = AltosRecord.MISSING;
+ ground_accel = AltosRecord.MISSING;
+ ground_accel_avg = AltosRecord.MISSING;
+ log_format = AltosRecord.MISSING;
+ serial = AltosRecord.MISSING;
+
+ baro = null;
+ companion = null;
+ }
+
+ void copy(AltosState old) {
+
+ record = null;
+
+ if (old == null) {
+ init();
+ return;
}
- report_time = System.currentTimeMillis();
+ report_time = old.report_time;
+ time = old.time;
+ time_change = 0;
+ tick = old.tick;
+ boost_tick = old.boost_tick;
+
+ state = old.state;
+ flight = old.flight;
+ landed = old.landed;
+ ascent = old.ascent;
+ boost = old.boost;
+ rssi = old.rssi;
+ status = old.status;
+
+ set = 0;
+
+ 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;
+ speed = old.speed;
+
+ prev_height = old.height;
+ prev_speed = old.speed;
+ prev_acceleration = old.acceleration;
+ prev_time = old.time;
+
+ max_height = old.max_height;
+ max_acceleration = old.max_acceleration;
+ max_speed = old.max_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;
+ if (old.temp_gps != null)
+ temp_gps = old.temp_gps.clone();
+ else
+ temp_gps = null;
+ gps_sequence = old.gps_sequence;
+ gps_pending = old.gps_pending;
- if (data.kalman_acceleration != AltosRecord.MISSING)
- acceleration = data.kalman_acceleration;
+ if (old.imu != null)
+ imu = old.imu.clone();
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;
-
- if (prev_state != null) {
-
- /* 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;
-
- if (data.kalman_speed != AltosRecord.MISSING) {
- baro_speed = accel_speed = data.kalman_speed;
- } else {
- /* compute barometric speed */
-
- double height_change = height - prev_state.height;
-
- double prev_baro_speed = prev_state.baro_speed;
- if (prev_baro_speed == AltosRecord.MISSING)
- prev_baro_speed = 0;
-
- if (time_change > 0)
- baro_speed = (prev_baro_speed * 3 + (height_change / time_change)) / 4.0;
- else
- baro_speed = prev_state.baro_speed;
+ imu = null;
- double prev_accel_speed = prev_state.accel_speed;
+ if (old.mag != null)
+ mag = old.mag.clone();
+ else
+ mag = null;
- if (prev_accel_speed == AltosRecord.MISSING)
- prev_accel_speed = 0;
+ npad = old.npad;
+ gps_waiting = old.gps_waiting;
+ gps_ready = old.gps_ready;
+ ngps = old.ngps;
- if (acceleration == AltosRecord.MISSING) {
- /* Fill in mising acceleration value */
- accel_speed = baro_speed;
+ if (old.from_pad != null)
+ from_pad = old.from_pad.clone();
+ else
+ from_pad = null;
- if (time_change > 0 && accel_speed != AltosRecord.MISSING)
- acceleration = (accel_speed - prev_accel_speed) / time_change;
- else
- acceleration = prev_state.acceleration;
- } else {
- /* compute accelerometer speed */
- accel_speed = prev_accel_speed + acceleration * time_change;
- }
- }
- } else {
- npad = 0;
- ngps = 0;
- gps = null;
- gps_sequence = 0;
- baro_speed = AltosRecord.MISSING;
- accel_speed = AltosRecord.MISSING;
- pad_alt = AltosRecord.MISSING;
- max_baro_speed = 0;
- max_accel_speed = 0;
- max_height = 0;
- max_acceleration = 0;
- time_change = 0;
- }
+ elevation = old.elevation;
+ range = old.range;
- time = tick / 100.0;
+ gps_height = old.gps_height;
+ pad_lat = old.pad_lat;
+ pad_lon = old.pad_lon;
+ pad_alt = old.pad_alt;
- if (data.gps != null && data.gps_sequence != gps_sequence && (state < AltosLib.ao_flight_boost)) {
+ speak_tick = old.speak_tick;
+ speak_altitude = old.speak_altitude;
- /* Track consecutive 'good' gps reports, waiting for 10 of them */
- if (data.gps != null && data.gps.locked && data.gps.nsat >= 4)
- npad++;
+ callsign = old.callsign;
+
+ accel_plus_g = old.accel_plus_g;
+ accel_minus_g = old.accel_minus_g;
+ accel = old.accel;
+ ground_accel = old.ground_accel;
+ ground_accel_avg = old.ground_accel_avg;
+
+ log_format = old.log_format;
+ serial = old.serial;
+
+ baro = old.baro;
+ companion = old.companion;
+ }
+
+ 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 && alt != AltosRecord.MISSING && ground_pressure == AltosRecord.MISSING) {
+ if (ground_altitude == AltosRecord.MISSING)
+ ground_altitude = alt;
else
- npad = 0;
-
- /* Average GPS data while on the pad */
- if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) {
- if (ngps > 1 && state == AltosLib.ao_flight_pad) {
- /* filter pad position */
- pad_lat = (pad_lat * 31.0 + data.gps.lat) / 32.0;
- pad_lon = (pad_lon * 31.0 + data.gps.lon) / 32.0;
- pad_alt = (pad_alt * 31.0 + data.gps.alt) / 32.0;
- } else {
- pad_lat = data.gps.lat;
- pad_lon = data.gps.lon;
- pad_alt = data.gps.alt;
- }
- ngps++;
- }
- } else {
- if (ngps == 0 && ground_altitude != AltosRecord.MISSING)
- pad_alt = ground_altitude;
+ ground_altitude = (ground_altitude * 7 + alt) / 8;
}
- gps_sequence = data.gps_sequence;
+ 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;
- gps_waiting = MIN_PAD_SAMPLES - npad;
- if (gps_waiting < 0)
- gps_waiting = 0;
+ if (height != AltosRecord.MISSING && height > max_height)
+ max_height = height;
- gps_ready = gps_waiting == 0;
+ update_speed();
+ }
+
+ double motion_filter_value() {
+ return 1/ Math.exp(time_change/2.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;
- ascent = (AltosLib.ao_flight_boost <= state &&
- state <= AltosLib.ao_flight_coast);
- boost = (AltosLib.ao_flight_boost == state);
+ if (prev_acceleration == AltosRecord.MISSING)
+ acceleration = new_acceleration;
+ else {
+ double filter = motion_filter_value();
+
+ acceleration = prev_acceleration * filter + new_acceleration * (1-filter);
+ }
+ }
+ }
+ if (boost && speed != AltosRecord.MISSING && speed > max_speed)
+ max_speed = speed;
+ }
+
+ void update_accel() {
+ double ground = ground_accel;
+
+ if (ground == AltosRecord.MISSING)
+ ground = ground_accel_avg;
+ if (accel == AltosRecord.MISSING)
+ return;
+ if (ground == 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) / counts_per_mss;
/* Only look at accelerometer data under boost */
if (boost && acceleration != AltosRecord.MISSING && (max_acceleration == AltosRecord.MISSING || acceleration > max_acceleration))
max_acceleration = acceleration;
- if (boost && accel_speed != AltosRecord.MISSING && accel_speed > max_accel_speed)
- max_accel_speed = accel_speed;
- if (boost && baro_speed != AltosRecord.MISSING && baro_speed > max_baro_speed)
- max_baro_speed = baro_speed;
+ update_speed();
+ }
- if (height != AltosRecord.MISSING && height > max_height)
- max_height = height;
+ void update_time() {
+ if (tick != AltosRecord.MISSING) {
+ time = tick / 100.0;
+ if (prev_time != AltosRecord.MISSING)
+ time_change = time - prev_time;
+ }
+ }
+
+ void update_gps() {
elevation = 0;
range = -1;
gps_height = 0;
- if (data.gps != null) {
- gps = data.gps;
- if (ngps > 0 && gps.locked) {
- double h = height;
-
- if (h == AltosRecord.MISSING) h = 0;
- from_pad = new AltosGreatCircle(pad_lat, pad_lon, 0, gps.lat, gps.lon, h);
- elevation = from_pad.elevation;
- range = from_pad.range;
- gps_height = gps.alt - pad_alt;
+
+ if (gps == null)
+ return;
+
+ if (gps.locked && gps.nsat >= 4) {
+ /* Track consecutive 'good' gps reports, waiting for 10 of them */
+ if (state == AltosLib.ao_flight_pad) {
+ set_npad(npad+1);
+ if (pad_lat != AltosRecord.MISSING) {
+ pad_lat = (pad_lat * 31 + gps.lat) / 32;
+ pad_lon = (pad_lon * 31 + gps.lon) / 32;
+ pad_alt = (pad_alt * 31 + gps.alt) / 32;
+ }
+ }
+ if (pad_lat == AltosRecord.MISSING) {
+ pad_lat = gps.lat;
+ pad_lon = gps.lon;
+ pad_alt = gps.alt;
+ }
+ }
+ if (gps.lat != 0 && gps.lon != 0 &&
+ pad_lat != AltosRecord.MISSING &&
+ pad_lon != AltosRecord.MISSING)
+ {
+ double h = height;
+
+ if (h == AltosRecord.MISSING)
+ h = 0;
+ from_pad = new AltosGreatCircle(pad_lat, pad_lon, 0, gps.lat, gps.lon, h);
+ elevation = from_pad.elevation;
+ range = from_pad.range;
+ gps_height = gps.alt - pad_alt;
+ }
+ }
+
+ 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_boost_tick(int boost_tick) {
+ if (boost_tick != AltosRecord.MISSING)
+ this.boost_tick = boost_tick;
+ }
+
+ public String state_name() {
+ return AltosLib.state_name(state);
+ }
+
+ 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_flight(int flight) {
+
+ /* When the flight changes, reset the state */
+ if (flight != AltosRecord.MISSING) {
+ if (this.flight != AltosRecord.MISSING &&
+ this.flight != flight) {
+ init();
+ }
+ this.flight = flight;
+ }
+ }
+
+ public void set_serial(int serial) {
+ /* When the serial changes, reset the state */
+ if (serial != AltosRecord.MISSING) {
+ if (this.serial != AltosRecord.MISSING &&
+ this.serial != serial) {
+ init();
+ }
+ this.serial = serial;
+ }
+ }
+
+ public int rssi() {
+ if (rssi == AltosRecord.MISSING)
+ return 0;
+ return rssi;
+ }
+
+ public void set_rssi(int rssi, int status) {
+ if (rssi != AltosRecord.MISSING) {
+ this.rssi = rssi;
+ this.status = status;
+ }
+ }
+
+ public void set_altitude(double altitude) {
+ if (altitude != AltosRecord.MISSING) {
+ this.altitude = altitude;
+ update_vertical_pos();
+ set |= set_position;
+ }
+ }
+
+ public void set_ground_altitude(double ground_altitude) {
+ if (ground_altitude != AltosRecord.MISSING) {
+ this.ground_altitude = ground_altitude;
+ update_vertical_pos();
+ }
+ }
+
+ public void set_ground_pressure (double pressure) {
+ if (pressure != AltosRecord.MISSING) {
+ this.ground_pressure = pressure;
+ set_ground_altitude(AltosConvert.pressure_to_altitude(pressure));
+ update_vertical_pos();
+ }
+ }
+
+ public void set_gps(AltosGPS gps, int sequence) {
+ if (gps != null) {
+ this.gps = gps.clone();
+ gps_sequence = sequence;
+ update_gps();
+ update_vertical_pos();
+ set |= set_gps;
+ }
+ }
+
+ public void set_kalman(double height, double speed, double acceleration) {
+ if (height != AltosRecord.MISSING) {
+ kalman_height = height;
+ kalman_speed = speed;
+ kalman_acceleration = acceleration;
+ 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 make_baro() {
+ if (baro == null)
+ baro = new AltosMs5607();
+ }
+
+ public void set_ms5607(int pres, int temp) {
+ if (baro != null) {
+ baro.set(pres, temp);
+
+ set_pressure(baro.pa);
+ set_temperature(baro.cc / 100.0);
+ }
+ }
+
+ public void make_companion (int nchannels) {
+ if (companion == null)
+ companion = new AltosRecordCompanion(nchannels);
+ }
+
+ public void set_companion(AltosRecordCompanion companion) {
+ this.companion = companion;
+ }
+
+ 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;
+ if (state == AltosLib.ao_flight_pad) {
+ if (ground_accel_avg == AltosRecord.MISSING)
+ ground_accel_avg = accel;
+ else
+ ground_accel_avg = (ground_accel_avg * 7 + accel) / 8;
}
}
+ update_accel();
+ }
+
+ public void set_temperature(double temperature) {
+ if (temperature != AltosRecord.MISSING) {
+ this.temperature = temperature;
+ set |= set_data;
+ }
+ }
+
+ public void set_battery_voltage(double battery_voltage) {
+ if (battery_voltage != AltosRecord.MISSING) {
+ this.battery_voltage = battery_voltage;
+ set |= set_data;
+ }
+ }
+
+ public void set_pyro_voltage(double pyro_voltage) {
+ if (pyro_voltage != AltosRecord.MISSING) {
+ this.pyro_voltage = pyro_voltage;
+ set |= set_data;
+ }
+ }
+
+ public void set_apogee_voltage(double apogee_voltage) {
+ if (apogee_voltage != AltosRecord.MISSING) {
+ this.apogee_voltage = apogee_voltage;
+ set |= set_data;
+ }
+ }
+
+ public void set_main_voltage(double main_voltage) {
+ if (main_voltage != AltosRecord.MISSING) {
+ this.main_voltage = main_voltage;
+ set |= set_data;
+ }
+ }
+
+
+ public double time_since_boost() {
+ if (tick == AltosRecord.MISSING)
+ return 0.0;
+
+ if (boost_tick != AltosRecord.MISSING) {
+ return (tick - boost_tick) / 100.0;
+ }
+ return tick / 100.0;
+ }
+
+ public boolean valid() {
+ return tick != AltosRecord.MISSING && serial != AltosRecord.MISSING;
+ }
+
+ public AltosGPS make_temp_gps() {
+ if (temp_gps == null) {
+ temp_gps = new AltosGPS(gps);
+ temp_gps.cc_gps_sat = null;
+ }
+ gps_pending = true;
+ return temp_gps;
+ }
+
+ public void set_temp_gps() {
+ set_gps(temp_gps, gps_sequence + 1);
+ gps_pending = false;
+ temp_gps = null;
+ }
+
+ public void init (AltosRecord cur, AltosState prev_state) {
+
+ System.out.printf ("init\n");
+ if (cur == null)
+ cur = new AltosRecord();
+
+ record = cur;
+
+ /* Discard previous state if it was for a different board */
+ if (prev_state != null && prev_state.serial != cur.serial)
+ prev_state = null;
+
+ copy(prev_state);
+
+ set_ground_altitude(cur.ground_altitude());
+ set_altitude(cur.altitude());
+
+ set_kalman(cur.kalman_height, cur.kalman_speed, cur.kalman_acceleration);
+
+ report_time = System.currentTimeMillis();
+
+ set_temperature(cur.temperature());
+ set_apogee_voltage(cur.drogue_voltage());
+ set_main_voltage(cur.main_voltage());
+ set_battery_voltage(cur.battery_voltage());
+
+ set_pressure(cur.pressure());
+
+ set_tick(cur.tick);
+ set_state(cur.state);
+
+ set_accel_g (cur.accel_minus_g, cur.accel_plus_g);
+ set_ground_accel(cur.ground_accel);
+ set_accel (cur.accel);
+
+ if (cur.gps_sequence != gps_sequence)
+ set_gps(cur.gps, cur.gps_sequence);
+
+ }
+
+ public AltosState clone() {
+ AltosState s = new AltosState();
+ s.copy(this);
+ return s;
}
public AltosState(AltosRecord cur) {
public AltosState (AltosRecord cur, AltosState prev) {
init(cur, prev);
}
+
+ public AltosState () {
+ init();
+ }
}
--- /dev/null
+/*
+ * 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.*;
+
+public abstract class AltosStateIterable implements Iterable<AltosState> {
+
+ public void write_comments (PrintStream out) {
+ }
+
+ public abstract void write(PrintStream out);
+}
--- /dev/null
+/*
+ * 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
* Telemetry data contents
*/
+public abstract class AltosTelemetry implements AltosStateUpdate {
+
+ /* All telemetry packets have these fields */
+ public int tick;
+ public int serial;
+ public int rssi;
+ public int status;
+
+ /* Mark when we received the packet */
+ long received_time;
+
+ static boolean cksum(int[] bytes) {
+ int sum = 0x5a;
+ for (int i = 1; i < bytes.length - 1; i++)
+ sum += bytes[i];
+ sum &= 0xff;
+ return sum == bytes[bytes.length - 1];
+ }
+
+ public void update_state(AltosState state) {
+ }
+ final static int PKT_APPEND_STATUS_1_CRC_OK = (1 << 7);
+ final static int PKT_APPEND_STATUS_1_LQI_MASK = (0x7f);
+ final static int PKT_APPEND_STATUS_1_LQI_SHIFT = 0;
+
+ final static int packet_type_TM_sensor = 0x01;
+ final static int packet_type_Tm_sensor = 0x02;
+ final static int packet_type_Tn_sensor = 0x03;
+ final static int packet_type_configuration = 0x04;
+ final static int packet_type_location = 0x05;
+ final static int packet_type_satellite = 0x06;
+ 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 AltosTelemetry parse_hex(String hex) throws ParseException, AltosCRCException {
+ AltosTelemetry telem = null;
+
+ int[] bytes;
+ try {
+ bytes = AltosLib.hexbytes(hex);
+ } catch (NumberFormatException ne) {
+ throw new ParseException(ne.getMessage(), 0);
+ }
+
+ /* one for length, one for checksum */
+ if (bytes[0] != bytes.length - 2)
+ throw new ParseException(String.format("invalid length %d != %d\n",
+ bytes[0],
+ bytes.length - 2), 0);
+ if (!cksum(bytes))
+ throw new ParseException(String.format("invalid line \"%s\"", hex), 0);
+
+ int rssi = AltosLib.int8(bytes, bytes.length - 3) / 2 - 74;
+ int status = AltosLib.uint8(bytes, bytes.length - 2);
+
+ if ((status & PKT_APPEND_STATUS_1_CRC_OK) == 0)
+ throw new AltosCRCException(rssi);
+
+ /* length, data ..., rssi, status, checksum -- 4 bytes extra */
+ switch (bytes.length) {
+ case AltosLib.ao_telemetry_standard_len + 4:
+ int type = AltosLib.uint8(bytes, 4 + 1);
/*
- * The packet format is a simple hex dump of the raw telemetry frame.
- * It starts with 'TELEM', then contains hex digits with a checksum as the last
- * byte on the line.
- *
- * Version 4 is a replacement with consistent syntax. Each telemetry line
- * contains a sequence of space-separated names and values, the values are
- * either integers or strings. The names are all unique. All values are
- * optional
- *
- * VERSION 4 c KD7SQG n 236 f 18 r -25 s pad t 513 r_a 15756 r_b 26444 r_t 20944
- * r_v 26640 r_d 512 r_m 208 c_a 15775 c_b 26439 c_p 15749 c_m 16281 a_a 15764
- * a_s 0 a_b 26439 g_s u g_n 0 s_n 0
- *
- * VERSION 4 c KD7SQG n 19 f 0 r -23 s pad t 513 r_b 26372 r_t 21292 r_v 26788
- * r_d 136 r_m 140 c_b 26370 k_h 0 k_s 0 k_a 0
- *
- * General header fields
- *
- * Name Value
- *
- * VERSION Telemetry version number (4 or more). Must be first.
- * c Callsign (string, no spaces allowed)
- * n Flight unit serial number (integer)
- * f Flight number (integer)
- * r Packet RSSI value (integer)
- * s Flight computer state (string, no spaces allowed)
- * t Flight computer clock (integer in centiseconds)
- *
- * Version 3 is Version 2 with fixed RSSI numbers -- the radio reports
- * in 1/2dB increments while this protocol provides only integers. So,
- * the syntax didn't change just the interpretation of the RSSI
- * values.
- *
- * Version 2 of the telemetry data stream is a bit of a mess, with no
- * consistent formatting. In particular, the GPS data is formatted for
- * viewing instead of parsing. However, the key feature is that every
- * telemetry line contains all of the information necessary to
- * describe the current rocket state, including the calibration values
- * for accelerometer and barometer.
- *
- * GPS unlocked:
- *
- * VERSION 2 CALL KB0G SERIAL 51 FLIGHT 2 RSSI -68 STATUS ff STATE pad 1001 \
- * a: 16032 p: 21232 t: 20284 v: 25160 d: 204 m: 204 fa: 16038 ga: 16032 fv: 0 \
- * fp: 21232 gp: 21230 a+: 16049 a-: 16304 GPS 0 sat unlocked SAT 1 15 30
- *
- * GPS locked:
- *
- * VERSION 2 CALL KB0G SERIAL 51 FLIGHT 2 RSSI -71 STATUS ff STATE pad 2504 \
- * a: 16028 p: 21220 t: 20360 v: 25004 d: 208 m: 200 fa: 16031 ga: 16032 fv: 330 \
- * fp: 21231 gp: 21230 a+: 16049 a-: 16304 \
- * GPS 9 sat 2010-02-13 17:16:51 35°20.0803'N 106°45.2235'W 1790m \
- * 0.00m/s(H) 0° 0.00m/s(V) 1.0(hdop) 0(herr) 0(verr) \
- * SAT 10 29 30 24 28 5 25 21 20 15 33 1 23 30 24 18 26 10 29 2 26
- *
- */
+ switch (type) {
+ case packet_type_TM_sensor:
+ case packet_type_Tm_sensor:
+ case packet_type_Tn_sensor:
+ telem = new AltosTelemetrySensor(bytes);
+ break;
+ case packet_type_configuration:
+ telem = new AltosTelemetryConfiguration(bytes);
+ break;
+ case packet_type_location:
+ telem = new AltosTelemetryLocation(bytes);
+ break;
+ case packet_type_satellite:
+ telem = new AltosTelemetrySatellite(bytes);
+ break;
+ case packet_type_companion:
+ telem = new AltosTelemetryCompanion(bytes);
+ break;
+ case packet_type_MM_sensor:
+ telem = new AltosTelemetryMegaSensor(bytes);
+ break;
+ case packet_type_MM_data:
+ telem = new AltosTelemetryMegaData(bytes);
+ break;
+ default:
+ telem = new AltosTelemetryRaw(bytes);
+ break;
+ }
+*/
+ break;
+ case AltosLib.ao_telemetry_0_9_len + 4:
+ telem = new AltosTelemetryLegacy(bytes);
+ break;
+ case AltosLib.ao_telemetry_0_8_len + 4:
+ telem = new AltosTelemetryLegacy(bytes);
+ break;
+ default:
+ throw new ParseException(String.format("Invalid packet length %d", bytes.length), 0);
+ }
+ if (telem != null) {
+ telem.received_time = System.currentTimeMillis();
+ telem.rssi = rssi;
+ telem.status = status;
+ }
+ return telem;
+ }
+
+ public static AltosTelemetry parse(String line) throws ParseException, AltosCRCException {
+ String[] word = line.split("\\s+");
+ int i =0;
+
+ if (word[i].equals("CRC") && word[i+1].equals("INVALID")) {
+ i += 2;
+ AltosParse.word(word[i++], "RSSI");
+ throw new AltosCRCException(AltosParse.parse_int(word[i++]));
+ }
+
+ AltosTelemetry telem;
-public abstract class AltosTelemetry extends AltosRecord {
-
- /*
- * General header fields
- *
- * Name Value
- *
- * VERSION Telemetry version number (4 or more). Must be first.
- * c Callsign (string, no spaces allowed)
- * n Flight unit serial number (integer)
- * f Flight number (integer)
- * r Packet RSSI value (integer)
- * s Flight computer state (string, no spaces allowed)
- * t Flight computer clock (integer in centiseconds)
- */
-
- final static String AO_TELEM_VERSION = "VERSION";
- final static String AO_TELEM_CALL = "c";
- final static String AO_TELEM_SERIAL = "n";
- final static String AO_TELEM_FLIGHT = "f";
- final static String AO_TELEM_RSSI = "r";
- final static String AO_TELEM_STATE = "s";
- final static String AO_TELEM_TICK = "t";
-
- /*
- * Raw sensor values
- *
- * Name Value
- * r_a Accelerometer reading (integer)
- * r_b Barometer reading (integer)
- * r_t Thermometer reading (integer)
- * r_v Battery reading (integer)
- * r_d Drogue continuity (integer)
- * r_m Main continuity (integer)
- */
-
- final static String AO_TELEM_RAW_ACCEL = "r_a";
- final static String AO_TELEM_RAW_BARO = "r_b";
- final static String AO_TELEM_RAW_THERMO = "r_t";
- final static String AO_TELEM_RAW_BATT = "r_v";
- final static String AO_TELEM_RAW_DROGUE = "r_d";
- final static String AO_TELEM_RAW_MAIN = "r_m";
-
- /*
- * Sensor calibration values
- *
- * Name Value
- * c_a Ground accelerometer reading (integer)
- * c_b Ground barometer reading (integer)
- * c_p Accelerometer reading for +1g
- * c_m Accelerometer reading for -1g
- */
-
- final static String AO_TELEM_CAL_ACCEL_GROUND = "c_a";
- final static String AO_TELEM_CAL_BARO_GROUND = "c_b";
- final static String AO_TELEM_CAL_ACCEL_PLUS = "c_p";
- final static String AO_TELEM_CAL_ACCEL_MINUS = "c_m";
-
- /*
- * Kalman state values
- *
- * Name Value
- * k_h Height above pad (integer, meters)
- * k_s Vertical speeed (integer, m/s * 16)
- * k_a Vertical acceleration (integer, m/s² * 16)
- */
-
- final static String AO_TELEM_KALMAN_HEIGHT = "k_h";
- final static String AO_TELEM_KALMAN_SPEED = "k_s";
- final static String AO_TELEM_KALMAN_ACCEL = "k_a";
-
- /*
- * Ad-hoc flight values
- *
- * Name Value
- * a_a Acceleration (integer, sensor units)
- * a_s Speed (integer, integrated acceleration value)
- * a_b Barometer reading (integer, sensor units)
- */
-
- final static String AO_TELEM_ADHOC_ACCEL = "a_a";
- final static String AO_TELEM_ADHOC_SPEED = "a_s";
- final static String AO_TELEM_ADHOC_BARO = "a_b";
-
- /*
- * GPS values
- *
- * Name Value
- * g_s GPS state (string):
- * l locked
- * u unlocked
- * e error (missing or broken)
- * g_n Number of sats used in solution
- * g_ns Latitude (degrees * 10e7)
- * g_ew Longitude (degrees * 10e7)
- * g_a Altitude (integer meters)
- * g_Y GPS year (integer)
- * g_M GPS month (integer - 1-12)
- * g_D GPS day (integer - 1-31)
- * g_h GPS hour (integer - 0-23)
- * g_m GPS minute (integer - 0-59)
- * g_s GPS second (integer - 0-59)
- * g_v GPS vertical speed (integer, cm/sec)
- * g_s GPS horizontal speed (integer, cm/sec)
- * g_c GPS course (integer, 0-359)
- * g_hd GPS hdop (integer * 10)
- * g_vd GPS vdop (integer * 10)
- * g_he GPS h error (integer)
- * g_ve GPS v error (integer)
- */
-
- final static String AO_TELEM_GPS_STATE = "g";
- final static String AO_TELEM_GPS_STATE_LOCKED = "l";
- final static String AO_TELEM_GPS_STATE_UNLOCKED = "u";
- final static String AO_TELEM_GPS_STATE_ERROR = "e";
- final static String AO_TELEM_GPS_NUM_SAT = "g_n";
- final static String AO_TELEM_GPS_LATITUDE = "g_ns";
- final static String AO_TELEM_GPS_LONGITUDE = "g_ew";
- final static String AO_TELEM_GPS_ALTITUDE = "g_a";
- final static String AO_TELEM_GPS_YEAR = "g_Y";
- final static String AO_TELEM_GPS_MONTH = "g_M";
- final static String AO_TELEM_GPS_DAY = "g_D";
- final static String AO_TELEM_GPS_HOUR = "g_h";
- final static String AO_TELEM_GPS_MINUTE = "g_m";
- final static String AO_TELEM_GPS_SECOND = "g_s";
- final static String AO_TELEM_GPS_VERTICAL_SPEED = "g_v";
- final static String AO_TELEM_GPS_HORIZONTAL_SPEED = "g_g";
- final static String AO_TELEM_GPS_COURSE = "g_c";
- final static String AO_TELEM_GPS_HDOP = "g_hd";
- final static String AO_TELEM_GPS_VDOP = "g_vd";
- final static String AO_TELEM_GPS_HERROR = "g_he";
- final static String AO_TELEM_GPS_VERROR = "g_ve";
-
- /*
- * GPS satellite values
- *
- * Name Value
- * s_n Number of satellites reported (integer)
- * s_v0 Space vehicle ID (integer) for report 0
- * s_c0 C/N0 number (integer) for report 0
- * s_v1 Space vehicle ID (integer) for report 1
- * s_c1 C/N0 number (integer) for report 1
- * ...
- */
-
- final static String AO_TELEM_SAT_NUM = "s_n";
- final static String AO_TELEM_SAT_SVID = "s_v";
- final static String AO_TELEM_SAT_C_N_0 = "s_c";
-
- static public AltosRecord parse(String line, AltosRecord previous) throws ParseException, AltosCRCException {
- AltosTelemetryRecord r = AltosTelemetryRecord.parse(line);
-
- return r.update_state(previous);
+ if (word[i].equals("TELEM")) {
+ telem = parse_hex(word[i+1]);
+ } else {
+ telem = new AltosTelemetryLegacy(line);
+ }
+ return telem;
}
}
--- /dev/null
+/*
+ * 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.*;
+
+class AltosTelemetryIterator implements Iterator<AltosState> {
+ AltosState state;
+ Iterator<AltosTelemetry> telems;
+ AltosTelemetry next;
+ boolean seen;
+
+ public boolean hasNext() {
+ return !seen || telems.hasNext();
+ }
+
+ public AltosState next() {
+ if (seen) {
+ AltosState n = state.clone();
+ AltosTelemetry t = telems.next();
+
+ t.update_state(n);
+ state = n;
+ }
+ seen = true;
+ return state;
+ }
+
+ public void remove () {
+ }
+
+ public AltosTelemetryIterator(AltosState start, Iterator<AltosTelemetry> telems) {
+ this.state = start;
+ this.telems = telems;
+ this.seen = false;
+ }
+}
+
+public class AltosTelemetryFile extends AltosStateIterable {
+
+ AltosTelemetryIterable telems;
+ AltosState start;
+
+ public void write_comments(PrintStream out) {
+ }
+
+ public void write(PrintStream out) {
+
+ }
+
+ public AltosTelemetryFile(FileInputStream input) {
+ telems = new AltosTelemetryIterable(input);
+ start = new AltosState();
+
+ /* Find boost tick */
+ AltosState state = start.clone();
+
+ for (AltosTelemetry telem : telems) {
+ telem.update_state(state);
+ if (state.state >= AltosLib.ao_flight_boost) {
+ start.set_boost_tick(state.tick);
+ break;
+ }
+ }
+ }
+
+ public Iterator<AltosState> iterator() {
+ AltosState state = start.clone();
+ Iterator<AltosTelemetry> i = telems.iterator();
+
+ while (i.hasNext() && !state.valid()) {
+ AltosTelemetry t = i.next();
+ t.update_state(state);
+ }
+ return new AltosTelemetryIterator(state, i);
+ }
+}
\ No newline at end of file
import java.util.*;
import java.text.*;
-public class AltosTelemetryIterable extends AltosRecordIterable {
- TreeSet<AltosRecord> records;
+public class AltosTelemetryIterable implements Iterable<AltosTelemetry> {
+ LinkedList<AltosTelemetry> telems;
- public Iterator<AltosRecord> iterator () {
- return records.iterator();
+ public Iterator<AltosTelemetry> iterator () {
+ return telems.iterator();
}
- boolean has_gps = false;
- boolean has_accel = false;
- boolean has_ignite = false;
- public boolean has_gps() { return has_gps; }
- public boolean has_accel() { return has_accel; }
- public boolean has_ignite() { return has_ignite; };
-
public AltosTelemetryIterable (FileInputStream input) {
- boolean saw_boost = false;
- int current_tick = 0;
- int boost_tick = 0;
-
- AltosRecord previous = null;
- records = new TreeSet<AltosRecord> ();
+ telems = new LinkedList<AltosTelemetry> ();
try {
for (;;) {
break;
}
try {
- AltosRecord record = AltosTelemetry.parse(line, previous);
- if (record == null)
+ AltosTelemetry telem = AltosTelemetry.parse(line);
+ if (telem == null)
break;
- if (records.isEmpty()) {
- current_tick = record.tick;
- } else {
- int tick = record.tick;
- while (tick < current_tick - 0x1000)
- tick += 0x10000;
- current_tick = tick;
- record.tick = current_tick;
- }
- if (!saw_boost && record.state >= AltosLib.ao_flight_boost)
- {
- saw_boost = true;
- boost_tick = record.tick;
- }
- if (record.acceleration() != AltosRecord.MISSING)
- has_accel = true;
- if (record.gps != null)
- has_gps = true;
- if (record.main_voltage() != AltosRecord.MISSING)
- has_ignite = true;
- if (previous != null && previous.tick != record.tick)
- records.add(previous);
- previous = record;
+ telems.add(telem);
} catch (ParseException pe) {
System.out.printf("parse exception %s\n", pe.getMessage());
} catch (AltosCRCException ce) {
} catch (IOException io) {
System.out.printf("io exception\n");
}
-
- if (previous != null)
- records.add(previous);
-
- /* Adjust all tick counts to match expected eeprom values,
- * which starts with a 16-bit tick count 16 samples before boost
- */
-
- int tick_adjust = (boost_tick - 16) & 0xffff0000;
- for (AltosRecord r : this)
- r.tick -= tick_adjust;
- boost_tick -= tick_adjust;
-
- /* adjust all tick counts to be relative to boost time */
- for (AltosRecord r : this)
- r.time = (r.tick - boost_tick) / 100.0;
-
- try {
- input.close();
- } catch (IOException ie) {
- }
}
}
--- /dev/null
+/*
+ * 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.*;
+
+/*
+ * Telemetry data contents
+ */
+
+
+/*
+ * The packet format is a simple hex dump of the raw telemetry frame.
+ * It starts with 'TELEM', then contains hex digits with a checksum as the last
+ * byte on the line.
+ *
+ * Version 4 is a replacement with consistent syntax. Each telemetry line
+ * contains a sequence of space-separated names and values, the values are
+ * either integers or strings. The names are all unique. All values are
+ * optional
+ *
+ * VERSION 4 c KD7SQG n 236 f 18 r -25 s pad t 513 r_a 15756 r_b 26444 r_t 20944
+ * r_v 26640 r_d 512 r_m 208 c_a 15775 c_b 26439 c_p 15749 c_m 16281 a_a 15764
+ * a_s 0 a_b 26439 g_s u g_n 0 s_n 0
+ *
+ * VERSION 4 c KD7SQG n 19 f 0 r -23 s pad t 513 r_b 26372 r_t 21292 r_v 26788
+ * r_d 136 r_m 140 c_b 26370 k_h 0 k_s 0 k_a 0
+ *
+ * General header fields
+ *
+ * Name Value
+ *
+ * VERSION Telemetry version number (4 or more). Must be first.
+ * c Callsign (string, no spaces allowed)
+ * n Flight unit serial number (integer)
+ * f Flight number (integer)
+ * r Packet RSSI value (integer)
+ * s Flight computer state (string, no spaces allowed)
+ * t Flight computer clock (integer in centiseconds)
+ *
+ * Version 3 is Version 2 with fixed RSSI numbers -- the radio reports
+ * in 1/2dB increments while this protocol provides only integers. So,
+ * the syntax didn't change just the interpretation of the RSSI
+ * values.
+ *
+ * Version 2 of the telemetry data stream is a bit of a mess, with no
+ * consistent formatting. In particular, the GPS data is formatted for
+ * viewing instead of parsing. However, the key feature is that every
+ * telemetry line contains all of the information necessary to
+ * describe the current rocket state, including the calibration values
+ * for accelerometer and barometer.
+ *
+ * GPS unlocked:
+ *
+ * VERSION 2 CALL KB0G SERIAL 51 FLIGHT 2 RSSI -68 STATUS ff STATE pad 1001 \
+ * a: 16032 p: 21232 t: 20284 v: 25160 d: 204 m: 204 fa: 16038 ga: 16032 fv: 0 \
+ * fp: 21232 gp: 21230 a+: 16049 a-: 16304 GPS 0 sat unlocked SAT 1 15 30
+ *
+ * GPS locked:
+ *
+ * VERSION 2 CALL KB0G SERIAL 51 FLIGHT 2 RSSI -71 STATUS ff STATE pad 2504 \
+ * a: 16028 p: 21220 t: 20360 v: 25004 d: 208 m: 200 fa: 16031 ga: 16032 fv: 330 \
+ * fp: 21231 gp: 21230 a+: 16049 a-: 16304 \
+ * GPS 9 sat 2010-02-13 17:16:51 35°20.0803'N 106°45.2235'W 1790m \
+ * 0.00m/s(H) 0° 0.00m/s(V) 1.0(hdop) 0(herr) 0(verr) \
+ * SAT 10 29 30 24 28 5 25 21 20 15 33 1 23 30 24 18 26 10 29 2 26
+ *
+ */
+
+public class AltosTelemetryLegacy extends AltosTelemetry {
+ /*
+ * General header fields
+ *
+ * Name Value
+ *
+ * VERSION Telemetry version number (4 or more). Must be first.
+ * c Callsign (string, no spaces allowed)
+ * n Flight unit serial number (integer)
+ * f Flight number (integer)
+ * r Packet RSSI value (integer)
+ * s Flight computer state (string, no spaces allowed)
+ * t Flight computer clock (integer in centiseconds)
+ */
+
+ final static String AO_TELEM_VERSION = "VERSION";
+ final static String AO_TELEM_CALL = "c";
+ final static String AO_TELEM_SERIAL = "n";
+ final static String AO_TELEM_FLIGHT = "f";
+ final static String AO_TELEM_RSSI = "r";
+ final static String AO_TELEM_STATE = "s";
+ final static String AO_TELEM_TICK = "t";
+
+ /*
+ * Raw sensor values
+ *
+ * Name Value
+ * r_a Accelerometer reading (integer)
+ * r_b Barometer reading (integer)
+ * r_t Thermometer reading (integer)
+ * r_v Battery reading (integer)
+ * r_d Drogue continuity (integer)
+ * r_m Main continuity (integer)
+ */
+
+ final static String AO_TELEM_RAW_ACCEL = "r_a";
+ final static String AO_TELEM_RAW_BARO = "r_b";
+ final static String AO_TELEM_RAW_THERMO = "r_t";
+ final static String AO_TELEM_RAW_BATT = "r_v";
+ final static String AO_TELEM_RAW_DROGUE = "r_d";
+ final static String AO_TELEM_RAW_MAIN = "r_m";
+
+ /*
+ * Sensor calibration values
+ *
+ * Name Value
+ * c_a Ground accelerometer reading (integer)
+ * c_b Ground barometer reading (integer)
+ * c_p Accelerometer reading for +1g
+ * c_m Accelerometer reading for -1g
+ */
+
+ final static String AO_TELEM_CAL_ACCEL_GROUND = "c_a";
+ final static String AO_TELEM_CAL_BARO_GROUND = "c_b";
+ final static String AO_TELEM_CAL_ACCEL_PLUS = "c_p";
+ final static String AO_TELEM_CAL_ACCEL_MINUS = "c_m";
+
+ /*
+ * Kalman state values
+ *
+ * Name Value
+ * k_h Height above pad (integer, meters)
+ * k_s Vertical speeed (integer, m/s * 16)
+ * k_a Vertical acceleration (integer, m/s² * 16)
+ */
+
+ final static String AO_TELEM_KALMAN_HEIGHT = "k_h";
+ final static String AO_TELEM_KALMAN_SPEED = "k_s";
+ final static String AO_TELEM_KALMAN_ACCEL = "k_a";
+
+ /*
+ * Ad-hoc flight values
+ *
+ * Name Value
+ * a_a Acceleration (integer, sensor units)
+ * a_s Speed (integer, integrated acceleration value)
+ * a_b Barometer reading (integer, sensor units)
+ */
+
+ final static String AO_TELEM_ADHOC_ACCEL = "a_a";
+ final static String AO_TELEM_ADHOC_SPEED = "a_s";
+ final static String AO_TELEM_ADHOC_BARO = "a_b";
+
+ /*
+ * GPS values
+ *
+ * Name Value
+ * g_s GPS state (string):
+ * l locked
+ * u unlocked
+ * e error (missing or broken)
+ * g_n Number of sats used in solution
+ * g_ns Latitude (degrees * 10e7)
+ * g_ew Longitude (degrees * 10e7)
+ * g_a Altitude (integer meters)
+ * g_Y GPS year (integer)
+ * g_M GPS month (integer - 1-12)
+ * g_D GPS day (integer - 1-31)
+ * g_h GPS hour (integer - 0-23)
+ * g_m GPS minute (integer - 0-59)
+ * g_s GPS second (integer - 0-59)
+ * g_v GPS vertical speed (integer, cm/sec)
+ * g_s GPS horizontal speed (integer, cm/sec)
+ * g_c GPS course (integer, 0-359)
+ * g_hd GPS hdop (integer * 10)
+ * g_vd GPS vdop (integer * 10)
+ * g_he GPS h error (integer)
+ * g_ve GPS v error (integer)
+ */
+
+ final static String AO_TELEM_GPS_STATE = "g";
+ final static String AO_TELEM_GPS_STATE_LOCKED = "l";
+ final static String AO_TELEM_GPS_STATE_UNLOCKED = "u";
+ final static String AO_TELEM_GPS_STATE_ERROR = "e";
+ final static String AO_TELEM_GPS_NUM_SAT = "g_n";
+ final static String AO_TELEM_GPS_LATITUDE = "g_ns";
+ final static String AO_TELEM_GPS_LONGITUDE = "g_ew";
+ final static String AO_TELEM_GPS_ALTITUDE = "g_a";
+ final static String AO_TELEM_GPS_YEAR = "g_Y";
+ final static String AO_TELEM_GPS_MONTH = "g_M";
+ final static String AO_TELEM_GPS_DAY = "g_D";
+ final static String AO_TELEM_GPS_HOUR = "g_h";
+ final static String AO_TELEM_GPS_MINUTE = "g_m";
+ final static String AO_TELEM_GPS_SECOND = "g_s";
+ final static String AO_TELEM_GPS_VERTICAL_SPEED = "g_v";
+ final static String AO_TELEM_GPS_HORIZONTAL_SPEED = "g_g";
+ final static String AO_TELEM_GPS_COURSE = "g_c";
+ final static String AO_TELEM_GPS_HDOP = "g_hd";
+ final static String AO_TELEM_GPS_VDOP = "g_vd";
+ final static String AO_TELEM_GPS_HERROR = "g_he";
+ final static String AO_TELEM_GPS_VERROR = "g_ve";
+
+ /*
+ * GPS satellite values
+ *
+ * Name Value
+ * s_n Number of satellites reported (integer)
+ * s_v0 Space vehicle ID (integer) for report 0
+ * s_c0 C/N0 number (integer) for report 0
+ * s_v1 Space vehicle ID (integer) for report 1
+ * s_c1 C/N0 number (integer) for report 1
+ * ...
+ */
+
+ final static String AO_TELEM_SAT_NUM = "s_n";
+ final static String AO_TELEM_SAT_SVID = "s_v";
+ final static String AO_TELEM_SAT_C_N_0 = "s_c";
+
+ public int version;
+ public String callsign;
+ public int flight;
+ public int state;
+
+ public AltosGPS gps;
+ public int gps_sequence;
+
+ /* Telemetry sources have these values recorded from the flight computer */
+ public double kalman_height;
+ public double kalman_speed;
+ public double kalman_acceleration;
+
+ /* Sensor values */
+ public int accel;
+ public int pres;
+ public int temp;
+ public int batt;
+ public int apogee;
+ public int main;
+
+ 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;
+
+ private void parse_v4(String[] words, int i) throws ParseException {
+ AltosTelemetryMap map = new AltosTelemetryMap(words, i);
+
+ callsign = map.get_string(AO_TELEM_CALL, "N0CALL");
+ serial = map.get_int(AO_TELEM_SERIAL, AltosRecord.MISSING);
+ flight = map.get_int(AO_TELEM_FLIGHT, AltosRecord.MISSING);
+ rssi = map.get_int(AO_TELEM_RSSI, AltosRecord.MISSING);
+ state = AltosLib.state(map.get_string(AO_TELEM_STATE, "invalid"));
+ tick = map.get_int(AO_TELEM_TICK, 0);
+
+ /* raw sensor values */
+ accel = map.get_int(AO_TELEM_RAW_ACCEL, AltosRecord.MISSING);
+ pres = map.get_int(AO_TELEM_RAW_BARO, AltosRecord.MISSING);
+ temp = map.get_int(AO_TELEM_RAW_THERMO, AltosRecord.MISSING);
+ batt = map.get_int(AO_TELEM_RAW_BATT, AltosRecord.MISSING);
+ apogee = map.get_int(AO_TELEM_RAW_DROGUE, AltosRecord.MISSING);
+ main = map.get_int(AO_TELEM_RAW_MAIN, AltosRecord.MISSING);
+
+ /* sensor calibration information */
+ ground_accel = map.get_int(AO_TELEM_CAL_ACCEL_GROUND, AltosRecord.MISSING);
+ ground_pres = map.get_int(AO_TELEM_CAL_BARO_GROUND, AltosRecord.MISSING);
+ accel_plus_g = map.get_int(AO_TELEM_CAL_ACCEL_PLUS, AltosRecord.MISSING);
+ accel_minus_g = map.get_int(AO_TELEM_CAL_ACCEL_MINUS, AltosRecord.MISSING);
+
+ /* flight computer values */
+ kalman_acceleration = map.get_double(AO_TELEM_KALMAN_ACCEL, AltosRecord.MISSING, 1/16.0);
+ kalman_speed = map.get_double(AO_TELEM_KALMAN_SPEED, AltosRecord.MISSING, 1/16.0);
+ kalman_height = map.get_int(AO_TELEM_KALMAN_HEIGHT, AltosRecord.MISSING);
+
+ flight_accel = map.get_int(AO_TELEM_ADHOC_ACCEL, AltosRecord.MISSING);
+ flight_vel = map.get_int(AO_TELEM_ADHOC_SPEED, AltosRecord.MISSING);
+ flight_pres = map.get_int(AO_TELEM_ADHOC_BARO, AltosRecord.MISSING);
+
+ if (map.has(AO_TELEM_GPS_STATE))
+ gps = new AltosGPS(map);
+ else
+ gps = null;
+ }
+
+ private void parse_legacy(String[] words, int i) throws ParseException {
+
+ AltosParse.word (words[i++], "CALL");
+ callsign = words[i++];
+
+ AltosParse.word (words[i++], "SERIAL");
+ serial = AltosParse.parse_int(words[i++]);
+
+ if (version >= 2) {
+ AltosParse.word (words[i++], "FLIGHT");
+ flight = AltosParse.parse_int(words[i++]);
+ } else
+ flight = 0;
+
+ AltosParse.word(words[i++], "RSSI");
+ rssi = AltosParse.parse_int(words[i++]);
+
+ /* Older telemetry data had mis-computed RSSI value */
+ if (version <= 2)
+ rssi = (rssi + 74) / 2 - 74;
+
+ AltosParse.word(words[i++], "STATUS");
+ status = AltosParse.parse_hex(words[i++]);
+
+ AltosParse.word(words[i++], "STATE");
+ state = AltosLib.state(words[i++]);
+
+ tick = AltosParse.parse_int(words[i++]);
+
+ AltosParse.word(words[i++], "a:");
+ accel = AltosParse.parse_int(words[i++]);
+
+ AltosParse.word(words[i++], "p:");
+ pres = AltosParse.parse_int(words[i++]);
+
+ AltosParse.word(words[i++], "t:");
+ temp = AltosParse.parse_int(words[i++]);
+
+ AltosParse.word(words[i++], "v:");
+ batt = AltosParse.parse_int(words[i++]);
+
+ AltosParse.word(words[i++], "d:");
+ apogee = AltosParse.parse_int(words[i++]);
+
+ AltosParse.word(words[i++], "m:");
+ main = AltosParse.parse_int(words[i++]);
+
+ AltosParse.word(words[i++], "fa:");
+ flight_accel = AltosParse.parse_int(words[i++]);
+
+ AltosParse.word(words[i++], "ga:");
+ ground_accel = AltosParse.parse_int(words[i++]);
+
+ AltosParse.word(words[i++], "fv:");
+ flight_vel = AltosParse.parse_int(words[i++]);
+
+ AltosParse.word(words[i++], "fp:");
+ flight_pres = AltosParse.parse_int(words[i++]);
+
+ /* Old TeleDongle code with kalman-reporting TeleMetrum code */
+ if ((flight_vel & 0xffff0000) == 0x80000000) {
+ kalman_speed = ((short) flight_vel) / 16.0;
+ kalman_acceleration = flight_accel / 16.0;
+ kalman_height = flight_pres;
+ flight_vel = AltosRecord.MISSING;
+ flight_pres = AltosRecord.MISSING;
+ flight_accel = AltosRecord.MISSING;
+ } else {
+ kalman_speed = AltosRecord.MISSING;
+ kalman_acceleration = AltosRecord.MISSING;
+ kalman_height = AltosRecord.MISSING;
+ }
+
+ AltosParse.word(words[i++], "gp:");
+ ground_pres = AltosParse.parse_int(words[i++]);
+
+ if (version >= 1) {
+ AltosParse.word(words[i++], "a+:");
+ accel_plus_g = AltosParse.parse_int(words[i++]);
+
+ AltosParse.word(words[i++], "a-:");
+ accel_minus_g = AltosParse.parse_int(words[i++]);
+ } else {
+ accel_plus_g = ground_accel;
+ accel_minus_g = ground_accel + 530;
+ }
+
+ gps = new AltosGPS(words, i, version);
+ gps_sequence++;
+ }
+
+ public AltosTelemetryLegacy(String line) throws ParseException, AltosCRCException {
+ String[] words = line.split("\\s+");
+ int i = 0;
+
+ if (words[i].equals("CRC") && words[i+1].equals("INVALID")) {
+ i += 2;
+ AltosParse.word(words[i++], "RSSI");
+ rssi = AltosParse.parse_int(words[i++]);
+ throw new AltosCRCException(rssi);
+ }
+ if (words[i].equals("CALL")) {
+ version = 0;
+ } else {
+ AltosParse.word (words[i++], "VERSION");
+ version = AltosParse.parse_int(words[i++]);
+ }
+
+ if (version < 4)
+ parse_legacy(words, i);
+ else
+ parse_v4(words, i);
+ }
+
+ /*
+ * Given a hex dump of a legacy telemetry line, construct an AltosRecordTM from that
+ */
+
+ int[] bytes;
+ int adjust;
+
+ /*
+ private int int8(int i) {
+ return AltosLib.int8(bytes, i + 1 + adjust);
+ }
+ */
+ private int uint8(int i) {
+ return AltosLib.uint8(bytes, i + 1 + adjust);
+ }
+ private int int16(int i) {
+ return AltosLib.int16(bytes, i + 1 + adjust);
+ }
+ private int uint16(int i) {
+ return AltosLib.uint16(bytes, i + 1 + adjust);
+ }
+ private int uint32(int i) {
+ return AltosLib.uint32(bytes, i + 1 + adjust);
+ }
+ private String string(int i, int l) {
+ return AltosLib.string(bytes, i + 1 + adjust, l);
+ }
+
+ static final int AO_GPS_NUM_SAT_MASK = (0xf << 0);
+ static final int AO_GPS_NUM_SAT_SHIFT = (0);
+
+ static final int AO_GPS_VALID = (1 << 4);
+ static final int AO_GPS_RUNNING = (1 << 5);
+ static final int AO_GPS_DATE_VALID = (1 << 6);
+ static final int AO_GPS_COURSE_VALID = (1 << 7);
+
+ public AltosTelemetryLegacy(int[] in_bytes) {
+ bytes = in_bytes;
+ version = 4;
+ adjust = 0;
+
+ if (bytes.length == AltosLib.ao_telemetry_0_8_len + 4) {
+ serial = uint8(0);
+ adjust = -1;
+ } else
+ serial = uint16(0);
+
+ callsign = string(62, 8);
+ flight = uint16(2);
+ state = uint8(4);
+ tick = uint16(21);
+ accel = int16(23);
+ pres = int16(25);
+ temp = int16(27);
+ batt = int16(29);
+ apogee = int16(31);
+ main = int16(33);
+
+ ground_accel = int16(7);
+ ground_pres = int16(15);
+ accel_plus_g = int16(17);
+ accel_minus_g = int16(19);
+
+ if (uint16(11) == 0x8000) {
+ kalman_acceleration = int16(5);
+ kalman_speed = int16(9);
+ kalman_height = int16(13);
+ flight_accel = AltosRecord.MISSING;
+ flight_vel = AltosRecord.MISSING;
+ flight_pres = AltosRecord.MISSING;
+ } else {
+ flight_accel = int16(5);
+ flight_vel = uint32(9);
+ flight_pres = int16(13);
+ kalman_acceleration = AltosRecord.MISSING;
+ kalman_speed = AltosRecord.MISSING;
+ kalman_height = AltosRecord.MISSING;
+ }
+
+ gps = null;
+
+ int gps_flags = uint8(41);
+
+ if ((gps_flags & (AO_GPS_VALID|AO_GPS_RUNNING)) != 0) {
+ gps = new AltosGPS();
+ gps_sequence++;
+
+ gps.nsat = (gps_flags & AO_GPS_NUM_SAT_MASK);
+ gps.locked = (gps_flags & AO_GPS_VALID) != 0;
+ gps.connected = true;
+ gps.lat = uint32(42) / 1.0e7;
+ gps.lon = uint32(46) / 1.0e7;
+ gps.alt = int16(50);
+ gps.ground_speed = uint16(52) / 100.0;
+ gps.course = uint8(54) * 2;
+ gps.hdop = uint8(55) / 5.0;
+ gps.h_error = uint16(58);
+ gps.v_error = uint16(60);
+
+ int n_tracking_reported = uint8(70);
+ if (n_tracking_reported > 12)
+ n_tracking_reported = 12;
+ int n_tracking_actual = 0;
+ for (int i = 0; i < n_tracking_reported; i++) {
+ if (uint8(71 + i*2) != 0)
+ n_tracking_actual++;
+ }
+ if (n_tracking_actual > 0) {
+ gps.cc_gps_sat = new AltosGPSSat[n_tracking_actual];
+
+ n_tracking_actual = 0;
+ for (int i = 0; i < n_tracking_reported; i++) {
+ int svid = uint8(71 + i*2);
+ int c_n0 = uint8(72 + i*2);
+ if (svid != 0)
+ gps.cc_gps_sat[n_tracking_actual++] = new AltosGPSSat(svid, c_n0);
+ }
+ }
+ }
+ }
+
+ public void update_state(AltosState state) {
+ state.set_tick(tick);
+ state.set_state(this.state);
+ state.set_flight(flight);
+ state.set_serial(serial);
+ state.set_rssi(rssi, status);
+
+ state.set_pressure(AltosConvert.barometer_to_pressure(pres));
+ state.set_accel_g(accel_plus_g, accel_minus_g);
+ state.set_accel(accel);
+ if (kalman_height != AltosRecord.MISSING)
+ state.set_kalman(kalman_height, kalman_speed, kalman_acceleration);
+ state.set_temperature(AltosEepromTM.thermometer_to_temperature(temp));
+ state.set_battery_voltage(AltosConvert.cc_battery_to_voltage(batt));
+ state.set_apogee_voltage(AltosConvert.cc_ignitor_to_voltage(apogee));
+ state.set_main_voltage(AltosConvert.cc_ignitor_to_voltage(main));
+ if (gps != null)
+ state.set_gps(gps, gps_sequence);
+ }
+}
AltosRecord previous;
double frequency;
int telemetry;
+ AltosState state = null;
LinkedBlockingQueue<AltosLine> telem;
- public AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException {
+ public AltosState read() throws InterruptedException, ParseException, AltosCRCException, IOException {
AltosLine l = telem.take();
if (l.line == null)
throw new IOException("IO error");
- AltosRecord next = AltosTelemetry.parse(l.line, previous);
- previous = next;
- return next;
+ AltosTelemetry telem = AltosTelemetryLegacy.parse(l.line);
+ if (state == null)
+ state = new AltosState();
+ else
+ state = state.clone();
+ telem.update_state(state);
+ return state;
}
public void flush() {
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;
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
AltosConvert.java \
AltosCRCException.java \
AltosDebug.java \
+ AltosEeprom.java \
AltosEepromChunk.java \
+ AltosEepromFile.java \
+ AltosEepromTM.java \
+ AltosEepromHeader.java \
AltosEepromIterable.java \
AltosEepromLog.java \
AltosEepromMega.java \
AltosEepromRecord.java \
AltosEepromTeleScience.java \
AltosEepromMini.java \
- AltosEepromMiniIterable.java \
+ AltosEepromOldIterable.java \
AltosFile.java \
AltosFlash.java \
AltosFlashListener.java \
AltosSensorMM.java \
AltosSensorTM.java \
AltosState.java \
+ AltosStateIterable.java \
+ AltosStateUpdate.java \
AltosTelemetry.java \
+ AltosTelemetryFile.java \
AltosTelemetryIterable.java \
+ AltosTelemetryLegacy.java \
AltosTelemetryMap.java \
AltosTelemetryReader.java \
- AltosTelemetryRecordCompanion.java \
- AltosTelemetryRecordConfiguration.java \
- AltosTelemetryRecordGeneral.java \
- AltosTelemetryRecord.java \
- AltosTelemetryRecordLegacy.java \
- AltosTelemetryRecordLocation.java \
- AltosTelemetryRecordRaw.java \
- AltosTelemetryRecordSatellite.java \
- AltosTelemetryRecordSensor.java \
- AltosTelemetryRecordMegaSensor.java \
- AltosTelemetryRecordMegaData.java \
AltosUnitsListener.java \
AltosMs5607.java \
AltosIMU.java \
class Speed extends AscentValueHold {
void show (AltosState state, AltosListenerState listener_state) {
- double speed = state.accel_speed;
- if (!state.ascent)
- speed = state.baro_speed;
- show(AltosConvert.speed, speed);
+ show(AltosConvert.speed, state.speed);
}
public Speed (GridBagLayout layout, int y) {
super (layout, y, "Speed");
class Apogee extends AscentStatus {
void show (AltosState state, AltosListenerState listener_state) {
- show("%4.2f V", state.drogue_sense);
- lights.set(state.drogue_sense > 3.2);
+ show("%4.2f V", state.apogee_voltage);
+ lights.set(state.apogee_voltage > 3.7);
}
public Apogee (GridBagLayout layout, int y) {
super(layout, y, "Apogee Igniter Voltage");
class Main extends AscentStatus {
void show (AltosState state, AltosListenerState listener_state) {
- show("%4.2f V", state.main_sense);
- lights.set(state.main_sense > 3.2);
+ show("%4.2f V", state.main_voltage);
+ lights.set(state.main_voltage > 3.7);
}
public Main (GridBagLayout layout, int y) {
super(layout, y, "Main Igniter Voltage");
lon.hide();
}
height.show(state, listener_state);
- if (state.main_sense != AltosRecord.MISSING)
+ if (state.main_voltage != AltosRecord.MISSING)
main.show(state, listener_state);
else
main.hide();
- if (state.drogue_sense != AltosRecord.MISSING)
+ if (state.apogee_voltage != AltosRecord.MISSING)
apogee.show(state, listener_state);
else
apogee.hide();
boolean header_written;
boolean seen_boost;
int boost_tick;
- LinkedList<AltosRecord> pad_records;
+ LinkedList<AltosState> pad_states;
AltosState state;
static final int ALTOS_CSV_VERSION = 5;
out.printf("version,serial,flight,call,time,clock,rssi,lqi");
}
- void write_general(AltosRecord record) {
+ void write_general(AltosState state) {
out.printf("%s, %d, %d, %s, %8.2f, %8.2f, %4d, %3d",
- ALTOS_CSV_VERSION, record.serial, record.flight, record.callsign,
- (double) record.time, (double) record.tick / 100.0,
- record.rssi,
- record.status & 0x7f);
+ ALTOS_CSV_VERSION, state.serial, state.flight, state.callsign,
+ (double) state.time, (double) state.tick / 100.0,
+ state.rssi,
+ state.status & 0x7f);
}
void write_flight_header() {
out.printf("state,state_name");
}
- void write_flight(AltosRecord record) {
- out.printf("%d,%8s", record.state, record.state());
+ void write_flight(AltosState state) {
+ out.printf("%d,%8s", state.state, state.state_name());
}
void write_basic_header() {
out.printf("acceleration,pressure,altitude,height,accel_speed,baro_speed,temperature,battery_voltage,drogue_voltage,main_voltage");
}
- void write_basic(AltosRecord record) {
+ void write_basic(AltosState state) {
out.printf("%8.2f,%10.2f,%8.2f,%8.2f,%8.2f,%8.2f,%5.1f,%5.2f,%5.2f,%5.2f",
- record.acceleration(),
- record.pressure(),
- record.altitude(),
- record.height(),
- state.accel_speed,
- state.baro_speed,
- record.temperature(),
- record.battery_voltage(),
- record.drogue_voltage(),
- record.main_voltage());
+ state.acceleration,
+ state.pressure,
+ state.altitude,
+ state.height,
+ state.speed,
+ state.speed,
+ state.temperature,
+ state.battery_voltage,
+ state.apogee_voltage,
+ state.main_voltage);
}
void write_advanced_header() {
out.printf("accel_x,accel_y,accel_z,gyro_x,gyro_y,gyro_z");
}
- void write_advanced(AltosRecord record) {
- AltosIMU imu = record.imu();
- AltosMag mag = record.mag();
+ void write_advanced(AltosState state) {
+ AltosIMU imu = state.imu;
+ AltosMag mag = state.mag;
if (imu == null)
imu = new AltosIMU();
out.printf("connected,locked,nsat,latitude,longitude,altitude,year,month,day,hour,minute,second,pad_dist,pad_range,pad_az,pad_el,hdop");
}
- void write_gps(AltosRecord record) {
- AltosGPS gps = record.gps;
+ void write_gps(AltosState state) {
+ AltosGPS gps = state.gps;
if (gps == null)
gps = new AltosGPS();
if (from_pad == null)
from_pad = new AltosGreatCircle();
- out.printf("%2d,%2d,%3d,%12.7f,%12.7f,%6d,%5d,%3d,%3d,%3d,%3d,%3d,%9.0f,%9.0f,%4.0f,%4.0f,%6.1f",
+ out.printf("%2d,%2d,%3d,%12.7f,%12.7f,%8.1f,%5d,%3d,%3d,%3d,%3d,%3d,%9.0f,%9.0f,%4.0f,%4.0f,%6.1f",
gps.connected?1:0,
gps.locked?1:0,
gps.nsat,
}
}
- void write_gps_sat(AltosRecord record) {
- AltosGPS gps = record.gps;
+ void write_gps_sat(AltosState state) {
+ AltosGPS gps = state.gps;
for(int i = 1; i <= 32; i++) {
int c_n0 = 0;
if (gps != null && gps.cc_gps_sat != null) {
out.printf(",companion_%02d", i);
}
- void write_companion(AltosRecord record) {
- AltosRecordCompanion companion = record.companion;
+ void write_companion(AltosState state) {
+ AltosRecordCompanion companion = state.companion;
int channels_written = 0;
if (companion == null) {
out.printf ("\n");
}
- void write_one(AltosRecord record) {
- state = new AltosState(record, state);
- write_general(record); out.printf(",");
- write_flight(record); out.printf(",");
- write_basic(record); out.printf(",");
- if (record.imu() != null || record.mag() != null)
- write_advanced(record);
- if (record.gps != null) {
+ void write_one(AltosState state) {
+ write_general(state); out.printf(",");
+ write_flight(state); out.printf(",");
+ write_basic(state); out.printf(",");
+ if (state.imu != null || state.mag != null)
+ write_advanced(state);
+ if (state.gps != null) {
out.printf(",");
- write_gps(record); out.printf(",");
- write_gps_sat(record);
+ write_gps(state); out.printf(",");
+ write_gps_sat(state);
}
- if (record.companion != null) {
+ if (state.companion != null) {
out.printf(",");
- write_companion(record);
+ write_companion(state);
}
out.printf ("\n");
}
void flush_pad() {
- while (!pad_records.isEmpty()) {
- write_one (pad_records.remove());
+ while (!pad_states.isEmpty()) {
+ write_one (pad_states.remove());
}
}
- public void write(AltosRecord record) {
- if (record.state == Altos.ao_flight_startup)
+ public void write(AltosState state) {
+ if (state.state == Altos.ao_flight_startup)
return;
if (!header_written) {
- write_header(record.imu() != null || record.mag() != null,
- record.gps != null, record.companion != null);
+ write_header(state.imu != null || state.mag != null,
+ state.gps != null, state.companion != null);
header_written = true;
}
if (!seen_boost) {
- if (record.state >= Altos.ao_flight_boost) {
+ if (state.state >= Altos.ao_flight_boost) {
seen_boost = true;
- boost_tick = record.tick;
+ boost_tick = state.tick;
flush_pad();
}
}
if (seen_boost)
- write_one(record);
+ write_one(state);
else
- pad_records.add(record);
+ pad_states.add(state);
}
public PrintStream out() {
}
public void close() {
- if (!pad_records.isEmpty()) {
- boost_tick = pad_records.element().tick;
+ if (!pad_states.isEmpty()) {
+ boost_tick = pad_states.element().tick;
flush_pad();
}
out.close();
}
- public void write(AltosRecordIterable iterable) {
- iterable.write_comments(out());
- for (AltosRecord r : iterable)
- write(r);
+ public void write(AltosStateIterable states) {
+ states.write_comments(out());
+ for (AltosState state : states)
+ write(state);
}
public AltosCSV(PrintStream in_out, File in_name) {
name = in_name;
out = in_out;
- pad_records = new LinkedList<AltosRecord>();
+ pad_states = new LinkedList<AltosState>();
}
public AltosCSV(File in_name) throws FileNotFoundException {
JFileChooser csv_chooser;
JPanel accessory;
JComboBox combo_box;
- AltosRecordIterable iterable;
+ Iterable<AltosState> states;
AltosWriter writer;
static String[] combo_box_items = { "Comma Separated Values (.CSV)", "Googleearth Data (.KML)" };
set_default_file();
}
- public AltosCSVUI(JFrame frame, AltosRecordIterable in_iterable, File source_file) {
- iterable = in_iterable;
+ public AltosCSVUI(JFrame frame, AltosStateIterable states, File source_file) {
+ this.states = states;
csv_chooser = new JFileChooser(source_file);
accessory = new JPanel();
writer = new AltosCSV(file);
else
writer = new AltosKML(file);
- writer.write(iterable);
+ writer.write(states);
writer.close();
} catch (FileNotFoundException ee) {
JOptionPane.showMessageDialog(frame,
public void show(AltosState state, AltosListenerState listener_state) {
if (state == null)
return;
- if (state.data.companion != null)
- companion = state.data.companion;
+ if (state.companion != null)
+ companion = state.companion;
info_reset();
info_add_row(0, "Companion board", "%s", board_name());
if (companion != null) {
return file;
}
- public AltosRecordIterable runDialog() {
+ public AltosStateIterable runDialog() {
int ret;
ret = showOpenDialog(frame);
try {
if (filename.endsWith("eeprom")) {
FileInputStream in = new FileInputStream(file);
- return new AltosEepromIterable(in);
+ return new AltosEepromFile(in);
} else if (filename.endsWith("telem")) {
FileInputStream in = new FileInputStream(file);
- return new AltosTelemetryIterable(in);
- } else if (filename.endsWith("mega")) {
- FileInputStream in = new FileInputStream(file);
- return new AltosEepromMegaIterable(in);
- } else if (filename.endsWith("mini")) {
- FileInputStream in = new FileInputStream(file);
- return new AltosEepromMiniIterable(in);
+ return null; // new AltosTelemetryIterable(in);
} else {
throw new FileNotFoundException();
}
class Speed extends DescentValue {
void show (AltosState state, AltosListenerState listener_state) {
- double speed = state.accel_speed;
- if (!state.ascent)
- speed = state.baro_speed;
- show(AltosConvert.speed, speed);
+ show(AltosConvert.speed, state.speed);
}
public Speed (GridBagLayout layout, int x, int y) {
super (layout, x, y, "Speed");
class Apogee extends DescentStatus {
void show (AltosState state, AltosListenerState listener_state) {
- show("%4.2f V", state.drogue_sense);
- lights.set(state.drogue_sense > 3.2);
+ show("%4.2f V", state.apogee_voltage);
+ lights.set(state.apogee_voltage > 3.7);
}
public Apogee (GridBagLayout layout, int y) {
super(layout, y, "Apogee Igniter Voltage");
class Main extends DescentStatus {
void show (AltosState state, AltosListenerState listener_state) {
- show("%4.2f V", state.main_sense);
- lights.set(state.main_sense > 3.2);
+ show("%4.2f V", state.main_voltage);
+ lights.set(state.main_voltage > 3.7);
}
public Main (GridBagLayout layout, int y) {
super(layout, y, "Main Igniter Voltage");
lat.hide();
lon.hide();
}
- if (state.main_sense != AltosRecord.MISSING)
+ if (state.main_voltage != AltosRecord.MISSING)
main.show(state, listener_state);
else
main.hide();
- if (state.drogue_sense != AltosRecord.MISSING)
+ if (state.apogee_voltage != AltosRecord.MISSING)
apogee.show(state, listener_state);
else
apogee.hide();
System.currentTimeMillis() - state.report_time >= 15000 ||
state.state == Altos.ao_flight_landed))
{
- if (Math.abs(state.baro_speed) < 20 && state.height < 100)
+ if (Math.abs(state.speed) < 20 && state.height < 100)
voice.speak("rocket landed safely");
else
voice.speak("rocket may have crashed");
synchronized boolean tell() {
boolean ret = false;
if (old_state == null || old_state.state != state.state) {
- voice.speak(state.data.state());
+ voice.speak(state.state_name());
if ((old_state == null || old_state.state <= Altos.ao_flight_boost) &&
state.state > Altos.ao_flight_boost) {
voice.speak("max speed: %s.",
- AltosConvert.speed.say_units(state.max_accel_speed + 0.5));
+ AltosConvert.speed.say_units(state.max_speed + 0.5));
ret = true;
} else if ((old_state == null || old_state.state < Altos.ao_flight_drogue) &&
state.state >= Altos.ao_flight_drogue) {
try {
for (;;) {
try {
- AltosRecord record = reader.read();
- if (record == null)
+ state = reader.read();
+ if (state == null)
break;
- old_state = state;
- state = new AltosState(record, state);
reader.update(state);
show_safely();
told = tell();
extension = "mega";
CaptureMega(eechunk);
break;
- case AltosLib.AO_LOG_FORMAT_MINI:
- extension = "mini";
+ case AltosLib.AO_LOG_FORMAT_EASYMINI:
+ case AltosLib.AO_LOG_FORMAT_TELEMINI:
+ extension = "eeprom";
CaptureMini(eechunk);
break;
}
double max_height;
double max_speed;
double max_acceleration;
- double[] state_accel_speed = new double[Altos.ao_flight_invalid + 1];
- double[] state_baro_speed = new double[Altos.ao_flight_invalid + 1];
+ double[] state_speed = new double[Altos.ao_flight_invalid + 1];
double[] state_accel = new double[Altos.ao_flight_invalid + 1];
int[] state_count = new int[Altos.ao_flight_invalid + 1];
double[] state_start = new double[Altos.ao_flight_invalid + 1];
boolean has_other_adc;
boolean has_rssi;
- double landed_time(AltosRecordIterable iterable) {
- AltosState state = null;
- for (AltosRecord record : iterable) {
- state = new AltosState(record, state);
+ double landed_time(AltosStateIterable states) {
+ AltosState state = null;
+ for (AltosState s : states) {
+ state = s;
if (state.state == Altos.ao_flight_landed)
break;
}
+ if (state == null)
+ return 0;
+
double landed_height = state.height;
state = null;
double landed_time = -1000;
- for (AltosRecord record : iterable) {
- state = new AltosState(record, state);
+ for (AltosState s : states) {
+ state = s;
if (state.height > landed_height + 10) {
above = true;
return landed_time;
}
- double boost_time(AltosRecordIterable iterable) {
- double boost_time = -1000;
-
- AltosState state = null;
+ double boost_time(AltosStateIterable states) {
+ double boost_time = AltosRecord.MISSING;
+ AltosState state = null;
- for (AltosRecord record : iterable) {
- state = new AltosState(record, state);
-
+ for (AltosState s : states) {
+ state = s;
if (state.acceleration < 1)
boost_time = state.time;
if (state.state >= Altos.ao_flight_boost)
break;
}
- if (boost_time == -1000)
+ if (state == null)
+ return 0;
+
+ if (boost_time == AltosRecord.MISSING)
boost_time = state.time;
return boost_time;
}
- public AltosFlightStats(AltosRecordIterable iterable) throws InterruptedException, IOException {
- AltosState state = null;
- AltosState new_state = null;
- double boost_time = boost_time(iterable);
+ public AltosFlightStats(AltosStateIterable states) throws InterruptedException, IOException {
+ double boost_time = boost_time(states);
double end_time = 0;
- double landed_time = landed_time(iterable);
+ double landed_time = landed_time(states);
- year = month = day = -1;
- hour = minute = second = -1;
- serial = flight = -1;
- lat = lon = -1;
+ year = month = day = AltosRecord.MISSING;
+ hour = minute = second = AltosRecord.MISSING;
+ serial = flight = AltosRecord.MISSING;
+ lat = lon = AltosRecord.MISSING;
has_gps = false;
has_other_adc = false;
has_rssi = false;
- for (AltosRecord record : iterable) {
- if (serial < 0)
- serial = record.serial;
- if ((record.seen & AltosRecord.seen_flight) != 0 && flight < 0)
- flight = record.flight;
- if ((record.seen & AltosRecord.seen_temp_volt) != 0)
+ for (AltosState state : states) {
+ if (serial == AltosRecord.MISSING && state.serial != AltosRecord.MISSING)
+ serial = state.serial;
+ if (flight == AltosRecord.MISSING && state.flight != AltosRecord.MISSING)
+ flight = state.flight;
+ if (state.battery_voltage != AltosRecord.MISSING)
has_other_adc = true;
- if (record.rssi != 0)
+ if (state.rssi != AltosRecord.MISSING)
has_rssi = true;
- new_state = new AltosState(record, state);
- end_time = new_state.time;
- state = new_state;
+ end_time = state.time;
if (state.time >= boost_time && state.state < Altos.ao_flight_boost)
state.state = Altos.ao_flight_boost;
if (state.time >= landed_time && state.state < Altos.ao_flight_landed)
state.state = Altos.ao_flight_landed;
+ if (state.gps != null && state.gps.locked) {
+ year = state.gps.year;
+ month = state.gps.month;
+ day = state.gps.day;
+ hour = state.gps.hour;
+ minute = state.gps.minute;
+ second = state.gps.second;
+ }
if (0 <= state.state && state.state < Altos.ao_flight_invalid) {
- if (state.state >= Altos.ao_flight_boost) {
- if (state.gps != null && state.gps.locked &&
- year < 0) {
- year = state.gps.year;
- month = state.gps.month;
- day = state.gps.day;
- hour = state.gps.hour;
- minute = state.gps.minute;
- second = state.gps.second;
- }
- }
state_accel[state.state] += state.acceleration;
- state_accel_speed[state.state] += state.accel_speed;
- state_baro_speed[state.state] += state.baro_speed;
+ state_speed[state.state] += state.speed;
state_count[state.state]++;
if (state_start[state.state] == 0.0)
state_start[state.state] = state.time;
if (state_end[state.state] < state.time)
state_end[state.state] = state.time;
max_height = state.max_height;
- if (state.max_accel_speed != 0)
- max_speed = state.max_accel_speed;
- else
- max_speed = state.max_baro_speed;
+ max_speed = state.max_speed;
max_acceleration = state.max_acceleration;
}
if (state.gps != null && state.gps.locked && state.gps.nsat >= 4) {
}
for (int s = Altos.ao_flight_startup; s <= Altos.ao_flight_landed; s++) {
if (state_count[s] > 0) {
- state_accel_speed[s] /= state_count[s];
- state_baro_speed[s] /= state_count[s];
+ state_speed[s] /= state_count[s];
state_accel[s] /= state_count[s];
}
if (state_start[s] == 0)
String.format("%5.0f G", AltosConvert.meters_to_g(stats.state_accel[Altos.ao_flight_boost])));
}
new FlightStat(layout, y++, "Drogue descent rate",
- String.format("%5.0f m/s", stats.state_baro_speed[Altos.ao_flight_drogue]),
- String.format("%5.0f ft/s", AltosConvert.meters_to_feet(stats.state_baro_speed[Altos.ao_flight_drogue])));
+ String.format("%5.0f m/s", stats.state_speed[Altos.ao_flight_drogue]),
+ String.format("%5.0f ft/s", AltosConvert.meters_to_feet(stats.state_speed[Altos.ao_flight_drogue])));
new FlightStat(layout, y++, "Main descent rate",
- String.format("%5.0f m/s", stats.state_baro_speed[Altos.ao_flight_main]),
- String.format("%5.0f ft/s", AltosConvert.meters_to_feet(stats.state_baro_speed[Altos.ao_flight_main])));
+ String.format("%5.0f m/s", stats.state_speed[Altos.ao_flight_main]),
+ String.format("%5.0f ft/s", AltosConvert.meters_to_feet(stats.state_speed[Altos.ao_flight_main])));
new FlightStat(layout, y++, "Ascent time",
String.format("%6.1f s %s", stats.state_end[AltosLib.ao_flight_boost] - stats.state_start[AltosLib.ao_flight_boost],
AltosLib.state_name(Altos.ao_flight_boost)),
class Call extends FlightValue {
void show(AltosState state, AltosListenerState listener_state) {
- value.setText(state.data.callsign);
+ value.setText(state.callsign);
}
public Call (GridBagLayout layout, int x) {
super (layout, x, "Callsign");
class Serial extends FlightValue {
void show(AltosState state, AltosListenerState listener_state) {
- if (state.data.serial == AltosRecord.MISSING)
+ if (state.serial == AltosRecord.MISSING)
value.setText("none");
else
- value.setText(String.format("%d", state.data.serial));
+ value.setText(String.format("%d", state.serial));
}
public Serial (GridBagLayout layout, int x) {
super (layout, x, "Serial");
class Flight extends FlightValue {
void show(AltosState state, AltosListenerState listener_state) {
- if (state.data.flight == AltosRecord.MISSING)
+ if (state.flight == AltosRecord.MISSING)
value.setText("none");
else
- value.setText(String.format("%d", state.data.flight));
+ value.setText(String.format("%d", state.flight));
}
public Flight (GridBagLayout layout, int x) {
super (layout, x, "Flight");
class FlightState extends FlightValue {
void show(AltosState state, AltosListenerState listener_state) {
- value.setText(state.data.state());
+ value.setText(state.state_name());
}
public FlightState (GridBagLayout layout, int x) {
super (layout, x, "State");
class RSSI extends FlightValue {
void show(AltosState state, AltosListenerState listener_state) {
- value.setText(String.format("%d", state.data.rssi));
+ value.setText(String.format("%d", state.rssi()));
}
public RSSI (GridBagLayout layout, int x) {
super (layout, x, "RSSI");
flightStatus.show(state, listener_state);
flightInfo.show(state, listener_state);
- if (state.data.companion != null) {
+ if (state.companion != null) {
if (!has_companion) {
pane.add("Companion", companion);
has_companion= true;
public static final int data_pressure = 15;
public double x() throws AltosUIDataMissing {
- if (state.data.time < -2)
+ double time = state.time_since_boost();
+ if (time < -2)
throw new AltosUIDataMissing(-1);
- return state.data.time;
+ return time;
}
public double y(int index) throws AltosUIDataMissing {
y = state.temperature;
break;
case data_battery_voltage:
- y = state.battery;
+ y = state.battery_voltage;
break;
case data_drogue_voltage:
- y = state.drogue_sense;
+ y = state.apogee_voltage;
break;
case data_main_voltage:
- y = state.main_sense;
+ y = state.main_voltage;
break;
case data_rssi:
- y = state.data.rssi;
+ y = state.rssi;
break;
case data_gps_height:
y = state.gps_height;
public int id(int index) {
if (index == data_state) {
- int s = state.data.state;
+ int s = state.state;
if (s < Altos.ao_flight_boost || s > Altos.ao_flight_landed)
return -1;
return s;
public String id_name(int index) {
if (index == data_state)
- return state.data.state();
+ return state.state_name();
return "";
}
class AltosGraphIterator implements Iterator<AltosUIDataPoint> {
AltosGraphDataSet dataSet;
- Iterator<AltosRecord> iterator;
-
- AltosState state;
+ Iterator<AltosState> iterator;
public boolean hasNext() {
return iterator.hasNext();
}
public AltosUIDataPoint next() {
- state = new AltosState(iterator.next(), state);
+ AltosState state = iterator.next();
- if ((state.data.seen & AltosRecord.seen_flight) != 0) {
- if (dataSet.callsign == null && state.data.callsign != null)
- dataSet.callsign = state.data.callsign;
+ if (state.flight != AltosRecord.MISSING) {
+ if (dataSet.callsign == null && state.callsign != null)
+ dataSet.callsign = state.callsign;
- if (dataSet.serial == 0 && state.data.serial != 0)
- dataSet.serial = state.data.serial;
+ if (dataSet.serial == 0 && state.serial != 0)
+ dataSet.serial = state.serial;
- if (dataSet.flight == 0 && state.data.flight != 0)
- dataSet.flight = state.data.flight;
+ if (dataSet.flight == 0 && state.flight != 0)
+ dataSet.flight = state.flight;
}
return new AltosGraphDataPoint(state);
}
- public AltosGraphIterator (Iterator<AltosRecord> iterator, AltosGraphDataSet dataSet) {
+ public AltosGraphIterator (Iterator<AltosState> iterator, AltosGraphDataSet dataSet) {
this.iterator = iterator;
- this.state = null;
this.dataSet = dataSet;
}
AltosGraphDataSet dataSet;
public Iterator<AltosUIDataPoint> iterator() {
- return new AltosGraphIterator(dataSet.records.iterator(), dataSet);
+ return new AltosGraphIterator(dataSet.states.iterator(), dataSet);
}
public AltosGraphIterable(AltosGraphDataSet dataSet) {
String callsign;
int serial;
int flight;
- AltosRecordIterable records;
+ AltosStateIterable states;
public String name() {
if (callsign != null)
return new AltosGraphIterable(this);
}
- public AltosGraphDataSet (AltosRecordIterable records) {
- this.records = records;
+ public AltosGraphDataSet (AltosStateIterable states) {
+ this.states = states;
this.callsign = null;
this.serial = 0;
this.flight = 0;
AltosFlightStatsTable statsTable;
boolean has_gps;
- void fill_map(AltosRecordIterable records) {
+ void fill_map(AltosStateIterable states) {
boolean any_gps = false;
- for (AltosRecord record : records) {
- state = new AltosState(record, state);
+ for (AltosState state : states) {
if (state.gps != null && state.gps.locked && state.gps.nsat >= 4) {
if (map == null)
map = new AltosSiteMap();
}
}
- AltosGraphUI(AltosRecordIterable records, File file) throws InterruptedException, IOException {
+ AltosGraphUI(AltosStateIterable states, File file) throws InterruptedException, IOException {
super(file.getName());
state = null;
enable = new AltosUIEnable();
- stats = new AltosFlightStats(records);
- graphDataSet = new AltosGraphDataSet(records);
+ stats = new AltosFlightStats(states);
+ graphDataSet = new AltosGraphDataSet(states);
graph = new AltosGraph(enable, stats, graphDataSet);
pane.add("Flight Statistics", statsTable);
has_gps = false;
- fill_map(records);
+ fill_map(states);
if (has_gps)
pane.add("Map", map);
if (state.speed() != AltosRecord.MISSING)
info_add_row(0, "Speed", "%8.1f m/s", state.speed());
if (state.speed() != AltosRecord.MISSING)
- info_add_row(0, "Max Speed", "%8.1f m/s", state.max_accel_speed);
+ info_add_row(0, "Max Speed", "%8.1f m/s", state.max_speed);
if (state.temperature != AltosRecord.MISSING)
info_add_row(0, "Temperature", "%9.2f °C", state.temperature);
- if (state.battery != AltosRecord.MISSING)
- info_add_row(0, "Battery", "%9.2f V", state.battery);
- if (state.drogue_sense != AltosRecord.MISSING)
- info_add_row(0, "Drogue", "%9.2f V", state.drogue_sense);
- if (state.main_sense != AltosRecord.MISSING)
- info_add_row(0, "Main", "%9.2f V", state.main_sense);
+ if (state.battery_voltage != AltosRecord.MISSING)
+ info_add_row(0, "Battery", "%9.2f V", state.battery_voltage);
+ if (state.apogee_voltage != AltosRecord.MISSING)
+ info_add_row(0, "Drogue", "%9.2f V", state.apogee_voltage);
+ if (state.main_voltage != AltosRecord.MISSING)
+ info_add_row(0, "Main", "%9.2f V", state.main_voltage);
}
if (listener_state != null) {
info_add_row(0, "CRC Errors", "%6d", listener_state.crc_errors);
else
info_add_row(1, "GPS state", "wait (%d)",
state.gps_waiting);
- if (state.data.gps.locked)
+ if (state.gps.locked)
info_add_row(1, "GPS", " locked");
- else if (state.data.gps.connected)
+ else if (state.gps.connected)
info_add_row(1, "GPS", " unlocked");
else
info_add_row(1, "GPS", " missing");
- info_add_row(1, "Satellites", "%6d", state.data.gps.nsat);
+ info_add_row(1, "Satellites", "%6d", state.gps.nsat);
info_add_deg(1, "Latitude", state.gps.lat, 'N', 'S');
info_add_deg(1, "Longitude", state.gps.lon, 'E', 'W');
info_add_row(1, "GPS altitude", "%6d", state.gps.alt);
File name;
PrintStream out;
- int state = -1;
- AltosRecord prev = null;
+ int flight_state = -1;
+ AltosState prev = null;
double gps_start_altitude;
static final String[] kml_state_colors = {
"</Document>\n" +
"</kml>\n";
- void start (AltosRecord record) {
+ void start (AltosState record) {
out.printf(kml_header_start, record.flight, record.serial);
out.printf("Date: %04d-%02d-%02d\n",
record.gps.year, record.gps.month, record.gps.day);
boolean started = false;
- void state_start(AltosRecord record) {
- String state_name = Altos.state_name(record.state);
- out.printf(kml_style_start, state_name, kml_state_colors[record.state]);
+ void state_start(AltosState state) {
+ String state_name = Altos.state_name(state.state);
+ out.printf(kml_style_start, state_name, kml_state_colors[state.state]);
out.printf("\tState: %s\n", state_name);
out.printf("%s", kml_style_end);
out.printf(kml_placemark_start, state_name, state_name);
}
- void state_end(AltosRecord record) {
+ void state_end(AltosState state) {
out.printf("%s", kml_placemark_end);
}
- void coord(AltosRecord record) {
- AltosGPS gps = record.gps;
+ void coord(AltosState state) {
+ AltosGPS gps = state.gps;
double altitude;
- if (record.height() != AltosRecord.MISSING)
- altitude = record.height() + gps_start_altitude;
+ if (state.height != AltosRecord.MISSING)
+ altitude = state.height + gps_start_altitude;
else
altitude = gps.alt;
out.printf(kml_coord_fmt,
gps.lon, gps.lat,
altitude, (double) gps.alt,
- record.time, gps.nsat);
+ state.time, gps.nsat);
}
void end() {
}
}
- public void write(AltosRecord record) {
- AltosGPS gps = record.gps;
+ public void write(AltosState state) {
+ AltosGPS gps = state.gps;
if (gps == null)
return;
- if ((record.seen & (AltosRecord.seen_gps_lat)) == 0)
+ if (gps.lat == AltosRecord.MISSING)
return;
- if ((record.seen & (AltosRecord.seen_gps_lon)) == 0)
+ if (gps.lon == AltosRecord.MISSING)
return;
if (!started) {
- start(record);
+ start(state);
started = true;
gps_start_altitude = gps.alt;
}
- if (prev != null && prev.gps_sequence == record.gps_sequence)
+ if (prev != null && prev.gps_sequence == state.gps_sequence)
return;
- if (record.state != state) {
- state = record.state;
+ if (state.state != flight_state) {
+ flight_state = state.state;
if (prev != null) {
- coord(record);
+ coord(state);
state_end(prev);
}
- state_start(record);
+ state_start(state);
}
- coord(record);
- prev = record;
+ coord(state);
+ prev = state;
}
- public void write(AltosRecordIterable iterable) {
- for (AltosRecord record : iterable)
- write(record);
+ public void write(AltosStateIterable states) {
+ for (AltosState state : states) {
+ if ((state.set & AltosState.set_gps) != 0)
+ write(state);
+ }
}
public AltosKML(File in_name) throws FileNotFoundException {
if (file != null) {
String filename = file.getName();
try {
- AltosRecordIterable records = null;
+ AltosStateIterable states = null;
if (filename.endsWith("eeprom")) {
FileInputStream in = new FileInputStream(file);
- records = new AltosEepromIterable(in);
+ states = new AltosEepromFile(in);
} else if (filename.endsWith("telem")) {
FileInputStream in = new FileInputStream(file);
- records = new AltosTelemetryIterable(in);
- } else if (filename.endsWith("mega")) {
- FileInputStream in = new FileInputStream(file);
- records = new AltosEepromMegaIterable(in);
- } else if (filename.endsWith("mini")) {
- FileInputStream in = new FileInputStream(file);
- records = new AltosEepromMiniIterable(in);
+ states = null; // new AltosTelemetryIterable(in);
} else {
throw new FileNotFoundException(filename);
}
try {
- new AltosGraphUI(records, file);
+ new AltosGraphUI(states, file);
} catch (InterruptedException ie) {
} catch (IOException ie) {
}
class Battery extends LaunchStatus {
void show (AltosState state, AltosListenerState listener_state) {
- if (state == null || state.battery == AltosRecord.MISSING)
+ if (state == null || state.battery_voltage == AltosRecord.MISSING)
hide();
else {
- show("%4.2f V", state.battery);
- lights.set(state.battery > 3.7);
+ show("%4.2f V", state.battery_voltage);
+ lights.set(state.battery_voltage > 3.7);
}
}
public Battery (GridBagLayout layout, int y) {
class Apogee extends LaunchStatus {
void show (AltosState state, AltosListenerState listener_state) {
- if (state == null || state.drogue_sense == AltosRecord.MISSING)
+ if (state == null || state.apogee_voltage == AltosRecord.MISSING)
hide();
else {
- show("%4.2f V", state.drogue_sense);
- lights.set(state.drogue_sense > 3.2);
+ show("%4.2f V", state.apogee_voltage);
+ lights.set(state.apogee_voltage > 3.7);
}
}
public Apogee (GridBagLayout layout, int y) {
class Main extends LaunchStatus {
void show (AltosState state, AltosListenerState listener_state) {
- if (state == null || state.main_sense == AltosRecord.MISSING)
+ if (state == null || state.main_voltage == AltosRecord.MISSING)
hide();
else {
- show("%4.2f V", state.main_sense);
- lights.set(state.main_sense > 3.2);
+ show("%4.2f V", state.main_voltage);
+ lights.set(state.main_voltage > 3.7);
}
}
public Main (GridBagLayout layout, int y) {
class LoggingReady extends LaunchStatus {
void show (AltosState state, AltosListenerState listener_state) {
- if (state == null || state.data.flight == AltosRecord.MISSING) {
+ if (state == null || state.flight == AltosRecord.MISSING) {
hide();
} else {
- if (state.data.flight != 0) {
- if (state.data.state <= Altos.ao_flight_pad)
+ if (state.flight != 0) {
+ if (state.state <= Altos.ao_flight_pad)
show("Ready to record");
- else if (state.data.state < Altos.ao_flight_landed)
+ else if (state.state < Altos.ao_flight_landed)
show("Recording data");
else
show("Recorded data");
} else
show("Storage full");
- lights.set(state.data.flight != 0);
+ lights.set(state.flight != 0);
}
}
public LoggingReady (GridBagLayout layout, int y) {
try {
for (;;) {
try {
- AltosRecord record = reader.read();
- if (record == null)
+ AltosState state = reader.read();
+ if (state == null)
continue;
- if ((record.seen & AltosRecord.seen_flight) != 0) {
- final AltosScanResult result = new AltosScanResult(record.callsign,
- record.serial,
- record.flight,
+ if (state.flight != AltosRecord.MISSING) {
+ final AltosScanResult result = new AltosScanResult(state.callsign,
+ state.serial,
+ state.flight,
frequencies[frequency_index],
telemetry);
Runnable r = new Runnable() {
int last_state = -1;
public void show(double lat, double lon) {
- initMaps(lat, lon);
- scrollRocketToVisible(pt(lat, lon));
+ System.out.printf ("show %g %g\n", lat, lon);
+ return;
+// initMaps(lat, lon);
+// scrollRocketToVisible(pt(lat, lon));
}
public void show(final AltosState state, final AltosListenerState listener_state) {
// if insufficient gps data, nothing to update
- if (!state.gps.locked && state.gps.nsat < 4)
+ AltosGPS gps = state.gps;
+
+ if (gps == null)
+ return;
+
+ if (!gps.locked && gps.nsat < 4)
return;
if (!initialised) {
- if (state.pad_lat != 0 || state.pad_lon != 0) {
+ if (state.pad_lat != AltosRecord.MISSING && state.pad_lon != AltosRecord.MISSING) {
initMaps(state.pad_lat, state.pad_lon);
initialised = true;
- } else if (state.gps.lat != 0 || state.gps.lon != 0) {
- initMaps(state.gps.lat, state.gps.lon);
+ } else if (gps.lat != AltosRecord.MISSING && gps.lon != AltosRecord.MISSING) {
+ initMaps(gps.lat, gps.lon);
initialised = true;
} else {
return;
}
}
- final Point2D.Double pt = pt(state.gps.lat, state.gps.lon);
+ final Point2D.Double pt = pt(gps.lat, gps.lon);
if (last_pt == pt && last_state == state.state)
return;
AltosDataChooser chooser = new AltosDataChooser(
AltosUI.this);
- AltosRecordIterable iterable = chooser.runDialog();
- if (iterable != null) {
- AltosFlightReader reader = new AltosReplayReader(iterable.iterator(),
+ Iterable<AltosState> states = chooser.runDialog();
+ if (states != null) {
+ AltosFlightReader reader = new AltosReplayReader(states.iterator(),
chooser.file());
new AltosFlightUI(voice, reader);
}
private void ExportData() {
AltosDataChooser chooser;
chooser = new AltosDataChooser(this);
- AltosRecordIterable record_reader = chooser.runDialog();
- if (record_reader == null)
+ AltosStateIterable states = chooser.runDialog();
+ if (states == null)
return;
- new AltosCSVUI(AltosUI.this, record_reader, chooser.file());
+ new AltosCSVUI(AltosUI.this, states, chooser.file());
}
/* Load a flight log CSV file and display a pretty graph.
private void GraphData() {
AltosDataChooser chooser;
chooser = new AltosDataChooser(this);
- AltosRecordIterable record_reader = chooser.runDialog();
- if (record_reader == null)
+ AltosStateIterable states = chooser.runDialog();
+ if (states == null)
return;
try {
- new AltosGraphUI(record_reader, chooser.file());
+ new AltosGraphUI(states, chooser.file());
} catch (InterruptedException ie) {
} catch (IOException ie) {
}
}
}
- static AltosRecordIterable open_logfile(File file) {
+ static AltosStateIterable open_logfile(File file) {
try {
FileInputStream in;
in = new FileInputStream(file);
if (file.getName().endsWith("eeprom"))
- return new AltosEepromIterable(in);
- else if (file.getName().endsWith("mega"))
- return new AltosEepromMegaIterable(in);
- else if (file.getName().endsWith("mini"))
- return new AltosEepromMiniIterable(in);
+ return new AltosEepromFile(in);
else
- return new AltosTelemetryIterable(in);
+ return new AltosTelemetryFile(in);
} catch (FileNotFoundException fe) {
System.out.printf("%s\n", fe.getMessage());
return null;
static final int process_graph = 3;
static final int process_replay = 4;
static final int process_summary = 5;
+ static final int process_cat = 6;
static boolean process_csv(File input) {
- AltosRecordIterable iterable = open_logfile(input);
- if (iterable == null)
+ AltosStateIterable states = open_logfile(input);
+ if (states == null)
return false;
File output = Altos.replace_extension(input,".csv");
AltosWriter writer = open_csv(output);
if (writer == null)
return false;
- writer.write(iterable);
+ writer.write(states);
writer.close();
}
return true;
}
static boolean process_kml(File input) {
- AltosRecordIterable iterable = open_logfile(input);
- if (iterable == null)
+ AltosStateIterable states = open_logfile(input);
+ if (states == null)
return false;
File output = Altos.replace_extension(input,".kml");
AltosWriter writer = open_kml(output);
if (writer == null)
return false;
- writer.write(iterable);
+ writer.write(states);
writer.close();
return true;
}
}
- static AltosRecordIterable record_iterable(File file) {
+ static AltosStateIterable record_iterable(File file) {
FileInputStream in;
try {
in = new FileInputStream(file);
System.out.printf("Failed to open file '%s'\n", file);
return null;
}
- AltosRecordIterable recs;
- //AltosReplayReader reader;
if (file.getName().endsWith("eeprom")) {
- recs = new AltosEepromIterable(in);
- } else if (file.getName().endsWith("mega")) {
- recs = new AltosEepromMegaIterable(in);
- } else if (file.getName().endsWith("mini")) {
- recs = new AltosEepromMiniIterable(in);
+ return new AltosEepromFile(in);
} else {
- recs = new AltosTelemetryIterable(in);
+ return new AltosTelemetryFile(in);
}
- return recs;
}
static AltosReplayReader replay_file(File file) {
- AltosRecordIterable recs = record_iterable(file);
- if (recs == null)
+ AltosStateIterable states = record_iterable(file);
+ if (states == null)
return null;
- return new AltosReplayReader(recs.iterator(), file);
+ return new AltosReplayReader(states.iterator(), file);
}
static boolean process_replay(File file) {
}
static boolean process_graph(File file) {
- AltosRecordIterable recs = record_iterable(file);
- if (recs == null)
+ AltosStateIterable states = record_iterable(file);
+ if (states == null)
return false;
try {
- new AltosGraphUI(recs, file);
+ new AltosGraphUI(states, file);
return true;
} catch (InterruptedException ie) {
} catch (IOException ie) {
}
static boolean process_summary(File file) {
- AltosRecordIterable iterable = record_iterable(file);
- if (iterable == null)
+ AltosStateIterable states = record_iterable(file);
+ if (states == null)
return false;
try {
- AltosFlightStats stats = new AltosFlightStats(iterable);
+ AltosFlightStats stats = new AltosFlightStats(states);
if (stats.serial > 0)
System.out.printf("Serial: %5d\n", stats.serial);
if (stats.flight > 0)
AltosConvert.meters_to_g(stats.max_acceleration));
}
System.out.printf("Drogue rate: %6.0f m/s %6.0f ft/s\n",
- stats.state_baro_speed[Altos.ao_flight_drogue],
- AltosConvert.meters_to_feet(stats.state_baro_speed[Altos.ao_flight_drogue]));
+ stats.state_speed[Altos.ao_flight_drogue],
+ AltosConvert.meters_to_feet(stats.state_speed[Altos.ao_flight_drogue]));
System.out.printf("Main rate: %6.0f m/s %6.0f ft/s\n",
- stats.state_baro_speed[Altos.ao_flight_main],
- AltosConvert.meters_to_feet(stats.state_baro_speed[Altos.ao_flight_main]));
+ stats.state_speed[Altos.ao_flight_main],
+ AltosConvert.meters_to_feet(stats.state_speed[Altos.ao_flight_main]));
System.out.printf("Flight time: %6.0f s\n",
stats.state_end[Altos.ao_flight_main] -
stats.state_start[Altos.ao_flight_boost]);
return false;
}
+ static boolean process_cat(File file) {
+ try {
+ AltosStateIterable eef = record_iterable(file);
+
+ System.out.printf ("process cat\n");
+ for (AltosState state : eef) {
+ if ((state.set & AltosState.set_gps) != 0)
+ System.out.printf ("time %g lat %g lon %g alt %g\n",
+ state.time_since_boost(),
+ state.gps.lat,
+ state.gps.lon,
+ state.gps.alt);
+ }
+
+ } catch (Exception e) {
+ System.out.printf("Failed to open file '%s'\n", file);
+ return false;
+ }
+ return true;
+ }
+
public static void help(int code) {
System.out.printf("Usage: altosui [OPTION]... [FILE]...\n");
System.out.printf(" Options:\n");
process = process_graph;
else if (args[i].equals("--summary"))
process = process_summary;
+ else if (args[i].equals("--cat"))
+ process = process_cat;
else if (args[i].startsWith("--"))
help(1);
else {
if (!process_summary(file))
++errors;
break;
+ case process_cat:
+ if (!process_cat(file))
+ ++errors;
}
}
}
public interface AltosWriter {
- public void write(AltosRecord record);
+ public void write(AltosState state);
- public void write(AltosRecordIterable iterable);
+ public void write(AltosStateIterable states);
public void close();
}
#define AO_LOG_FORMAT_TELEMETRY 3 /* 32 byte ao_telemetry records */
#define AO_LOG_FORMAT_TELESCIENCE 4 /* 32 byte typed telescience records */
#define AO_LOG_FORMAT_TELEMEGA 5 /* 32 byte typed telemega records */
-#define AO_LOG_FORMAT_MINI 6 /* 16-byte MS5607 baro only */
+#define AO_LOG_FORMAT_EASYMINI 6 /* 16-byte MS5607 baro only, 3.0V supply */
#define AO_LOG_FORMAT_TELEMETRUM 7 /* 16-byte typed telemetrum records */
+#define AO_LOG_FORMAT_TELEMINI 8 /* 16-byte MS5607 baro only, 3.3V supply */
#define AO_LOG_FORMAT_NONE 127 /* No log at all */
extern __code uint8_t ao_log_format;
static __xdata uint8_t ao_log_mutex;
static __xdata struct ao_log_mini log;
-__code uint8_t ao_log_format = AO_LOG_FORMAT_MINI;
+__code uint8_t ao_log_format = AO_LOG_FORMAT;
static uint8_t
ao_log_csum(__xdata uint8_t *b) __reentrant
#define PACKET_HAS_SLAVE 0
+#define AO_LOG_FORMAT AO_LOG_FORMAT_EASYMINI
+
/* USART */
#define HAS_SERIAL 0
#define AO_IGNITER_CHARGE_TIME AO_MS_TO_TICKS(2000)
#define AO_SEND_MINI
+#define AO_LOG_FORMAT AO_LOG_FORMAT_TELEMINI
/*
* ADC