From: Keith Packard Date: Thu, 23 Feb 2012 04:00:48 +0000 (+1300) Subject: Move altoslib sources to top dir X-Git-Tag: 1.0.9.6~95 X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=commitdiff_plain;h=69e6df07976a56b49e07c242cd6e5b2cbd2a578d Move altoslib sources to top dir No sense having them live deep in the file system. Signed-off-by: Keith Packard --- diff --git a/altoslib/AltosCRCException.java b/altoslib/AltosCRCException.java new file mode 100644 index 00000000..101c5363 --- /dev/null +++ b/altoslib/AltosCRCException.java @@ -0,0 +1,26 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +public class AltosCRCException extends Exception { + public int rssi; + + public AltosCRCException (int in_rssi) { + rssi = in_rssi; + } +} diff --git a/altoslib/AltosConfigData.java b/altoslib/AltosConfigData.java new file mode 100644 index 00000000..4ad4e58a --- /dev/null +++ b/altoslib/AltosConfigData.java @@ -0,0 +1,184 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.*; +import org.altusmetrum.AltosLib.*; + +public class AltosConfigData implements Iterable { + + /* Version information */ + public String manufacturer; + public String product; + public String version; + public int log_format; + public int serial; + + /* Strings returned */ + public LinkedList lines; + + /* Config information */ + public int config_major; + public int config_minor; + public int main_deploy; + public int apogee_delay; + public int radio_channel; + public int radio_setting; + public int radio_frequency; + public String callsign; + public int accel_cal_plus, accel_cal_minus; + public int radio_calibration; + public int flight_log_max; + public int ignite_mode; + public int stored_flight; + public int storage_size; + public int storage_erase_unit; + + public static String get_string(String line, String label) throws ParseException { + if (line.startsWith(label)) { + String quoted = line.substring(label.length()).trim(); + + if (quoted.startsWith("\"")) + quoted = quoted.substring(1); + if (quoted.endsWith("\"")) + quoted = quoted.substring(0,quoted.length()-1); + return quoted; + } + throw new ParseException("mismatch", 0); + } + + public static int get_int(String line, String label) throws NumberFormatException, ParseException { + if (line.startsWith(label)) { + String tail = line.substring(label.length()).trim(); + String[] tokens = tail.split("\\s+"); + if (tokens.length > 0) + return Integer.parseInt(tokens[0]); + } + throw new ParseException("mismatch", 0); + } + + public Iterator iterator() { + return lines.iterator(); + } + + public int log_available() { + switch (log_format) { + case AltosLib.AO_LOG_FORMAT_TINY: + if (stored_flight == 0) + return 1; + return 0; + default: + if (flight_log_max <= 0) + return 1; + int log_space = storage_size - storage_erase_unit; + int log_used = stored_flight * flight_log_max; + + if (log_used >= log_space) + return 0; + return (log_space - log_used) / flight_log_max; + } + } + + int[] parse_version(String v) { + String[] parts = v.split("\\."); + int r[] = new int[parts.length]; + + for (int i = 0; i < parts.length; i++) { + try { + r[i] = Altos.fromdec(parts[i]); + } catch (NumberFormatException n) { + r[i] = 0; + } + } + + return r; + } + + public int compare_version(String other) { + int[] me = parse_version(version); + int[] them = parse_version(other); + + int l = Math.min(me.length, them.length); + + for (int i = 0; i < l; i++) { + int d = me[i] - them[i]; + if (d != 0) + return d; + } + if (me.length > l) + return 1; + if (them.length > l) + return -1; + return 0; + } + + public AltosConfigData(AltosLink link) throws InterruptedException, TimeoutException { + link.printf("c s\np\nf\nl\nv\n"); + lines = new LinkedList(); + radio_setting = 0; + radio_frequency = 0; + stored_flight = 0; + for (;;) { + String line = link.get_reply(); + if (line == null) + throw new TimeoutException(); + if (line.contains("Syntax error")) + continue; + lines.add(line); + try { serial = get_int(line, "serial-number"); } catch (Exception e) {} + try { log_format = get_int(line, "log-format"); } catch (Exception e) {} + try { main_deploy = get_int(line, "Main deploy:"); } catch (Exception e) {} + try { apogee_delay = get_int(line, "Apogee delay:"); } catch (Exception e) {} + try { radio_channel = get_int(line, "Radio channel:"); } catch (Exception e) {} + try { radio_setting = get_int(line, "Radio setting:"); } catch (Exception e) {} + try { + radio_frequency = get_int(line, "Frequency:"); + if (radio_frequency < 0) + radio_frequency = 434550; + } catch (Exception e) {} + try { + if (line.startsWith("Accel cal")) { + String[] bits = line.split("\\s+"); + if (bits.length >= 6) { + accel_cal_plus = Integer.parseInt(bits[3]); + accel_cal_minus = Integer.parseInt(bits[5]); + } + } + } catch (Exception e) {} + try { radio_calibration = get_int(line, "Radio cal:"); } catch (Exception e) {} + try { flight_log_max = get_int(line, "Max flight log:"); } catch (Exception e) {} + try { ignite_mode = get_int(line, "Ignite mode:"); } catch (Exception e) {} + try { callsign = get_string(line, "Callsign:"); } catch (Exception e) {} + try { version = get_string(line,"software-version"); } catch (Exception e) {} + try { product = get_string(line,"product"); } catch (Exception e) {} + + try { get_int(line, "flight"); stored_flight++; } catch (Exception e) {} + try { storage_size = get_int(line, "Storage size:"); } catch (Exception e) {} + try { storage_erase_unit = get_int(line, "Storage erase unit"); } catch (Exception e) {} + + /* signals the end of the version info */ + if (line.startsWith("software-version")) + break; + } + } + +} \ No newline at end of file diff --git a/altoslib/AltosConvert.java b/altoslib/AltosConvert.java new file mode 100644 index 00000000..3527b575 --- /dev/null +++ b/altoslib/AltosConvert.java @@ -0,0 +1,259 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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. + */ + +/* + * Sensor data conversion functions + */ +package org.altusmetrum.AltosLib; + +public class AltosConvert { + /* + * Pressure Sensor Model, version 1.1 + * + * written by Holly Grimes + * + * Uses the International Standard Atmosphere as described in + * "A Quick Derivation relating altitude to air pressure" (version 1.03) + * from the Portland State Aerospace Society, except that the atmosphere + * is divided into layers with each layer having a different lapse rate. + * + * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007 + * at site MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */ + return 0; + + /* calculate the base temperature and pressure for the atmospheric layer + associated with the inputted altitude */ + for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) { + delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + base_pressure *= Math.exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + base_pressure *= Math.pow(base, exponent); + } + base_temperature += delta_z * lapse_rate[layer_number]; + } + + /* calculate the pressure at the inputted altitude */ + delta_z = altitude - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + pressure = base_pressure * Math.exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + pressure = base_pressure * Math.pow(base, exponent); + } + + return pressure; + } + + +/* outputs the altitude associated with the given pressure. the altitude + returned is measured with respect to the mean sea level */ + public static double + pressure_to_altitude(double pressure) + { + + double next_base_temperature = LAYER0_BASE_TEMPERATURE; + double next_base_pressure = LAYER0_BASE_PRESSURE; + + double altitude; + double base_pressure; + double base_temperature; + double base; /* base for function to determine base pressure of next layer */ + double exponent; /* exponent for function to determine base pressure + of next layer */ + double coefficient; + int layer_number; /* identifies layer in the atmosphere */ + int delta_z; /* difference between two altitudes */ + + if (pressure < 0) /* illegal pressure */ + return -1; + if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */ + return MAXIMUM_ALTITUDE; + + /* calculate the base temperature and pressure for the atmospheric layer + associated with the inputted pressure. */ + layer_number = -1; + do { + layer_number++; + base_pressure = next_base_pressure; + base_temperature = next_base_temperature; + delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + next_base_pressure *= Math.exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + next_base_pressure *= Math.pow(base, exponent); + } + next_base_temperature += delta_z * lapse_rate[layer_number]; + } + while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure); + + /* calculate the altitude associated with the inputted pressure */ + if (lapse_rate[layer_number] == 0.0) { + coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION) + * base_temperature; + altitude = base_altitude[layer_number] + + coefficient * Math.log(pressure / base_pressure); + } + else { + base = pressure / base_pressure; + exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number] + / GRAVITATIONAL_ACCELERATION; + coefficient = base_temperature / lapse_rate[layer_number]; + altitude = base_altitude[layer_number] + + coefficient * (Math.pow(base, exponent) - 1); + } + + return altitude; + } + + public static double + cc_battery_to_voltage(double battery) + { + return battery / 32767.0 * 5.0; + } + + public static double + cc_ignitor_to_voltage(double ignite) + { + return ignite / 32767 * 15.0; + } + + public static double radio_to_frequency(int freq, int setting, int cal, int channel) { + double f; + + if (freq > 0) + f = freq / 1000.0; + else { + if (setting <= 0) + setting = cal; + f = 434.550 * setting / cal; + /* Round to nearest 50KHz */ + f = Math.floor (20.0 * f + 0.5) / 20.0; + } + return f + channel * 0.100; + } + + public static int radio_frequency_to_setting(double frequency, int cal) { + double set = frequency / 434.550 * cal; + + return (int) Math.floor (set + 0.5); + } + + public static int radio_frequency_to_channel(double frequency) { + int channel = (int) Math.floor ((frequency - 434.550) / 0.100 + 0.5); + + if (channel < 0) + channel = 0; + if (channel > 9) + channel = 9; + return channel; + } + + public static double radio_channel_to_frequency(int channel) { + return 434.550 + channel * 0.100; + } + + public static int[] ParseHex(String line) { + String[] tokens = line.split("\\s+"); + int[] array = new int[tokens.length]; + + for (int i = 0; i < tokens.length; i++) + try { + array[i] = Integer.parseInt(tokens[i], 16); + } catch (NumberFormatException ne) { + return null; + } + return array; + } + + public static double meters_to_feet(double meters) { + return meters * (100 / (2.54 * 12)); + } + + public static double meters_to_mach(double meters) { + return meters / 343; /* something close to mach at usual rocket sites */ + } + + public static double meters_to_g(double meters) { + return meters / 9.80665; + } + + public static int checksum(int[] data, int start, int length) { + int csum = 0x5a; + for (int i = 0; i < length; i++) + csum += data[i + start]; + return csum & 0xff; + } +} diff --git a/altoslib/AltosEepromChunk.java b/altoslib/AltosEepromChunk.java new file mode 100644 index 00000000..6d889723 --- /dev/null +++ b/altoslib/AltosEepromChunk.java @@ -0,0 +1,102 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.concurrent.*; + +public class AltosEepromChunk { + + public static final int chunk_size = 256; + public static final int per_line = 8; + + public int data[]; + public int address; + public ParseException parse_exception = null; + + int[] ParseHex(String line) { + String[] tokens = line.split("\\s+"); + int[] array = new int[tokens.length]; + + for (int i = 0; i < tokens.length; i++) + try { + array[i] = Integer.parseInt(tokens[i], 16); + } catch (NumberFormatException ne) { + return null; + } + return array; + } + + public int data(int offset) { + return data[offset]; + } + + public int data16(int offset) { + return data[offset] | (data[offset + 1] << 8); + } + + public int data32(int offset) { + return data[offset] | (data[offset + 1] << 8) | + (data[offset+2] << 16) | (data[offset+3] << 24); + } + + public boolean erased(int start, int len) { + for (int i = 0; i < len; i++) + if (data[start+i] != 0xff) + return false; + return true; + } + + public AltosEepromChunk(AltosLink link, int block, boolean flush) + throws TimeoutException, InterruptedException { + + int offset; + + data = new int[chunk_size]; + address = block * chunk_size; + if (flush) + link.flush_input(); + link.printf("e %x\n", block); + + for (offset = 0; offset < chunk_size; offset += per_line) { + try { + String line = link.get_reply(5000); + + if (line == null) + throw new TimeoutException(); + + int[] values = ParseHex(line); + + if (values == null || values.length != per_line + 1) + throw new ParseException(String.format("invalid line %s", line), 0); + if (values[0] != offset) + throw new ParseException(String.format("data address out of sync at 0x%x", + address + offset), 0); + for (int i = 0; i < per_line; i++) + data[offset + i] = values[1 + i]; + } catch (ParseException pe) { + for (int i = 0; i < per_line; i++) + data[offset + i] = 0xff; + if (parse_exception == null) + parse_exception = pe; + } + } + } +} \ No newline at end of file diff --git a/altoslib/AltosEepromIterable.java b/altoslib/AltosEepromIterable.java new file mode 100644 index 00000000..f1397c7b --- /dev/null +++ b/altoslib/AltosEepromIterable.java @@ -0,0 +1,475 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +/* + * AltosRecords with an index field so they can be sorted by tick while preserving + * the original ordering for elements with matching ticks + */ +class AltosOrderedRecord extends AltosEepromRecord implements Comparable { + + public int index; + + public AltosOrderedRecord(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 AltosOrderedRecord(int in_cmd, int in_tick, int in_a, int in_b, int in_index) { + super(in_cmd, in_tick, in_a, in_b); + index = in_index; + } + + public String toString() { + return String.format("%d.%d %04x %04x %04x", + cmd, index, tick, a, b); + } + + public int compareTo(AltosOrderedRecord o) { + int tick_diff = tick - o.tick; + if (tick_diff != 0) + return tick_diff; + return index - o.index; + } +} + +public class AltosEepromIterable 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; + + AltosEepromRecord flight_record; + AltosEepromRecord gps_date_record; + + TreeSet records; + + LinkedList 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(AltosRecord state, AltosEepromRecord record, EepromState eeprom) { + state.tick = record.tick; + switch (record.cmd) { + case AltosLib.AO_LOG_FLIGHT: + eeprom.seen |= seen_flight; + state.ground_accel = record.a; + state.flight_accel = record.a; + state.flight = record.b; + eeprom.boost_tick = record.tick; + break; + case 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 & 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_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 |= seen_sensor; + break; + case AltosLib.AO_LOG_TEMP_VOLT: + state.temp = record.a; + state.batt = record.b; + eeprom.seen |= seen_temp_volt; + break; + case AltosLib.AO_LOG_DEPLOY: + state.drogue = record.a; + state.main = record.b; + eeprom.seen |= 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; + 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.new_gps = true; + has_gps = true; + break; + case AltosLib.AO_LOG_GPS_LAT: + int lat32 = record.a | (record.b << 16); + state.gps.lat = (double) lat32 / 1e7; + break; + case AltosLib.AO_LOG_GPS_LON: + int lon32 = record.a | (record.b << 16); + state.gps.lon = (double) lon32 / 1e7; + break; + case AltosLib.AO_LOG_GPS_ALT: + 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; + state.gps.add_sat(svid, c_n0); + } + break; + case AltosLib.AO_LOG_GPS_DATE: + state.gps.year = (record.a & 0xff) + 2000; + state.gps.month = record.a >> 8; + state.gps.day = record.b & 0xff; + break; + + case 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 make_list() { + LinkedList list = new LinkedList(); + Iterator iterator = records.iterator(); + AltosOrderedRecord record = null; + AltosRecord state = new AltosRecord(); + 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) { + AltosRecord r = new AltosRecord(state); + r.time = (r.tick - eeprom.boost_tick) / 100.0; + list.add(r); + } + update_state(state, record, eeprom); + } + AltosRecord r = new AltosRecord(state); + r.time = (r.tick - eeprom.boost_tick) / 100.0; + list.add(r); + return list; + } + + public Iterator 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 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 Altos.AO_LOG_BARO_RESERVED: + out.printf ("# Baro reserved: %d\n", record.a); + break; + case Altos.AO_LOG_BARO_SENS: + out.printf ("# Baro sens: %d\n", record.a); + break; + case Altos.AO_LOG_BARO_OFF: + out.printf ("# Baro off: %d\n", record.a); + break; + case Altos.AO_LOG_BARO_TCS: + out.printf ("# Baro tcs: %d\n", record.a); + break; + case Altos.AO_LOG_BARO_TCO: + out.printf ("# Baro tco: %d\n", record.a); + break; + case Altos.AO_LOG_BARO_TREF: + out.printf ("# Baro tref: %d\n", record.a); + break; + case Altos.AO_LOG_BARO_TEMPSENS: + out.printf ("# Baro tempsens: %d\n", record.a); + break; + case Altos.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 AltosEepromIterable (FileInputStream input) { + records = new TreeSet(); + + 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 = AltosRecord.gets(input); + if (line == null) + break; + AltosOrderedRecord record = new AltosOrderedRecord(line, index++, prev_tick, prev_tick_valid); + if (record == null) + break; + 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 iterator = records.iterator(); + while (iterator.hasNext()) { + AltosOrderedRecord old = iterator.next(); + if (old.cmd == AltosLib.AO_LOG_GPS_TIME && + old.a == -1 && old.b == -1) + { + update_time(record, old); + } + } + missing_time = false; + } + } + + if (record.cmd == AltosLib.AO_LOG_GPS_LAT) { + if (last_gps_time == null || last_gps_time.tick != record.tick) { + AltosOrderedRecord add_gps_time = new AltosOrderedRecord(AltosLib.AO_LOG_GPS_TIME, + record.tick, + -1, -1, index-1); + if (last_gps_time != null) + update_time(last_gps_time, add_gps_time); + else + missing_time = true; + + records.add(add_gps_time); + record.index = index++; + } + } + records.add(record); + + /* Bail after reading the 'landed' record; we're all done */ + if (record.cmd == AltosLib.AO_LOG_STATE && + record.a == AltosLib.ao_flight_landed) + break; + } + } catch (IOException io) { + } catch (ParseException pe) { + } + try { + input.close(); + } catch (IOException ie) { + } + } +} diff --git a/altoslib/AltosEepromLog.java b/altoslib/AltosEepromLog.java new file mode 100644 index 00000000..7fca4bd9 --- /dev/null +++ b/altoslib/AltosEepromLog.java @@ -0,0 +1,100 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.*; + +/* + * Extract a bit of information from an eeprom-stored flight log. + */ + +public class AltosEepromLog { + public int serial; + public boolean has_flight; + public int flight; + public int start_block; + public int end_block; + + public int year, month, day; + + public boolean selected; + + public AltosEepromLog(AltosConfigData config_data, + AltosLink link, + int in_flight, int in_start_block, + int in_end_block) + throws InterruptedException, TimeoutException { + + int block; + boolean has_date = false; + + flight = in_flight; + if (flight != 0) + has_flight = true; + start_block = in_start_block; + end_block = in_end_block; + serial = config_data.serial; + + /* + * Select all flights for download + */ + selected = true; + + /* + * Look in TeleMetrum log data for date + */ + if (config_data.log_format == AltosLib.AO_LOG_FORMAT_UNKNOWN || + config_data.log_format == AltosLib.AO_LOG_FORMAT_FULL) + { + /* + * Only look in the first two blocks so that this + * process doesn't take a long time + */ + if (in_end_block > in_start_block + 2) + in_end_block = in_start_block + 2; + + for (block = in_start_block; block < in_end_block; block++) { + AltosEepromChunk eechunk = new AltosEepromChunk(link, block, block == in_start_block); + + for (int i = 0; i < eechunk.chunk_size; i += AltosEepromRecord.record_length) { + try { + AltosEepromRecord r = new AltosEepromRecord(eechunk, i); + + if (r.cmd == AltosLib.AO_LOG_FLIGHT) { + flight = r.b; + has_flight = true; + } + if (r.cmd == AltosLib.AO_LOG_GPS_DATE) { + year = 2000 + (r.a & 0xff); + month = (r.a >> 8) & 0xff; + day = (r.b & 0xff); + has_date = true; + } + } catch (ParseException pe) { + } + } + if (has_date && has_flight) + break; + } + } + } +} diff --git a/altoslib/AltosEepromRecord.java b/altoslib/AltosEepromRecord.java new file mode 100644 index 00000000..1e845f46 --- /dev/null +++ b/altoslib/AltosEepromRecord.java @@ -0,0 +1,139 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.*; + +public class AltosEepromRecord { + public int cmd; + public int tick; + public int a; + public int b; + public String data; + public boolean tick_valid; + + public static final int record_length = 8; + + public AltosEepromRecord (AltosEepromChunk chunk, int start) throws ParseException { + + cmd = chunk.data(start); + tick_valid = true; + + tick_valid = !chunk.erased(start, record_length); + if (tick_valid) { + if (AltosConvert.checksum(chunk.data, start, record_length) != 0) + throw new ParseException(String.format("invalid checksum at 0x%x", + chunk.address + start), 0); + } else { + cmd = AltosLib.AO_LOG_INVALID; + } + + tick = chunk.data16(start + 2); + a = chunk.data16(start + 4); + b = chunk.data16(start + 6); + + data = null; + } + + public AltosEepromRecord (String line) { + tick_valid = false; + tick = 0; + a = 0; + b = 0; + data = null; + if (line == null) { + cmd = AltosLib.AO_LOG_INVALID; + data = ""; + } else { + try { + String[] tokens = line.split("\\s+"); + + if (tokens[0].length() == 1) { + if (tokens.length != 4) { + cmd = AltosLib.AO_LOG_INVALID; + data = line; + } else { + cmd = tokens[0].codePointAt(0); + tick = Integer.parseInt(tokens[1],16); + tick_valid = true; + a = Integer.parseInt(tokens[2],16); + b = Integer.parseInt(tokens[3],16); + } + } else if (tokens[0].equals("Config") && tokens[1].equals("version:")) { + cmd = AltosLib.AO_LOG_CONFIG_VERSION; + data = tokens[2]; + } else if (tokens[0].equals("Main") && tokens[1].equals("deploy:")) { + cmd = AltosLib.AO_LOG_MAIN_DEPLOY; + a = Integer.parseInt(tokens[2]); + } else if (tokens[0].equals("Apogee") && tokens[1].equals("delay:")) { + cmd = AltosLib.AO_LOG_APOGEE_DELAY; + a = Integer.parseInt(tokens[2]); + } else if (tokens[0].equals("Radio") && tokens[1].equals("channel:")) { + cmd = AltosLib.AO_LOG_RADIO_CHANNEL; + a = Integer.parseInt(tokens[2]); + } else if (tokens[0].equals("Callsign:")) { + cmd = AltosLib.AO_LOG_CALLSIGN; + data = tokens[1].replaceAll("\"",""); + } else if (tokens[0].equals("Accel") && tokens[1].equals("cal")) { + cmd = AltosLib.AO_LOG_ACCEL_CAL; + a = Integer.parseInt(tokens[3]); + b = Integer.parseInt(tokens[5]); + } else if (tokens[0].equals("Radio") && tokens[1].equals("cal:")) { + cmd = AltosLib.AO_LOG_RADIO_CAL; + a = Integer.parseInt(tokens[2]); + } else if (tokens[0].equals("Max") && tokens[1].equals("flight") && tokens[2].equals("log:")) { + cmd = AltosLib.AO_LOG_MAX_FLIGHT_LOG; + a = Integer.parseInt(tokens[3]); + } else if (tokens[0].equals("manufacturer")) { + cmd = AltosLib.AO_LOG_MANUFACTURER; + data = tokens[1]; + } else if (tokens[0].equals("product")) { + cmd = AltosLib.AO_LOG_PRODUCT; + data = tokens[1]; + } else if (tokens[0].equals("serial-number")) { + cmd = AltosLib.AO_LOG_SERIAL_NUMBER; + a = Integer.parseInt(tokens[1]); + } else if (tokens[0].equals("log-format")) { + cmd = AltosLib.AO_LOG_LOG_FORMAT; + a = Integer.parseInt(tokens[1]); + } else if (tokens[0].equals("software-version")) { + cmd = AltosLib.AO_LOG_SOFTWARE_VERSION; + data = tokens[1]; + } else { + cmd = AltosLib.AO_LOG_INVALID; + data = line; + } + } catch (NumberFormatException ne) { + cmd = AltosLib.AO_LOG_INVALID; + data = line; + } + } + } + + public AltosEepromRecord(int in_cmd, int in_tick, int in_a, int in_b) { + tick_valid = true; + cmd = in_cmd; + tick = in_tick; + a = in_a; + b = in_b; + } +} diff --git a/altoslib/AltosEepromTeleScience.java b/altoslib/AltosEepromTeleScience.java new file mode 100644 index 00000000..1758fa34 --- /dev/null +++ b/altoslib/AltosEepromTeleScience.java @@ -0,0 +1,59 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.*; + +public class AltosEepromTeleScience { + public int type; + public int tick; + public int tm_state; + public int tm_tick; + public int[] data; + public boolean valid; + + public static final int AO_LOG_TELESCIENCE_START = 's'; + public static final int AO_LOG_TELESCIENCE_DATA = 'd'; + + static final int max_data = 12; + public static final int record_length = 32; + + public AltosEepromTeleScience (AltosEepromChunk chunk, int start) throws ParseException { + type = 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 { + type = AltosLib.AO_LOG_INVALID; + } + + tick = chunk.data16(start+2); + tm_tick = chunk.data16(start+4); + tm_state = chunk.data(start+6); + data = new int[max_data]; + for (int i = 0; i < max_data; i++) + data[i] = chunk.data16(start + 8 + i * 2); + } +} diff --git a/altoslib/AltosFile.java b/altoslib/AltosFile.java new file mode 100644 index 00000000..d2e4f2f7 --- /dev/null +++ b/altoslib/AltosFile.java @@ -0,0 +1,44 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.lang.*; +import java.io.File; +import java.util.*; + +public class AltosFile extends File { + + 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)); + } + + public AltosFile(int serial, int flight, String extension) { + this(Calendar.getInstance().get(Calendar.YEAR), + Calendar.getInstance().get(Calendar.MONTH) + 1, + Calendar.getInstance().get(Calendar.DAY_OF_MONTH), + serial, + flight, + extension); + } + + public AltosFile(AltosRecord telem) { + this(telem.serial, telem.flight, "telem"); + } +} diff --git a/altoslib/AltosFlightReader.java b/altoslib/AltosFlightReader.java new file mode 100644 index 00000000..3fdea469 --- /dev/null +++ b/altoslib/AltosFlightReader.java @@ -0,0 +1,49 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.lang.*; +import java.text.*; +import java.io.*; +import java.util.concurrent.*; + +public class AltosFlightReader { + public String name; + + public int serial; + + public void init() { } + + public AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; } + + public void close(boolean interrupted) { } + + public void set_frequency(double frequency) throws InterruptedException, TimeoutException { } + + public void save_frequency() { } + + public void set_telemetry(int telemetry) { } + + public void save_telemetry() { } + + public void update(AltosState state) throws InterruptedException { } + + public boolean supports_telemetry(int telemetry) { return false; } + + public File backing_file() { return null; } +} diff --git a/altoslib/AltosFrequency.java b/altoslib/AltosFrequency.java new file mode 100644 index 00000000..f08ff116 --- /dev/null +++ b/altoslib/AltosFrequency.java @@ -0,0 +1,48 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + +import java.io.*; +import java.util.*; +import java.text.*; + +public class AltosFrequency { + public double frequency; + public String description; + + public String toString() { + return String.format("%7.3f MHz %-20s", + frequency, description); + } + + public String toShortString() { + return String.format("%7.3f MHz %s", + frequency, description); + } + + public boolean close(double f) { + double diff = Math.abs(frequency - f); + + return diff < 0.010; + } + + public AltosFrequency(double f, String d) { + frequency = f; + description = d; + } +} \ No newline at end of file diff --git a/altoslib/AltosGPS.java b/altoslib/AltosGPS.java new file mode 100644 index 00000000..f078a469 --- /dev/null +++ b/altoslib/AltosGPS.java @@ -0,0 +1,248 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.lang.*; +import java.text.*; + +public class AltosGPS { + + public final static int MISSING = AltosRecord.MISSING; + + public int nsat; + public boolean locked; + public boolean connected; + public double lat; /* degrees (+N -S) */ + public double lon; /* degrees (+E -W) */ + public int alt; /* m */ + public int year; + public int month; + public int day; + public int hour; + public int minute; + public int second; + + public double ground_speed; /* m/s */ + public int course; /* degrees */ + public double climb_rate; /* m/s */ + public double hdop; /* unitless */ + public double vdop; /* unitless */ + public int h_error; /* m */ + public int v_error; /* m */ + + public AltosGPSSat[] cc_gps_sat; /* tracking data */ + + public void ParseGPSDate(String date) throws ParseException { + String[] ymd = date.split("-"); + if (ymd.length != 3) + throw new ParseException("error parsing GPS date " + date + " got " + ymd.length, 0); + year = AltosParse.parse_int(ymd[0]); + month = AltosParse.parse_int(ymd[1]); + day = AltosParse.parse_int(ymd[2]); + } + + public void ParseGPSTime(String time) throws ParseException { + String[] hms = time.split(":"); + if (hms.length != 3) + throw new ParseException("Error parsing GPS time " + time + " got " + hms.length, 0); + hour = AltosParse.parse_int(hms[0]); + minute = AltosParse.parse_int(hms[1]); + second = AltosParse.parse_int(hms[2]); + } + + public void ClearGPSTime() { + year = month = day = 0; + hour = minute = second = 0; + } + + public AltosGPS(AltosTelemetryMap map) throws ParseException { + String state = map.get_string(AltosTelemetry.AO_TELEM_GPS_STATE, + AltosTelemetry.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)) { + 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); + 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); + + 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); + + ground_speed = map.get_double(AltosTelemetry.AO_TELEM_GPS_HORIZONTAL_SPEED, + AltosRecord.MISSING, 1/100.0); + course = map.get_int(AltosTelemetry.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)) { + connected = true; + locked = false; + } else { + connected = false; + locked = false; + } + } + + public AltosGPS(String[] words, int i, int version) throws ParseException { + AltosParse.word(words[i++], "GPS"); + nsat = AltosParse.parse_int(words[i++]); + AltosParse.word(words[i++], "sat"); + + connected = false; + locked = false; + lat = lon = 0; + alt = 0; + ClearGPSTime(); + if ((words[i]).equals("unlocked")) { + connected = true; + i++; + } else if ((words[i]).equals("not-connected")) { + i++; + } else if (words.length >= 40) { + locked = true; + connected = true; + + if (version > 1) + ParseGPSDate(words[i++]); + else + year = month = day = 0; + ParseGPSTime(words[i++]); + lat = AltosParse.parse_coord(words[i++]); + lon = AltosParse.parse_coord(words[i++]); + alt = AltosParse.parse_int(words[i++]); + if (version > 1 || (i < words.length && !words[i].equals("SAT"))) { + ground_speed = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(H)")); + course = AltosParse.parse_int(words[i++]); + climb_rate = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(V)")); + hdop = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "(hdop)")); + h_error = AltosParse.parse_int(words[i++]); + v_error = AltosParse.parse_int(words[i++]); + } + } else { + i++; + } + if (i < words.length) { + AltosParse.word(words[i++], "SAT"); + int tracking_channels = 0; + if (words[i].equals("not-connected")) + tracking_channels = 0; + else + tracking_channels = AltosParse.parse_int(words[i]); + i++; + cc_gps_sat = new AltosGPSSat[tracking_channels]; + for (int chan = 0; chan < tracking_channels; chan++) { + cc_gps_sat[chan] = new AltosGPSSat(); + cc_gps_sat[chan].svid = AltosParse.parse_int(words[i++]); + /* Older versions included SiRF status bits */ + if (version < 2) + i++; + cc_gps_sat[chan].c_n0 = AltosParse.parse_int(words[i++]); + } + } else + cc_gps_sat = new AltosGPSSat[0]; + } + + public void set_latitude(int in_lat) { + lat = in_lat / 10.0e7; + } + + public void set_longitude(int in_lon) { + lon = in_lon / 10.0e7; + } + + public void set_time(int hour, int minute, int second) { + hour = hour; + minute = minute; + second = second; + } + + public void set_date(int year, int month, int day) { + year = year; + month = month; + day = day; + } + + public void set_flags(int flags) { + flags = flags; + } + + public void set_altitude(int altitude) { + altitude = altitude; + } + + public void add_sat(int svid, int c_n0) { + if (cc_gps_sat == null) { + cc_gps_sat = new AltosGPSSat[1]; + } else { + AltosGPSSat[] new_gps_sat = new AltosGPSSat[cc_gps_sat.length + 1]; + for (int i = 0; i < cc_gps_sat.length; i++) + new_gps_sat[i] = cc_gps_sat[i]; + cc_gps_sat = new_gps_sat; + } + AltosGPSSat sat = new AltosGPSSat(); + sat.svid = svid; + sat.c_n0 = c_n0; + cc_gps_sat[cc_gps_sat.length - 1] = sat; + } + + public AltosGPS() { + ClearGPSTime(); + cc_gps_sat = null; + } + + public AltosGPS(AltosGPS old) { + nsat = old.nsat; + locked = old.locked; + connected = old.connected; + lat = old.lat; /* degrees (+N -S) */ + lon = old.lon; /* degrees (+E -W) */ + alt = old.alt; /* m */ + year = old.year; + month = old.month; + day = old.day; + hour = old.hour; + minute = old.minute; + second = old.second; + + ground_speed = old.ground_speed; /* m/s */ + course = old.course; /* degrees */ + climb_rate = old.climb_rate; /* m/s */ + hdop = old.hdop; /* unitless? */ + h_error = old.h_error; /* m */ + v_error = old.v_error; /* m */ + + if (old.cc_gps_sat != null) { + cc_gps_sat = new AltosGPSSat[old.cc_gps_sat.length]; + for (int i = 0; i < old.cc_gps_sat.length; i++) { + cc_gps_sat[i] = new AltosGPSSat(); + cc_gps_sat[i].svid = old.cc_gps_sat[i].svid; + cc_gps_sat[i].c_n0 = old.cc_gps_sat[i].c_n0; + } + } + } +} diff --git a/altoslib/AltosGPSSat.java b/altoslib/AltosGPSSat.java new file mode 100644 index 00000000..faa1ec8d --- /dev/null +++ b/altoslib/AltosGPSSat.java @@ -0,0 +1,32 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + +public class AltosGPSSat { + public int svid; + public int c_n0; + + public AltosGPSSat(int s, int c) { + svid = s; + c_n0= c; + } + + public AltosGPSSat() { + } +} + diff --git a/altoslib/AltosGreatCircle.java b/altoslib/AltosGreatCircle.java new file mode 100644 index 00000000..76b71859 --- /dev/null +++ b/altoslib/AltosGreatCircle.java @@ -0,0 +1,101 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.lang.Math; + +public class AltosGreatCircle { + public double distance; + public double bearing; + + double sqr(double a) { return a * a; } + + static final double rad = Math.PI / 180; + static final double earth_radius = 6371.2 * 1000; /* in meters */ + + public static final int BEARING_LONG = 0; + public static final int BEARING_SHORT = 1; + public static final int BEARING_VOICE = 2; + + public String bearing_words(int length) { + String [][] bearing_string = { + { + "North", "North North East", "North East", "East North East", + "East", "East South East", "South East", "South South East", + "South", "South South West", "South West", "West South West", + "West", "West North West", "North West", "North North West" + }, { + "N", "NNE", "NE", "ENE", + "E", "ESE", "SE", "SSE", + "S", "SSW", "SW", "WSW", + "W", "WNW", "NW", "NNW" + }, { + "north", "nor nor east", "north east", "east nor east", + "east", "east sow east", "south east", "sow sow east", + "south", "sow sow west", "south west", "west sow west", + "west", "west nor west", "north west", "nor nor west " + } + }; + return bearing_string[length][(int)((bearing / 90 * 8 + 1) / 2)%16]; + } + + public AltosGreatCircle (double start_lat, double start_lon, + double end_lat, double end_lon) + { + double lat1 = rad * start_lat; + double lon1 = rad * -start_lon; + double lat2 = rad * end_lat; + double lon2 = rad * -end_lon; + + double d_lon = lon2 - lon1; + + /* From http://en.wikipedia.org/wiki/Great-circle_distance */ + double vdn = Math.sqrt(sqr(Math.cos(lat2) * Math.sin(d_lon)) + + sqr(Math.cos(lat1) * Math.sin(lat2) - + Math.sin(lat1) * Math.cos(lat2) * Math.cos(d_lon))); + double vdd = Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(d_lon); + double d = Math.atan2(vdn,vdd); + double course; + + if (Math.cos(lat1) < 1e-20) { + if (lat1 > 0) + course = Math.PI; + else + course = -Math.PI; + } else { + if (d < 1e-10) + course = 0; + else + course = Math.acos((Math.sin(lat2)-Math.sin(lat1)*Math.cos(d)) / + (Math.sin(d)*Math.cos(lat1))); + if (Math.sin(lon2-lon1) > 0) + course = 2 * Math.PI-course; + } + distance = d * earth_radius; + bearing = course * 180/Math.PI; + } + + public AltosGreatCircle(AltosGPS start, AltosGPS end) { + this(start.lat, start.lon, end.lat, end.lon); + } + + public AltosGreatCircle() { + distance = 0; + bearing = 0; + } +} diff --git a/altoslib/AltosLib.java b/altoslib/AltosLib.java new file mode 100644 index 00000000..2921d040 --- /dev/null +++ b/altoslib/AltosLib.java @@ -0,0 +1,348 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.awt.*; +import java.util.*; +import java.text.*; +import java.nio.charset.Charset; + +public class AltosLib { + /* EEProm command letters */ + public static final int AO_LOG_FLIGHT = 'F'; + public static final int AO_LOG_SENSOR = 'A'; + public static final int AO_LOG_TEMP_VOLT = 'T'; + public static final int AO_LOG_DEPLOY = 'D'; + public static final int AO_LOG_STATE = 'S'; + public static final int AO_LOG_GPS_TIME = 'G'; + public static final int AO_LOG_GPS_LAT = 'N'; + public static final int AO_LOG_GPS_LON = 'W'; + public static final int AO_LOG_GPS_ALT = 'H'; + public static final int AO_LOG_GPS_SAT = 'V'; + public static final int AO_LOG_GPS_DATE = 'Y'; + public static final int AO_LOG_PRESSURE = 'P'; + + /* Added for header fields in eeprom files */ + public static final int AO_LOG_CONFIG_VERSION = 1000; + public static final int AO_LOG_MAIN_DEPLOY = 1001; + public static final int AO_LOG_APOGEE_DELAY = 1002; + public static final int AO_LOG_RADIO_CHANNEL = 1003; + public static final int AO_LOG_CALLSIGN = 1004; + public static final int AO_LOG_ACCEL_CAL = 1005; + public static final int AO_LOG_RADIO_CAL = 1006; + public static final int AO_LOG_MAX_FLIGHT_LOG = 1007; + public static final int AO_LOG_MANUFACTURER = 2000; + public static final int AO_LOG_PRODUCT = 2001; + public static final int AO_LOG_SERIAL_NUMBER = 2002; + public static final int AO_LOG_LOG_FORMAT = 2003; + public static final int AO_LOG_SOFTWARE_VERSION = 9999; + + /* Added to flag invalid records */ + public static final int AO_LOG_INVALID = -1; + + /* Flight state numbers and names */ + public static final int ao_flight_startup = 0; + public static final int ao_flight_idle = 1; + public static final int ao_flight_pad = 2; + public static final int ao_flight_boost = 3; + public static final int ao_flight_fast = 4; + public static final int ao_flight_coast = 5; + public static final int ao_flight_drogue = 6; + public static final int ao_flight_main = 7; + public static final int ao_flight_landed = 8; + public static final int ao_flight_invalid = 9; + + /* Telemetry modes */ + public static final int ao_telemetry_off = 0; + public static final int ao_telemetry_min = 1; + public static final int ao_telemetry_standard = 1; + public static final int ao_telemetry_0_9 = 2; + public static final int ao_telemetry_0_8 = 3; + public static final int ao_telemetry_max = 3; + + public static final String[] ao_telemetry_name = { + "Off", "Standard Telemetry", "TeleMetrum v0.9", "TeleMetrum v0.8" + }; + + public static final String launch_sites_url = "http://www.altusmetrum.org/AltOS/launch-sites.txt"; + + public static final int ao_telemetry_standard_len = 32; + public static final int ao_telemetry_0_9_len = 95; + public static final int ao_telemetry_0_8_len = 94; + + public static final int[] ao_telemetry_len = { + 0, 32, 95, 94 + }; + + public static HashMap string_to_state = new HashMap(); + + public static boolean map_initialized = false; + + public static void initialize_map() + { + string_to_state.put("startup", ao_flight_startup); + string_to_state.put("idle", ao_flight_idle); + string_to_state.put("pad", ao_flight_pad); + string_to_state.put("boost", ao_flight_boost); + string_to_state.put("fast", ao_flight_fast); + string_to_state.put("coast", ao_flight_coast); + string_to_state.put("drogue", ao_flight_drogue); + string_to_state.put("apogee", ao_flight_coast); + string_to_state.put("main", ao_flight_main); + string_to_state.put("landed", ao_flight_landed); + string_to_state.put("invalid", ao_flight_invalid); + map_initialized = true; + } + + public static int telemetry_len(int telemetry) { + if (telemetry <= ao_telemetry_max) + return ao_telemetry_len[telemetry]; + throw new IllegalArgumentException(String.format("Invalid telemetry %d", + telemetry)); + } + + public static String telemetry_name(int telemetry) { + if (telemetry <= ao_telemetry_max) + return ao_telemetry_name[telemetry]; + throw new IllegalArgumentException(String.format("Invalid telemetry %d", + telemetry)); + } + + public static String[] state_to_string = { + "startup", + "idle", + "pad", + "boost", + "fast", + "coast", + "drogue", + "main", + "landed", + "invalid", + }; + + public static String[] state_to_string_capital = { + "Startup", + "Idle", + "Pad", + "Boost", + "Fast", + "Coast", + "Drogue", + "Main", + "Landed", + "Invalid", + }; + + public static int state(String state) { + if (!map_initialized) + initialize_map(); + if (string_to_state.containsKey(state)) + return string_to_state.get(state); + return ao_flight_invalid; + } + + public static String state_name(int state) { + if (state < 0 || state_to_string.length <= state) + return "invalid"; + return state_to_string[state]; + } + + public static final int AO_GPS_VALID = (1 << 4); + public static final int AO_GPS_RUNNING = (1 << 5); + public static final int AO_GPS_DATE_VALID = (1 << 6); + public static final int AO_GPS_NUM_SAT_SHIFT = 0; + public static final int AO_GPS_NUM_SAT_MASK = 0xf; + + public static final int AO_LOG_FORMAT_UNKNOWN = 0; + public static final int AO_LOG_FORMAT_FULL = 1; + public static final int AO_LOG_FORMAT_TINY = 2; + 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_NONE = 127; + + public static boolean isspace(int c) { + switch (c) { + case ' ': + case '\t': + return true; + } + return false; + } + + public static boolean ishex(int c) { + if ('0' <= c && c <= '9') + return true; + if ('a' <= c && c <= 'f') + return true; + if ('A' <= c && c <= 'F') + return true; + return false; + } + + public static boolean ishex(String s) { + for (int i = 0; i < s.length(); i++) + if (!ishex(s.charAt(i))) + return false; + return true; + } + + public static int fromhex(int c) { + if ('0' <= c && c <= '9') + return c - '0'; + if ('a' <= c && c <= 'f') + return c - 'a' + 10; + if ('A' <= c && c <= 'F') + return c - 'A' + 10; + return -1; + } + + public static int fromhex(String s) throws NumberFormatException { + int c, v = 0; + for (int i = 0; i < s.length(); i++) { + c = s.charAt(i); + if (!ishex(c)) { + if (i == 0) + throw new NumberFormatException(String.format("invalid hex \"%s\"", s)); + return v; + } + v = v * 16 + fromhex(c); + } + return v; + } + + public static boolean isdec(int c) { + if ('0' <= c && c <= '9') + return true; + return false; + } + + public static boolean isdec(String s) { + for (int i = 0; i < s.length(); i++) + if (!isdec(s.charAt(i))) + return false; + return true; + } + + public static int fromdec(int c) { + if ('0' <= c && c <= '9') + return c - '0'; + return -1; + } + + public static int int8(int[] bytes, int i) { + return (int) (byte) bytes[i]; + } + + public static int uint8(int[] bytes, int i) { + return bytes[i]; + } + + public static int int16(int[] bytes, int i) { + return (int) (short) (bytes[i] + (bytes[i+1] << 8)); + } + + public static int uint16(int[] bytes, int i) { + return bytes[i] + (bytes[i+1] << 8); + } + + public static int uint32(int[] bytes, int i) { + return bytes[i] + + (bytes[i+1] << 8) + + (bytes[i+2] << 16) + + (bytes[i+3] << 24); + } + + public static final Charset unicode_set = Charset.forName("UTF-8"); + + public static String string(int[] bytes, int s, int l) { + if (s + l > bytes.length) { + if (s > bytes.length) { + s = bytes.length; + l = 0; + } else { + l = bytes.length - s; + } + } + + int i; + for (i = l - 1; i >= 0; i--) + if (bytes[s+i] != 0) + break; + + l = i + 1; + byte[] b = new byte[l]; + + for (i = 0; i < l; i++) + b[i] = (byte) bytes[s+i]; + String n = new String(b, unicode_set); + return n; + } + + public static int hexbyte(String s, int i) { + int c0, c1; + + if (s.length() < i + 2) + throw new NumberFormatException(String.format("invalid hex \"%s\"", s)); + c0 = s.charAt(i); + if (!ishex(c0)) + throw new NumberFormatException(String.format("invalid hex \"%c\"", c0)); + c1 = s.charAt(i+1); + if (!ishex(c1)) + throw new NumberFormatException(String.format("invalid hex \"%c\"", c1)); + return fromhex(c0) * 16 + fromhex(c1); + } + + public static int[] hexbytes(String s) { + int n; + int[] r; + int i; + + if ((s.length() & 1) != 0) + throw new NumberFormatException(String.format("invalid line \"%s\"", s)); + n = s.length() / 2; + r = new int[n]; + for (i = 0; i < n; i++) + r[i] = hexbyte(s, i * 2); + return r; + } + + public static int fromdec(String s) throws NumberFormatException { + int c, v = 0; + int sign = 1; + for (int i = 0; i < s.length(); i++) { + c = s.charAt(i); + if (i == 0 && c == '-') { + sign = -1; + } else if (!isdec(c)) { + if (i == 0) + throw new NumberFormatException(String.format("invalid number \"%s\"", s)); + return v; + } else + v = v * 10 + fromdec(c); + } + return v * sign; + } + + public static String replace_extension(String input, String extension) { + int dot = input.lastIndexOf("."); + if (dot > 0) + input = input.substring(0,dot); + return input.concat(extension); + } +} diff --git a/altoslib/AltosLine.java b/altoslib/AltosLine.java new file mode 100644 index 00000000..5627795a --- /dev/null +++ b/altoslib/AltosLine.java @@ -0,0 +1,30 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +public class AltosLine { + public String line; + + public AltosLine() { + line = null; + } + + public AltosLine(String s) { + line = s; + } +} \ No newline at end of file diff --git a/altoslib/AltosLink.java b/altoslib/AltosLink.java new file mode 100644 index 00000000..9b80e916 --- /dev/null +++ b/altoslib/AltosLink.java @@ -0,0 +1,238 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + +import java.lang.*; +import java.io.*; +import java.util.concurrent.*; +import java.util.*; +import java.text.*; + +public abstract class AltosLink { + public abstract void print(String data); + public abstract void close(); + + public static boolean debug = false; + public static void set_debug(boolean in_debug) { debug = in_debug; } + LinkedList pending_output = new LinkedList(); + + public LinkedList> monitors = new LinkedList> ();; + public LinkedBlockingQueue reply_queue = new LinkedBlockingQueue(); + + public void add_monitor(LinkedBlockingQueue q) { + set_monitor(true); + monitors.add(q); + } + + public void remove_monitor(LinkedBlockingQueue q) { + monitors.remove(q); + if (monitors.isEmpty()) + set_monitor(false); + } + + public void printf(String format, Object ... arguments) { + String line = String.format(format, arguments); + if (debug) + pending_output.add(line); + print(line); + } + + public String get_reply_no_dialog(int timeout) throws InterruptedException, TimeoutException { + flush_output(); + AltosLine line = reply_queue.poll(timeout, TimeUnit.MILLISECONDS); + if (line != null) + return line.line; + return null; + } + + public String get_reply(int timeout) throws InterruptedException { + try { + return get_reply_no_dialog(timeout); + } catch (TimeoutException te) { + return null; + } + } + + public String get_reply() throws InterruptedException { + return get_reply(5000); + } + + public void add_telem(AltosLine line) throws InterruptedException { + for (int e = 0; e < monitors.size(); e++) { + LinkedBlockingQueue q = monitors.get(e); + q.put(line); + } + } + + public void add_reply(AltosLine line) throws InterruptedException { + reply_queue.put (line); + } + + public void add_string(String line) throws InterruptedException { + if (line.startsWith("TELEM") || line.startsWith("VERSION") || line.startsWith("CRC")) { + add_telem(new AltosLine(line)); + } else { + add_reply(new AltosLine(line)); + } + } + + public void add_bytes(byte[] bytes, int len) throws InterruptedException { + String line; + try { + line = new String(bytes, 0, len, "UTF-8"); + } catch (UnsupportedEncodingException ue) { + line = ""; + for (int i = 0; i < len; i++) + line = line + bytes[i]; + } + if (debug) + System.out.printf("\t\t\t\t\t%s\n", line); + add_string(line); + } + + public void flush_output() { + for (String s : pending_output) + System.out.print(s); + pending_output.clear(); + } + + public void flush_input(int timeout) throws InterruptedException { + flush_output(); + boolean got_some; + + do { + Thread.sleep(timeout); + got_some = !reply_queue.isEmpty(); + reply_queue.clear(); + } while (got_some); + } + + + public void flush_input() throws InterruptedException { + flush_input(100); + } + + + /* + * Various command-level operations on + * the link + */ + public boolean monitor_mode = false; + public int telemetry = AltosLib.ao_telemetry_standard; + public double frequency; + AltosConfigData config_data; + + private int telemetry_len() { + return AltosLib.telemetry_len(telemetry); + } + + public void set_telemetry(int in_telemetry) { + telemetry = in_telemetry; + if (monitor_mode) + printf("m 0\nm %x\n", telemetry_len()); + flush_output(); + } + + public void set_monitor(boolean monitor) { + monitor_mode = monitor; + if (monitor) + printf("m %x\n", telemetry_len()); + else + printf("m 0\n"); + flush_output(); + } + + private void set_channel(int channel) { + if (monitor_mode) + printf("m 0\nc r %d\nm %x\n", + channel, telemetry_len()); + else + printf("c r %d\n", channel); + flush_output(); + } + + private void set_radio_setting(int setting) { + if (monitor_mode) + printf("m 0\nc R %d\nm %x\n", + setting, telemetry_len()); + else + printf("c R %d\n", setting); + flush_output(); + } + + public void set_radio_frequency(double frequency, + boolean has_setting, + int cal) { + if (debug) + System.out.printf("set_radio_frequency %7.3f %b %d\n", frequency, has_setting, cal); + if (has_setting) + set_radio_setting(AltosConvert.radio_frequency_to_setting(frequency, cal)); + else + set_channel(AltosConvert.radio_frequency_to_channel(frequency)); + } + + public AltosConfigData config_data() throws InterruptedException, TimeoutException { + if (config_data == null) + config_data = new AltosConfigData(this); + return config_data; + } + + public void set_radio_frequency(double in_frequency) throws InterruptedException, TimeoutException { + frequency = in_frequency; + config_data(); + set_radio_frequency(frequency, + config_data.radio_setting != 0, + config_data.radio_calibration); + } + + public void set_callsign(String callsign) { + printf ("c c %s\n", callsign); + flush_output(); + } + + public boolean remote; + public int serial; + public String name; + + public void start_remote() throws TimeoutException, InterruptedException { + if (debug) + System.out.printf("start remote %7.3f\n", frequency); + if (frequency == 0.0) + frequency = AltosPreferences.frequency(serial); + set_radio_frequency(frequency); + set_callsign(AltosPreferences.callsign()); + printf("p\nE 0\n"); + flush_input(); + remote = true; + } + + public void stop_remote() throws InterruptedException { + if (debug) + System.out.printf("stop remote\n"); + try { + flush_input(); + } finally { + printf ("~\n"); + flush_output(); + } + remote = false; + } + + public AltosLink() { + } +} diff --git a/altoslib/AltosLog.java b/altoslib/AltosLog.java new file mode 100644 index 00000000..08c45ca8 --- /dev/null +++ b/altoslib/AltosLog.java @@ -0,0 +1,126 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.io.*; +import java.lang.*; +import java.util.*; +import java.text.ParseException; +import java.util.concurrent.LinkedBlockingQueue; + +/* + * This creates a thread to capture telemetry data and write it to + * a log file + */ +class AltosLog implements Runnable { + + LinkedBlockingQueue input_queue; + LinkedBlockingQueue pending_queue; + int serial; + int flight; + FileWriter log_file; + Thread log_thread; + AltosFile file; + + private void close_log_file() { + if (log_file != null) { + try { + log_file.close(); + } catch (IOException io) { + } + log_file = null; + } + } + + void close() { + close_log_file(); + if (log_thread != null) { + log_thread.interrupt(); + log_thread = null; + } + } + + File file() { + return file; + } + + boolean open (AltosRecord telem) throws IOException { + AltosFile a = new AltosFile(telem); + + System.out.printf("open %s\n", a.toString()); + log_file = new FileWriter(a, true); + if (log_file != null) { + while (!pending_queue.isEmpty()) { + try { + String s = pending_queue.take(); + log_file.write(s); + log_file.write('\n'); + } catch (InterruptedException ie) { + } + } + log_file.flush(); + file = a; + } + return log_file != null; + } + + public void run () { + try { + AltosRecord previous = null; + for (;;) { + AltosLine line = input_queue.take(); + if (line.line == null) + continue; + try { + AltosRecord telem = AltosTelemetry.parse(line.line, previous); + if (telem.serial != 0 && telem.flight != 0 && + (telem.serial != serial || telem.flight != flight || log_file == null)) + { + close_log_file(); + serial = telem.serial; + flight = telem.flight; + open(telem); + } + previous = telem; + } catch (ParseException pe) { + } catch (AltosCRCException ce) { + } + if (log_file != null) { + log_file.write(line.line); + log_file.write('\n'); + log_file.flush(); + } else + pending_queue.put(line.line); + } + } catch (InterruptedException ie) { + } catch (IOException ie) { + } + close(); + } + + public AltosLog (AltosLink link) { + pending_queue = new LinkedBlockingQueue (); + input_queue = new LinkedBlockingQueue (); + link.add_monitor(input_queue); + serial = -1; + flight = -1; + log_file = null; + log_thread = new Thread(this); + log_thread.start(); + } +} diff --git a/altoslib/AltosParse.java b/altoslib/AltosParse.java new file mode 100644 index 00000000..7d832f1a --- /dev/null +++ b/altoslib/AltosParse.java @@ -0,0 +1,79 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.text.*; +import java.lang.*; + +public class AltosParse { + public static boolean isdigit(char c) { + return '0' <= c && c <= '9'; + } + + public static int parse_int(String v) throws ParseException { + try { + return AltosLib.fromdec(v); + } catch (NumberFormatException e) { + throw new ParseException("error parsing int " + v, 0); + } + } + + public static int parse_hex(String v) throws ParseException { + try { + return AltosLib.fromhex(v); + } catch (NumberFormatException e) { + throw new ParseException("error parsing hex " + v, 0); + } + } + + public static double parse_double(String v) throws ParseException { + try { + return Double.parseDouble(v); + } catch (NumberFormatException e) { + throw new ParseException("error parsing double " + v, 0); + } + } + + public static double parse_coord(String coord) throws ParseException { + String[] dsf = coord.split("\\D+"); + + if (dsf.length != 3) { + throw new ParseException("error parsing coord " + coord, 0); + } + int deg = parse_int(dsf[0]); + int min = parse_int(dsf[1]); + int frac = parse_int(dsf[2]); + + double r = deg + (min + frac / 10000.0) / 60.0; + if (coord.endsWith("S") || coord.endsWith("W")) + r = -r; + return r; + } + + public static String strip_suffix(String v, String suffix) { + if (v.endsWith(suffix)) + return v.substring(0, v.length() - suffix.length()); + return v; + } + + public static void word(String v, String m) throws ParseException { + if (!v.equals(m)) { + throw new ParseException("error matching '" + v + "' '" + m + "'", 0); + } + } +} diff --git a/altoslib/AltosPreferences.java b/altoslib/AltosPreferences.java new file mode 100644 index 00000000..43c7088d --- /dev/null +++ b/altoslib/AltosPreferences.java @@ -0,0 +1,365 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; +import java.awt.Component; +import javax.swing.*; +import javax.swing.filechooser.FileSystemView; + +public class AltosPreferences { + public static Preferences preferences; + + /* logdir preference name */ + public final static String logdirPreference = "LOGDIR"; + + /* channel preference name */ + public final static String channelPreferenceFormat = "CHANNEL-%d"; + + /* frequency preference name */ + public final static String frequencyPreferenceFormat = "FREQUENCY-%d"; + + /* telemetry format preference name */ + public final static String telemetryPreferenceFormat = "TELEMETRY-%d"; + + /* voice preference name */ + public final static String voicePreference = "VOICE"; + + /* callsign preference name */ + public final static String callsignPreference = "CALLSIGN"; + + /* firmware directory preference name */ + public final static String firmwaredirPreference = "FIRMWARE"; + + /* serial debug preference name */ + public final static String serialDebugPreference = "SERIAL-DEBUG"; + + /* scanning telemetry preferences name */ + public final static String scanningTelemetryPreference = "SCANNING-TELEMETRY"; + + /* Launcher serial preference name */ + public final static String launcherSerialPreference = "LAUNCHER-SERIAL"; + + /* Launcher channel preference name */ + public final static String launcherChannelPreference = "LAUNCHER-CHANNEL"; + + /* Default logdir is ~/TeleMetrum */ + public final static String logdirName = "TeleMetrum"; + + /* Log directory */ + public static File logdir; + + /* Map directory -- hangs of logdir */ + public static File mapdir; + + /* Frequency (map serial to frequency) */ + public static Hashtable frequencies; + + /* Telemetry (map serial to telemetry format) */ + public static Hashtable telemetries; + + /* Voice preference */ + public static boolean voice; + + /* Callsign preference */ + public static String callsign; + + /* Firmware directory */ + public static File firmwaredir; + + /* Scanning telemetry */ + public static int scanning_telemetry; + + /* List of frequencies */ + public final static String common_frequencies_node_name = "COMMON-FREQUENCIES"; + public static AltosFrequency[] common_frequencies; + + public final static String frequency_count = "COUNT"; + public final static String frequency_format = "FREQUENCY-%d"; + public final static String description_format = "DESCRIPTION-%d"; + + public static AltosFrequency[] load_common_frequencies() { + AltosFrequency[] frequencies = null; + boolean existing = false; + try { + existing = preferences.nodeExists(common_frequencies_node_name); + } catch (BackingStoreException be) { + existing = false; + } + if (existing) { + Preferences node = preferences.node(common_frequencies_node_name); + int count = node.getInt(frequency_count, 0); + + frequencies = new AltosFrequency[count]; + for (int i = 0; i < count; i++) { + double frequency; + String description; + + frequency = node.getDouble(String.format(frequency_format, i), 0.0); + description = node.get(String.format(description_format, i), null); + frequencies[i] = new AltosFrequency(frequency, description); + } + } else { + frequencies = new AltosFrequency[10]; + for (int i = 0; i < 10; i++) { + frequencies[i] = new AltosFrequency(434.550 + i * .1, + String.format("Channel %d", i)); + } + } + return frequencies; + } + + public static void save_common_frequencies(AltosFrequency[] frequencies) { + Preferences node = preferences.node(common_frequencies_node_name); + + node.putInt(frequency_count, frequencies.length); + for (int i = 0; i < frequencies.length; i++) { + node.putDouble(String.format(frequency_format, i), frequencies[i].frequency); + node.put(String.format(description_format, i), frequencies[i].description); + } + } + public static int launcher_serial; + + public static int launcher_channel; + + public static void init() { + preferences = Preferences.userRoot().node("/org/altusmetrum/altosui"); + + /* Initialize logdir from preferences */ + String logdir_string = preferences.get(logdirPreference, null); + if (logdir_string != null) + logdir = new File(logdir_string); + else { + /* Use the file system view default directory */ + logdir = new File(FileSystemView.getFileSystemView().getDefaultDirectory(), logdirName); + if (!logdir.exists()) + logdir.mkdirs(); + } + mapdir = new File(logdir, "maps"); + if (!mapdir.exists()) + mapdir.mkdirs(); + + frequencies = new Hashtable(); + + telemetries = new Hashtable(); + + voice = preferences.getBoolean(voicePreference, true); + + callsign = preferences.get(callsignPreference,"N0CALL"); + + scanning_telemetry = preferences.getInt(scanningTelemetryPreference,(1 << AltosLib.ao_telemetry_standard)); + + launcher_serial = preferences.getInt(launcherSerialPreference, 0); + + launcher_channel = preferences.getInt(launcherChannelPreference, 0); + + String firmwaredir_string = preferences.get(firmwaredirPreference, null); + if (firmwaredir_string != null) + firmwaredir = new File(firmwaredir_string); + else + firmwaredir = null; + + common_frequencies = load_common_frequencies(); + + } + + static { init(); } + + public static void flush_preferences() { + try { + preferences.flush(); + } catch (BackingStoreException ee) { +/* + if (component != null) + JOptionPane.showMessageDialog(component, + preferences.absolutePath(), + "Cannot save prefernces", + JOptionPane.ERROR_MESSAGE); + else +*/ + System.err.printf("Cannot save preferences\n"); + } + } + + public static void set_logdir(File new_logdir) { + logdir = new_logdir; + mapdir = new File(logdir, "maps"); + if (!mapdir.exists()) + mapdir.mkdirs(); + synchronized (preferences) { + preferences.put(logdirPreference, logdir.getPath()); + flush_preferences(); + } + } + + public static File logdir() { + return logdir; + } + + public static File mapdir() { + return mapdir; + } + + public static void set_frequency(int serial, double new_frequency) { + frequencies.put(serial, new_frequency); + synchronized (preferences) { + preferences.putDouble(String.format(frequencyPreferenceFormat, serial), new_frequency); + flush_preferences(); + } + } + + public static double frequency(int serial) { + if (frequencies.containsKey(serial)) + return frequencies.get(serial); + double frequency = preferences.getDouble(String.format(frequencyPreferenceFormat, serial), 0); + if (frequency == 0.0) { + int channel = preferences.getInt(String.format(channelPreferenceFormat, serial), 0); + frequency = AltosConvert.radio_channel_to_frequency(channel); + } + frequencies.put(serial, frequency); + return frequency; + } + + public static void set_telemetry(int serial, int new_telemetry) { + telemetries.put(serial, new_telemetry); + synchronized (preferences) { + preferences.putInt(String.format(telemetryPreferenceFormat, serial), new_telemetry); + flush_preferences(); + } + } + + public static int telemetry(int serial) { + if (telemetries.containsKey(serial)) + return telemetries.get(serial); + int telemetry = preferences.getInt(String.format(telemetryPreferenceFormat, serial), + AltosLib.ao_telemetry_standard); + telemetries.put(serial, telemetry); + return telemetry; + } + + public static void set_scanning_telemetry(int new_scanning_telemetry) { + scanning_telemetry = new_scanning_telemetry; + synchronized (preferences) { + preferences.putInt(scanningTelemetryPreference, scanning_telemetry); + flush_preferences(); + } + } + + public static int scanning_telemetry() { + return scanning_telemetry; + } + + public static void set_voice(boolean new_voice) { + voice = new_voice; + synchronized (preferences) { + preferences.putBoolean(voicePreference, voice); + flush_preferences(); + } + } + + public static boolean voice() { + return voice; + } + + public static void set_callsign(String new_callsign) { + callsign = new_callsign; + synchronized(preferences) { + preferences.put(callsignPreference, callsign); + flush_preferences(); + } + } + + public static String callsign() { + return callsign; + } + + public static void set_firmwaredir(File new_firmwaredir) { + firmwaredir = new_firmwaredir; + synchronized (preferences) { + preferences.put(firmwaredirPreference, firmwaredir.getPath()); + flush_preferences(); + } + } + + public static File firmwaredir() { + return firmwaredir; + } + + public static void set_launcher_serial(int new_launcher_serial) { + launcher_serial = new_launcher_serial; + System.out.printf("set launcher serial to %d\n", new_launcher_serial); + synchronized (preferences) { + preferences.putInt(launcherSerialPreference, launcher_serial); + flush_preferences(); + } + } + + public static int launcher_serial() { + return launcher_serial; + } + + public static void set_launcher_channel(int new_launcher_channel) { + launcher_channel = new_launcher_channel; + System.out.printf("set launcher channel to %d\n", new_launcher_channel); + synchronized (preferences) { + preferences.putInt(launcherChannelPreference, launcher_channel); + flush_preferences(); + } + } + + public static int launcher_channel() { + return launcher_channel; + } + + public static Preferences bt_devices() { + return preferences.node("bt_devices"); + } + + public static AltosFrequency[] common_frequencies() { + return common_frequencies; + } + + public static void set_common_frequencies(AltosFrequency[] frequencies) { + common_frequencies = frequencies; + synchronized(preferences) { + save_common_frequencies(frequencies); + flush_preferences(); + } + } + + public static void add_common_frequency(AltosFrequency frequency) { + AltosFrequency[] new_frequencies = new AltosFrequency[common_frequencies.length + 1]; + int i; + + for (i = 0; i < common_frequencies.length; i++) { + if (frequency.frequency == common_frequencies[i].frequency) + return; + if (frequency.frequency < common_frequencies[i].frequency) + break; + new_frequencies[i] = common_frequencies[i]; + } + new_frequencies[i] = frequency; + for (; i < common_frequencies.length; i++) + new_frequencies[i+1] = common_frequencies[i]; + set_common_frequencies(new_frequencies); + } +} diff --git a/altoslib/AltosRecord.java b/altoslib/AltosRecord.java new file mode 100644 index 00000000..e4915af0 --- /dev/null +++ b/altoslib/AltosRecord.java @@ -0,0 +1,319 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.lang.*; +import java.text.*; +import java.util.HashMap; +import java.io.*; + +public class AltosRecord implements Comparable { + public final static int MISSING = 0x7fffffff; + + public static final int seen_flight = 1; + public static final int seen_sensor = 2; + public static final int seen_temp_volt = 4; + public static final int seen_deploy = 8; + public static final int seen_gps_time = 16; + public static final int seen_gps_lat = 32; + public static final int seen_gps_lon = 64; + public static final int seen_companion = 128; + public int seen; + + public int version; + public String callsign; + public int serial; + public int flight; + public int rssi; + public int status; + public int state; + public int tick; + + public int accel; + public int pres; + public int temp; + public int batt; + public int drogue; + public int main; + + public int ground_accel; + public int ground_pres; + public int accel_plus_g; + public int accel_minus_g; + + public double acceleration; + public double speed; + public double height; + + public int flight_accel; + public int flight_vel; + public int flight_pres; + + public AltosGPS gps; + public boolean new_gps; + + public AltosIMU imu; + public AltosMag mag; + + public double time; /* seconds since boost */ + + public int device_type; + public int config_major; + public int config_minor; + public int apogee_delay; + public int main_deploy; + public int flight_log_max; + public String firmware_version; + + public AltosRecordCompanion companion; + +>>>>>>> 5a249bc... altosui: Complete split out of separate java library + /* + * Values for our MP3H6115A pressure sensor + * + * From the data sheet: + * + * Pressure range: 15-115 kPa + * Voltage at 115kPa: 2.82 + * Output scale: 27mV/kPa + * + * + * 27 mV/kPa * 2047 / 3300 counts/mV = 16.75 counts/kPa + * 2.82V * 2047 / 3.3 counts/V = 1749 counts/115 kPa + */ + + public static final double counts_per_kPa = 27 * 2047 / 3300; + public static final double counts_at_101_3kPa = 1674.0; + + public static double + barometer_to_pressure(double count) + { + return ((count / 16.0) / 2047.0 + 0.095) / 0.009 * 1000.0; + } + + public double raw_pressure() { + if (pres == MISSING) + return MISSING; + return barometer_to_pressure(pres); + } + + public double filtered_pressure() { + if (flight_pres == MISSING) + return MISSING; + return barometer_to_pressure(flight_pres); + } + + public double ground_pressure() { + if (ground_pres == MISSING) + return MISSING; + return barometer_to_pressure(ground_pres); + } + + public double raw_altitude() { + double p = raw_pressure(); + if (p == MISSING) + return MISSING; + return AltosConvert.pressure_to_altitude(p); + } + + public double ground_altitude() { + double p = ground_pressure(); + if (p == MISSING) + return MISSING; + return AltosConvert.pressure_to_altitude(p); + } + + public double filtered_altitude() { + if (height != MISSING && ground_pres != MISSING) + return height + ground_altitude(); + + double p = filtered_pressure(); + if (p == MISSING) + return MISSING; + return AltosConvert.pressure_to_altitude(p); + } + + public double filtered_height() { + if (height != MISSING) + return height; + + double f = filtered_altitude(); + double g = ground_altitude(); + if (f == MISSING || g == MISSING) + return MISSING; + return f - g; + } + + public double raw_height() { + double r = raw_altitude(); + double g = ground_altitude(); + + if (r == MISSING || g == MISSING) + return height; + return r - g; + } + + public double battery_voltage() { + if (batt == MISSING) + return MISSING; + return AltosConvert.cc_battery_to_voltage(batt); + } + + public double main_voltage() { + if (main == MISSING) + return MISSING; + return AltosConvert.cc_ignitor_to_voltage(main); + } + + public double drogue_voltage() { + if (drogue == MISSING) + return MISSING; + return AltosConvert.cc_ignitor_to_voltage(drogue); + } + + /* Value for the CC1111 built-in temperature sensor + * Output voltage at 0°C = 0.755V + * Coefficient = 0.00247V/°C + * Reference voltage = 1.25V + * + * temp = ((value / 32767) * 1.25 - 0.755) / 0.00247 + * = (value - 19791.268) / 32768 * 1.25 / 0.00247 + */ + + public static double + thermometer_to_temperature(double thermo) + { + return (thermo - 19791.268) / 32728.0 * 1.25 / 0.00247; + } + + public double temperature() { + if (temp == MISSING) + return MISSING; + return thermometer_to_temperature(temp); + } + + public 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 (acceleration != MISSING) + return acceleration; + + if (ground_accel == MISSING || accel == MISSING) + return MISSING; + return (ground_accel - accel) / accel_counts_per_mss(); + } + + public double accel_speed() { + if (speed != MISSING) + return speed; + if (flight_vel == MISSING) + return MISSING; + return flight_vel / (accel_counts_per_mss() * 100.0); + } + + public String state() { + return AltosLib.state_name(state); + } + + public static String gets(FileInputStream s) throws IOException { + int c; + String line = ""; + + while ((c = s.read()) != -1) { + if (c == '\r') + continue; + if (c == '\n') { + return line; + } + line = line + (char) c; + } + return null; + } + + public int compareTo(AltosRecord o) { + return tick - o.tick; + } + + public AltosRecord(AltosRecord old) { + version = old.version; + seen = old.seen; + callsign = old.callsign; + serial = old.serial; + flight = old.flight; + rssi = old.rssi; + status = old.status; + state = old.state; + tick = old.tick; + accel = old.accel; + pres = old.pres; + temp = old.temp; + batt = old.batt; + drogue = old.drogue; + main = old.main; + flight_accel = old.flight_accel; + ground_accel = old.ground_accel; + flight_vel = old.flight_vel; + flight_pres = old.flight_pres; + ground_pres = old.ground_pres; + accel_plus_g = old.accel_plus_g; + accel_minus_g = old.accel_minus_g; + acceleration = old.acceleration; + speed = old.speed; + height = old.height; + gps = new AltosGPS(old.gps); + new_gps = false; + companion = old.companion; + imu = old.imu; + mag = old.mag; + } + + public AltosRecord() { + version = 0; + seen = 0; + callsign = "N0CALL"; + serial = 0; + flight = 0; + rssi = 0; + status = 0; + state = AltosLib.ao_flight_startup; + tick = 0; + accel = MISSING; + pres = MISSING; + temp = MISSING; + batt = MISSING; + drogue = MISSING; + main = MISSING; + flight_accel = 0; + ground_accel = 0; + flight_vel = 0; + flight_pres = 0; + ground_pres = 0; + accel_plus_g = 0; + accel_minus_g = 0; + acceleration = MISSING; + speed = MISSING; + height = MISSING; + gps = new AltosGPS(); + new_gps = false; + companion = null; + } +} diff --git a/altoslib/AltosRecordCompanion.java b/altoslib/AltosRecordCompanion.java new file mode 100644 index 00000000..c8cc6cac --- /dev/null +++ b/altoslib/AltosRecordCompanion.java @@ -0,0 +1,38 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + +public class AltosRecordCompanion { + 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 AltosRecordCompanion(int in_channels) { + channels = in_channels; + if (channels < 0) + channels = 0; + if (channels > MAX_CHANNELS) + channels = MAX_CHANNELS; + companion_data = new int[channels]; + } +} diff --git a/altoslib/AltosRecordIterable.java b/altoslib/AltosRecordIterable.java new file mode 100644 index 00000000..ed1787ed --- /dev/null +++ b/altoslib/AltosRecordIterable.java @@ -0,0 +1,29 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.io.*; +import java.util.*; + +public abstract class AltosRecordIterable implements Iterable { + public abstract Iterator iterator(); + public void write_comments(PrintStream out) { } + public boolean has_accel() { return false; } + public boolean has_gps() { return false; } + public boolean has_ignite() { return false; }; +} diff --git a/altoslib/AltosReplayReader.java b/altoslib/AltosReplayReader.java new file mode 100644 index 00000000..1585f9eb --- /dev/null +++ b/altoslib/AltosReplayReader.java @@ -0,0 +1,56 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +/* + * Open an existing telemetry file and replay it in realtime + */ + +public class AltosReplayReader extends AltosFlightReader { + Iterator iterator; + File file; + + public AltosRecord read() { + if (iterator.hasNext()) + return iterator.next(); + return null; + } + + public void close (boolean interrupted) { + } + + public void update(AltosState state) throws InterruptedException { + /* Make it run in realtime after the rocket leaves the pad */ + if (state.state > AltosLib.ao_flight_pad) + Thread.sleep((int) (Math.min(state.time_change,10) * 1000)); + } + + public File backing_file() { return file; } + + public AltosReplayReader(Iterator in_iterator, File in_file) { + iterator = in_iterator; + file = in_file; + name = file.getName(); + } +} diff --git a/altoslib/AltosState.java b/altoslib/AltosState.java new file mode 100644 index 00000000..0645e448 --- /dev/null +++ b/altoslib/AltosState.java @@ -0,0 +1,210 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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. + */ + +/* + * Track flight state from telemetry or eeprom data stream + */ + +package org.altusmetrum.AltosLib; + +public class AltosState { + public AltosRecord data; + + /* derived data */ + + public long report_time; + + public double time; + public double time_change; + public int tick; + + public int state; + public boolean landed; + public boolean ascent; /* going up? */ + public boolean boost; /* under power */ + + public double ground_altitude; + public double height; + public double speed; + public double acceleration; + public double battery; + public double temperature; + public double main_sense; + public double drogue_sense; + public double baro_speed; + + public double max_height; + public double max_acceleration; + public double max_speed; + public double max_baro_speed; + + public AltosGPS gps; + + public AltosIMU imu; + public AltosMag mag; + + public static final int MIN_PAD_SAMPLES = 10; + + public int npad; + public int ngps; + public int gps_waiting; + public boolean gps_ready; + + public AltosGreatCircle from_pad; + public double elevation; /* from pad */ + public double range; /* total distance */ + + public double gps_height; + + public int speak_tick; + public double speak_altitude; + + public void init (AltosRecord cur, AltosState prev_state) { + int i; + AltosRecord prev; + + data = cur; + + ground_altitude = data.ground_altitude(); + height = data.filtered_height(); + + report_time = System.currentTimeMillis(); + + acceleration = data.acceleration(); + speed = data.accel_speed(); + temperature = data.temperature(); + drogue_sense = data.drogue_voltage(); + main_sense = data.main_voltage(); + battery = data.battery_voltage(); + 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; + 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_speed = prev_state.max_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; + + /* compute barometric speed */ + + double height_change = height - prev_state.height; + if (data.speed != AltosRecord.MISSING) + baro_speed = data.speed; + else { + if (time_change > 0) + baro_speed = (prev_state.baro_speed * 3 + (height_change / time_change)) / 4.0; + else + baro_speed = prev_state.baro_speed; + } + } else { + npad = 0; + ngps = 0; + gps = null; + baro_speed = 0; + time_change = 0; + } + + time = tick / 100.0; + + if (cur.new_gps && (state == AltosLib.ao_flight_pad || state == AltosLib.ao_flight_idle)) { + + /* Track consecutive 'good' gps reports, waiting for 10 of them */ + if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) + npad++; + else + npad = 0; + + /* Average GPS data while on the pad */ + if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) { + if (ngps > 1) { + /* 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++; + } + } + + gps_waiting = MIN_PAD_SAMPLES - npad; + if (gps_waiting < 0) + gps_waiting = 0; + + gps_ready = gps_waiting == 0; + + ascent = (AltosLib.ao_flight_boost <= state && + state <= AltosLib.ao_flight_coast); + boost = (AltosLib.ao_flight_boost == state); + + /* Only look at accelerometer data under boost */ + if (boost && acceleration > max_acceleration) + max_acceleration = acceleration; + if (boost && speed > max_speed) + max_speed = speed; + if (boost && baro_speed > max_baro_speed) + max_baro_speed = baro_speed; + + if (height > max_height) + max_height = height; + if (data.gps != null) { + if (gps == null || !gps.locked || data.gps.locked) + gps = data.gps; + if (ngps > 0 && gps.locked) { + from_pad = new AltosGreatCircle(pad_lat, pad_lon, gps.lat, gps.lon); + } + } + elevation = 0; + range = -1; + if (ngps > 0) { + gps_height = gps.alt - pad_alt; + if (from_pad != null) { + elevation = Math.atan2(height, from_pad.distance) * 180 / Math.PI; + range = Math.sqrt(height * height + from_pad.distance * from_pad.distance); + } + } else { + gps_height = 0; + } + } + + public AltosState(AltosRecord cur) { + init(cur, null); + } + + public AltosState (AltosRecord cur, AltosState prev) { + init(cur, prev); + } +} diff --git a/altoslib/AltosTelemetry.java b/altoslib/AltosTelemetry.java new file mode 100644 index 00000000..04abb1f3 --- /dev/null +++ b/altoslib/AltosTelemetry.java @@ -0,0 +1,241 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.lang.*; +import java.text.*; +import java.util.HashMap; + +/* + * 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 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); + } +} diff --git a/altoslib/AltosTelemetryIterable.java b/altoslib/AltosTelemetryIterable.java new file mode 100644 index 00000000..f4b4029f --- /dev/null +++ b/altoslib/AltosTelemetryIterable.java @@ -0,0 +1,109 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.io.*; +import java.util.*; +import java.text.*; + +public class AltosTelemetryIterable extends AltosRecordIterable { + TreeSet records; + + public Iterator iterator () { + return records.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 (); + + try { + for (;;) { + String line = AltosRecord.gets(input); + if (line == null) { + break; + } + try { + AltosRecord record = AltosTelemetry.parse(line, previous); + if (record == 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.accel != AltosRecord.MISSING) + has_accel = true; + if (record.gps != null) + has_gps = true; + if (record.main != AltosRecord.MISSING) + has_ignite = true; + if (previous != null && previous.tick != record.tick) + records.add(previous); + previous = record; + } 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) { + } + } +} diff --git a/altoslib/AltosTelemetryMap.java b/altoslib/AltosTelemetryMap.java new file mode 100644 index 00000000..003cb6a9 --- /dev/null +++ b/altoslib/AltosTelemetryMap.java @@ -0,0 +1,63 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; +import java.lang.*; +import java.text.*; +import java.util.HashMap; + +public class AltosTelemetryMap extends HashMap { + public boolean has(String key) { + return containsKey(key); + } + + public String get_string(String key) throws ParseException { + if (!has(key)) + throw new ParseException ("missing " + key, 0); + return (String) get(key); + } + + public String get_string(String key, String def) { + if (has(key)) + return get(key); + else + return def; + } + + public int get_int(String key) throws ParseException { + return AltosParse.parse_int(get_string(key)); + } + + public int get_int(String key, int def) throws ParseException { + if (has(key)) + return get_int(key); + else + return def; + } + + public double get_double(String key, double def, double scale) throws ParseException { + if (has(key)) + return get_int(key) * scale; + else + return def; + } + + public AltosTelemetryMap(String[] words, int start) { + for (int i = start; i < words.length - 1; i += 2) + put(words[i], words[i+1]); + } +} diff --git a/altoslib/AltosTelemetryReader.java b/altoslib/AltosTelemetryReader.java new file mode 100644 index 00000000..67ac1b65 --- /dev/null +++ b/altoslib/AltosTelemetryReader.java @@ -0,0 +1,119 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.lang.*; +import java.text.*; +import java.io.*; +import java.util.concurrent.*; + +public class AltosTelemetryReader extends AltosFlightReader { + AltosLink link; + AltosLog log; + AltosRecord previous; + double frequency; + int telemetry; + + LinkedBlockingQueue telem; + + public AltosRecord 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; + } + + public void flush() { + telem.clear(); + } + + public void close(boolean interrupted) { + link.remove_monitor(telem); + log.close(); + link.close(); + } + + public void set_frequency(double in_frequency) throws InterruptedException, TimeoutException { + frequency = in_frequency; + link.set_radio_frequency(frequency); + } + + public boolean supports_telemetry(int telemetry) { + + try { + /* Version 1.0 or later firmware supports all telemetry formats */ + if (serial.config_data().compare_version("1.0") >= 0) + return true; + + /* Version 0.9 firmware only supports 0.9 telemetry */ + if (serial.config_data().compare_version("0.9") >= 0) { + if (telemetry == Altos.ao_telemetry_0_9) + return true; + else + return false; + } + + /* Version 0.8 firmware only supports 0.8 telemetry */ + if (telemetry == Altos.ao_telemetry_0_8) + return true; + else + return false; + } catch (InterruptedException ie) { + return true; + } catch (TimeoutException te) { + return true; + } + } + + public void save_frequency() { + AltosPreferences.set_frequency(link.serial, frequency); + } + + public void set_telemetry(int in_telemetry) { + telemetry = in_telemetry; + link.set_telemetry(telemetry); + } + + public void save_telemetry() { + AltosPreferences.set_telemetry(link.serial, telemetry); + } + + public void set_monitor(boolean monitor) { + link.set_monitor(monitor); + } + + public File backing_file() { + return log.file(); + } + + public AltosTelemetryReader (AltosLink in_link) + throws IOException, InterruptedException, TimeoutException { + link = in_link; + log = new AltosLog(link); + name = link.name; + previous = null; + telem = new LinkedBlockingQueue(); + frequency = AltosPreferences.frequency(link.serial); + set_frequency(frequency); + telemetry = AltosPreferences.telemetry(link.serial); + set_telemetry(telemetry); + link.add_monitor(telem); + } +} diff --git a/altoslib/AltosTelemetryRecord.java b/altoslib/AltosTelemetryRecord.java new file mode 100644 index 00000000..367c148d --- /dev/null +++ b/altoslib/AltosTelemetryRecord.java @@ -0,0 +1,126 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + +public abstract class AltosTelemetryRecord { + + long received_time; + abstract public AltosRecord update_state(AltosRecord previous); + + 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]; + } + + 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; + + static AltosTelemetryRecord parse_hex(String hex) throws ParseException, AltosCRCException { + AltosTelemetryRecord r; + + int[] bytes; + try { + bytes = Altos.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 = Altos.int8(bytes, bytes.length - 3) / 2 - 74; + int status = Altos.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 Altos.ao_telemetry_standard_len + 4: + int type = Altos.uint8(bytes, 4 + 1); + switch (type) { + case packet_type_TM_sensor: + case packet_type_Tm_sensor: + case packet_type_Tn_sensor: + r = new AltosTelemetryRecordSensor(bytes, rssi); + break; + case packet_type_configuration: + r = new AltosTelemetryRecordConfiguration(bytes); + break; + case packet_type_location: + r = new AltosTelemetryRecordLocation(bytes); + break; + case packet_type_satellite: + r = new AltosTelemetryRecordSatellite(bytes); + break; + case packet_type_companion: + r = new AltosTelemetryRecordCompanion(bytes); + break; + default: + r = new AltosTelemetryRecordRaw(bytes); + break; + } + break; + case Altos.ao_telemetry_0_9_len + 4: + r = new AltosTelemetryRecordLegacy(bytes, rssi, status); + break; + case Altos.ao_telemetry_0_8_len + 4: + r = new AltosTelemetryRecordLegacy(bytes, rssi, status); + break; + default: + throw new ParseException(String.format("Invalid packet length %d", bytes.length), 0); + } + r.received_time = System.currentTimeMillis(); + return r; + } + + public static AltosTelemetryRecord parse(String line) throws ParseException, AltosCRCException { + AltosTelemetryRecord r; + + 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++])); + } + + if (word[i].equals("TELEM")) + r = parse_hex(word[i+1]); + else + r = new AltosTelemetryRecordLegacy(line); + return r; + } +} diff --git a/altoslib/AltosTelemetryRecordCompanion.java b/altoslib/AltosTelemetryRecordCompanion.java new file mode 100644 index 00000000..6ad17244 --- /dev/null +++ b/altoslib/AltosTelemetryRecordCompanion.java @@ -0,0 +1,52 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + +public class AltosTelemetryRecordCompanion extends AltosTelemetryRecordRaw { + + AltosRecordCompanion companion; + + public AltosTelemetryRecordCompanion(int[] in_bytes) { + super(in_bytes); + + int off = 0; + if (uint8(6) == 0) + off = 1; + int channels = uint8(7+off); + + if (off != 0 && channels >= 12) + channels = 11; + + companion = new AltosRecordCompanion(channels); + companion.tick = tick; + companion.board_id = uint8(5); + companion.update_period = uint8(6+off); + for (int i = 0; i < companion.companion_data.length; i++) + companion.companion_data[i] = uint16(8 + off + i * 2); + } + + public AltosRecord update_state(AltosRecord previous) { + AltosRecord next = super.update_state(previous); + + next.companion = companion; + next.seen |= AltosRecord.seen_sensor | AltosRecord.seen_temp_volt; + + companion.tick = tick; + return next; + } +} diff --git a/altoslib/AltosTelemetryRecordConfiguration.java b/altoslib/AltosTelemetryRecordConfiguration.java new file mode 100644 index 00000000..25242edc --- /dev/null +++ b/altoslib/AltosTelemetryRecordConfiguration.java @@ -0,0 +1,64 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + + +public class AltosTelemetryRecordConfiguration extends AltosTelemetryRecordRaw { + int device_type; + int flight; + int config_major; + int config_minor; + int apogee_delay; + int main_deploy; + int flight_log_max; + String callsign; + String version; + + public AltosTelemetryRecordConfiguration(int[] in_bytes) { + super(in_bytes); + + device_type = uint8(5); + flight = uint16(6); + config_major = uint8(8); + config_minor = uint8(9); + apogee_delay = uint16(10); + main_deploy = uint16(12); + flight_log_max = uint16(14); + callsign = string(16, 8); + version = string(24, 8); + } + + public AltosRecord update_state(AltosRecord previous) { + AltosRecord next = super.update_state(previous); + + next.device_type = device_type; + next.flight = flight; + next.config_major = config_major; + next.config_minor = config_minor; + next.apogee_delay = apogee_delay; + next.main_deploy = main_deploy; + next.flight_log_max = flight_log_max; + + next.callsign = callsign; + next.firmware_version = version; + + next.seen |= AltosRecord.seen_deploy | AltosRecord.seen_flight; + + return next; + } +} diff --git a/altoslib/AltosTelemetryRecordGeneral.java b/altoslib/AltosTelemetryRecordGeneral.java new file mode 100644 index 00000000..5e157a54 --- /dev/null +++ b/altoslib/AltosTelemetryRecordGeneral.java @@ -0,0 +1,43 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + +import java.lang.*; +import java.text.*; +import java.util.HashMap; + +public class AltosTelemetryRecordGeneral { + + static AltosTelemetryRecord parse(String line) throws ParseException, AltosCRCException { + AltosTelemetryRecord r; + + 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++])); + } + + if (word[i].equals("TELEM")) + r = AltosTelemetryRecordRaw.parse(word[i+1]); + else + r = new AltosTelemetryRecordLegacy(line); + return r; + } +} diff --git a/altoslib/AltosTelemetryRecordLegacy.java b/altoslib/AltosTelemetryRecordLegacy.java new file mode 100644 index 00000000..8e3713cc --- /dev/null +++ b/altoslib/AltosTelemetryRecordLegacy.java @@ -0,0 +1,521 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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; + +import java.lang.*; +import java.text.*; +import java.util.HashMap; + +/* + * 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 AltosTelemetryRecordLegacy extends AltosTelemetryRecord { + /* + * 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"; + + AltosRecord record; + + private void parse_v4(String[] words, int i) throws ParseException { + AltosTelemetryMap map = new AltosTelemetryMap(words, i); + + record.callsign = map.get_string(AO_TELEM_CALL, "N0CALL"); + record.serial = map.get_int(AO_TELEM_SERIAL, AltosRecord.MISSING); + record.flight = map.get_int(AO_TELEM_FLIGHT, AltosRecord.MISSING); + record.rssi = map.get_int(AO_TELEM_RSSI, AltosRecord.MISSING); + record.state = Altos.state(map.get_string(AO_TELEM_STATE, "invalid")); + record.tick = map.get_int(AO_TELEM_TICK, 0); + + /* raw sensor values */ + record.accel = map.get_int(AO_TELEM_RAW_ACCEL, AltosRecord.MISSING); + record.pres = map.get_int(AO_TELEM_RAW_BARO, AltosRecord.MISSING); + record.temp = map.get_int(AO_TELEM_RAW_THERMO, AltosRecord.MISSING); + record.batt = map.get_int(AO_TELEM_RAW_BATT, AltosRecord.MISSING); + record.drogue = map.get_int(AO_TELEM_RAW_DROGUE, AltosRecord.MISSING); + record.main = map.get_int(AO_TELEM_RAW_MAIN, AltosRecord.MISSING); + + /* sensor calibration information */ + record.ground_accel = map.get_int(AO_TELEM_CAL_ACCEL_GROUND, AltosRecord.MISSING); + record.ground_pres = map.get_int(AO_TELEM_CAL_BARO_GROUND, AltosRecord.MISSING); + record.accel_plus_g = map.get_int(AO_TELEM_CAL_ACCEL_PLUS, AltosRecord.MISSING); + record.accel_minus_g = map.get_int(AO_TELEM_CAL_ACCEL_MINUS, AltosRecord.MISSING); + + /* flight computer values */ + record.acceleration = map.get_double(AO_TELEM_KALMAN_ACCEL, AltosRecord.MISSING, 1/16.0); + record.speed = map.get_double(AO_TELEM_KALMAN_SPEED, AltosRecord.MISSING, 1/16.0); + record.height = map.get_int(AO_TELEM_KALMAN_HEIGHT, AltosRecord.MISSING); + + record.flight_accel = map.get_int(AO_TELEM_ADHOC_ACCEL, AltosRecord.MISSING); + record.flight_vel = map.get_int(AO_TELEM_ADHOC_SPEED, AltosRecord.MISSING); + record.flight_pres = map.get_int(AO_TELEM_ADHOC_BARO, AltosRecord.MISSING); + + if (map.has(AO_TELEM_GPS_STATE)) { + record.gps = new AltosGPS(map); + record.new_gps = true; + } + else + record.gps = null; + } + + private void parse_legacy(String[] words, int i) throws ParseException { + + AltosParse.word (words[i++], "CALL"); + record.callsign = words[i++]; + + AltosParse.word (words[i++], "SERIAL"); + record.serial = AltosParse.parse_int(words[i++]); + + if (record.version >= 2) { + AltosParse.word (words[i++], "FLIGHT"); + record.flight = AltosParse.parse_int(words[i++]); + } else + record.flight = 0; + + AltosParse.word(words[i++], "RSSI"); + record.rssi = AltosParse.parse_int(words[i++]); + + /* Older telemetry data had mis-computed RSSI value */ + if (record.version <= 2) + record.rssi = (record.rssi + 74) / 2 - 74; + + AltosParse.word(words[i++], "STATUS"); + record.status = AltosParse.parse_hex(words[i++]); + + AltosParse.word(words[i++], "STATE"); + record.state = Altos.state(words[i++]); + + record.tick = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "a:"); + record.accel = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "p:"); + record.pres = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "t:"); + record.temp = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "v:"); + record.batt = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "d:"); + record.drogue = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "m:"); + record.main = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "fa:"); + record.flight_accel = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "ga:"); + record.ground_accel = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "fv:"); + record.flight_vel = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "fp:"); + record.flight_pres = AltosParse.parse_int(words[i++]); + + /* Old TeleDongle code with kalman-reporting TeleMetrum code */ + if ((record.flight_vel & 0xffff0000) == 0x80000000) { + record.speed = ((short) record.flight_vel) / 16.0; + record.acceleration = record.flight_accel / 16.0; + record.height = record.flight_pres; + record.flight_vel = AltosRecord.MISSING; + record.flight_pres = AltosRecord.MISSING; + record.flight_accel = AltosRecord.MISSING; + } + + AltosParse.word(words[i++], "gp:"); + record.ground_pres = AltosParse.parse_int(words[i++]); + + if (record.version >= 1) { + AltosParse.word(words[i++], "a+:"); + record.accel_plus_g = AltosParse.parse_int(words[i++]); + + AltosParse.word(words[i++], "a-:"); + record.accel_minus_g = AltosParse.parse_int(words[i++]); + } else { + record.accel_plus_g = record.ground_accel; + record.accel_minus_g = record.ground_accel + 530; + } + + record.gps = new AltosGPS(words, i, record.version); + record.new_gps = true; + } + + public AltosTelemetryRecordLegacy(String line) throws ParseException, AltosCRCException { + String[] words = line.split("\\s+"); + int i = 0; + + record = new AltosRecord(); + + if (words[i].equals("CRC") && words[i+1].equals("INVALID")) { + i += 2; + AltosParse.word(words[i++], "RSSI"); + record.rssi = AltosParse.parse_int(words[i++]); + throw new AltosCRCException(record.rssi); + } + if (words[i].equals("CALL")) { + record.version = 0; + } else { + AltosParse.word (words[i++], "VERSION"); + record.version = AltosParse.parse_int(words[i++]); + } + + if (record.version < 4) + parse_legacy(words, i); + else + parse_v4(words, i); + } + + /* + * Given a hex dump of a legacy telemetry line, construct an AltosRecord 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 AltosTelemetryRecordLegacy(int[] in_bytes, int in_rssi, int in_status) { + record = new AltosRecord(); + + bytes = in_bytes; + record.version = 4; + adjust = 0; + + if (bytes.length == AltosLib.ao_telemetry_0_8_len + 4) { + record.serial = uint8(0); + adjust = -1; + } else + record.serial = uint16(0); + + record.seen = AltosRecord.seen_flight | AltosRecord.seen_sensor | AltosRecord.seen_temp_volt | AltosRecord.seen_deploy; + + record.callsign = string(62, 8); + record.flight = uint16(2); + record.rssi = in_rssi; + record.status = in_status; + record.state = uint8(4); + record.tick = uint16(21); + record.accel = int16(23); + record.pres = int16(25); + record.temp = int16(27); + record.batt = int16(29); + record.drogue = int16(31); + record.main = int16(33); + + record.ground_accel = int16(7); + record.ground_pres = int16(15); + record.accel_plus_g = int16(17); + record.accel_minus_g = int16(19); + + if (uint16(11) == 0x8000) { + record.acceleration = int16(5); + record.speed = int16(9); + record.height = int16(13); + record.flight_accel = AltosRecord.MISSING; + record.flight_vel = AltosRecord.MISSING; + record.flight_pres = AltosRecord.MISSING; + } else { + record.flight_accel = int16(5); + record.flight_vel = uint32(9); + record.flight_pres = int16(13); + record.acceleration = AltosRecord.MISSING; + record.speed = AltosRecord.MISSING; + record.height = AltosRecord.MISSING; + } + + record.gps = null; + + int gps_flags = uint8(41); + + if ((gps_flags & (AO_GPS_VALID|AO_GPS_RUNNING)) != 0) { + record.gps = new AltosGPS(); + record.new_gps = true; + + record.seen |= record.seen_gps_time | record.seen_gps_lat | record.seen_gps_lon; + record.gps.nsat = (gps_flags & AO_GPS_NUM_SAT_MASK); + record.gps.locked = (gps_flags & AO_GPS_VALID) != 0; + record.gps.connected = true; + record.gps.lat = uint32(42) / 1.0e7; + record.gps.lon = uint32(46) / 1.0e7; + record.gps.alt = int16(50); + record.gps.ground_speed = uint16(52) / 100.0; + record.gps.course = uint8(54) * 2; + record.gps.hdop = uint8(55) / 5.0; + record.gps.h_error = uint16(58); + record.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) { + record.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) + record.gps.cc_gps_sat[n_tracking_actual++] = new AltosGPSSat(svid, c_n0); + } + } + } + + record.time = 0.0; + } + + public AltosRecord update_state(AltosRecord previous) { + return record; + } +} diff --git a/altoslib/AltosTelemetryRecordLocation.java b/altoslib/AltosTelemetryRecordLocation.java new file mode 100644 index 00000000..cddb773d --- /dev/null +++ b/altoslib/AltosTelemetryRecordLocation.java @@ -0,0 +1,93 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + + +public class AltosTelemetryRecordLocation extends AltosTelemetryRecordRaw { + int flags; + int altitude; + int latitude; + int longitude; + int year; + int month; + int day; + int hour; + int minute; + int second; + int pdop; + int hdop; + int vdop; + int mode; + int ground_speed; + int climb_rate; + int course; + + public AltosTelemetryRecordLocation(int[] in_bytes) { + super(in_bytes); + + flags = uint8(5); + altitude = int16(6); + latitude = uint32(8); + longitude = uint32(12); + year = uint8(16); + month = uint8(17); + day = uint8(18); + hour = uint8(19); + minute = uint8(20); + second = uint8(21); + pdop = uint8(22); + hdop = uint8(23); + vdop = uint8(24); + mode = uint8(25); + ground_speed = uint16(26); + climb_rate = int16(28); + course = uint8(30); + } + + public AltosRecord update_state(AltosRecord previous) { + AltosRecord next = super.update_state(previous); + + if (next.gps == null) + next.gps = new AltosGPS(); + + next.gps.nsat = flags & 0xf; + next.gps.locked = (flags & (1 << 4)) != 0; + next.gps.connected = (flags & (1 << 5)) != 0; + + if (next.gps.locked) { + next.gps.lat = latitude * 1.0e-7; + next.gps.lon = longitude * 1.0e-7; + next.gps.alt = altitude; + next.gps.year = 2000 + year; + next.gps.month = month; + next.gps.day = day; + next.gps.hour = hour; + next.gps.minute = minute; + next.gps.second = second; + next.gps.ground_speed = ground_speed * 1.0e-2; + next.gps.course = course * 2; + next.gps.climb_rate = climb_rate * 1.0e-2; + next.gps.hdop = hdop; + next.gps.vdop = vdop; + next.seen |= AltosRecord.seen_gps_time | AltosRecord.seen_gps_lat | AltosRecord.seen_gps_lon; + next.new_gps = true; + } + + return next; + } +} diff --git a/altoslib/AltosTelemetryRecordRaw.java b/altoslib/AltosTelemetryRecordRaw.java new file mode 100644 index 00000000..43d0f17a --- /dev/null +++ b/altoslib/AltosTelemetryRecordRaw.java @@ -0,0 +1,77 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + +import java.lang.*; +import java.text.*; +import java.util.HashMap; + +public class AltosTelemetryRecordRaw extends AltosTelemetryRecord { + int[] bytes; + int serial; + int tick; + int type; + + long received_time; + + public int int8(int off) { + return AltosLib.int8(bytes, off + 1); + } + + public int uint8(int off) { + return AltosLib.uint8(bytes, off + 1); + } + + public int int16(int off) { + return AltosLib.int16(bytes, off + 1); + } + + public int uint16(int off) { + return AltosLib.uint16(bytes, off + 1); + } + + public int uint32(int off) { + return AltosLib.uint32(bytes, off + 1); + } + + public String string(int off, int l) { + return AltosLib.string(bytes, off + 1, l); + } + + public AltosTelemetryRecordRaw(int[] in_bytes) { + bytes = in_bytes; + serial = uint16(0); + tick = uint16(2); + type = uint8(4); + } + + public AltosRecord update_state(AltosRecord previous) { + AltosRecord next; + if (previous != null) + next = new AltosRecord(previous); + else + next = new AltosRecord(); + next.serial = serial; + next.tick = tick; + return next; + } + + public long received_time() { + return received_time; + } +} diff --git a/altoslib/AltosTelemetryRecordSatellite.java b/altoslib/AltosTelemetryRecordSatellite.java new file mode 100644 index 00000000..2526afb6 --- /dev/null +++ b/altoslib/AltosTelemetryRecordSatellite.java @@ -0,0 +1,52 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + +public class AltosTelemetryRecordSatellite extends AltosTelemetryRecordRaw { + int channels; + AltosGPSSat[] sats; + + public AltosTelemetryRecordSatellite(int[] in_bytes) { + super(in_bytes); + + channels = uint8(5); + if (channels > 12) + channels = 12; + if (channels == 0) + sats = null; + else { + sats = new AltosGPSSat[channels]; + for (int i = 0; i < channels; i++) { + int svid = uint8(6 + i * 2 + 0); + int c_n_1 = uint8(6 + i * 2 + 1); + sats[i] = new AltosGPSSat(svid, c_n_1); + } + } + } + + public AltosRecord update_state(AltosRecord previous) { + AltosRecord next = super.update_state(previous); + + if (next.gps == null) + next.gps = new AltosGPS(); + + next.gps.cc_gps_sat = sats; + + return next; + } +} diff --git a/altoslib/AltosTelemetryRecordSensor.java b/altoslib/AltosTelemetryRecordSensor.java new file mode 100644 index 00000000..cfaf90b0 --- /dev/null +++ b/altoslib/AltosTelemetryRecordSensor.java @@ -0,0 +1,104 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; + + +public class AltosTelemetryRecordSensor extends AltosTelemetryRecordRaw { + int state; + int accel; + int pres; + int temp; + int v_batt; + int sense_d; + int sense_m; + + int acceleration; + int speed; + int height; + + int ground_accel; + int ground_pres; + int accel_plus_g; + int accel_minus_g; + + int rssi; + + public AltosTelemetryRecordSensor(int[] in_bytes, int in_rssi) { + super(in_bytes); + state = uint8(5); + + accel = int16(6); + pres = int16(8); + temp = int16(10); + v_batt = int16(12); + sense_d = int16(14); + sense_m = int16(16); + + acceleration = int16(18); + speed = int16(20); + height = int16(22); + + ground_pres = int16(24); + ground_accel = int16(26); + accel_plus_g = int16(28); + accel_minus_g = int16(30); + + rssi = in_rssi; + } + + public AltosRecord update_state(AltosRecord previous) { + AltosRecord next = super.update_state(previous); + + next.state = state; + if (type == packet_type_TM_sensor) + next.accel = accel; + else + next.accel = AltosRecord.MISSING; + next.pres = pres; + next.temp = temp; + next.batt = v_batt; + if (type == packet_type_TM_sensor || type == packet_type_Tm_sensor) { + next.drogue = sense_d; + next.main = sense_m; + } else { + next.drogue = AltosRecord.MISSING; + next.main = AltosRecord.MISSING; + } + + next.acceleration = acceleration / 16.0; + next.speed = speed / 16.0; + next.height = height; + + next.ground_pres = ground_pres; + if (type == packet_type_TM_sensor) { + next.ground_accel = ground_accel; + next.accel_plus_g = accel_plus_g; + next.accel_minus_g = accel_minus_g; + } else { + next.ground_accel = AltosRecord.MISSING; + next.accel_plus_g = AltosRecord.MISSING; + next.accel_minus_g = AltosRecord.MISSING; + } + + next.rssi = rssi; + + next.seen |= AltosRecord.seen_sensor | AltosRecord.seen_temp_volt; + + return next; + } +} diff --git a/altoslib/Makefile.am b/altoslib/Makefile.am index 2ddd24e6..4262daca 100644 --- a/altoslib/Makefile.am +++ b/altoslib/Makefile.am @@ -4,7 +4,7 @@ JAVAROOT=bin CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH="bin:$(FREETTS)/*:/usr/share/java/*" -SRC=src/org/altusmetrum/AltosLib +SRC=. BIN=bin/org/altusmetrum/AltosLib AltosLibdir = $(datadir)/java diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosCRCException.java b/altoslib/src/org/altusmetrum/AltosLib/AltosCRCException.java deleted file mode 100644 index 101c5363..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosCRCException.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -public class AltosCRCException extends Exception { - public int rssi; - - public AltosCRCException (int in_rssi) { - rssi = in_rssi; - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosConfigData.java b/altoslib/src/org/altusmetrum/AltosLib/AltosConfigData.java deleted file mode 100644 index 4ad4e58a..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosConfigData.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.util.concurrent.*; -import org.altusmetrum.AltosLib.*; - -public class AltosConfigData implements Iterable { - - /* Version information */ - public String manufacturer; - public String product; - public String version; - public int log_format; - public int serial; - - /* Strings returned */ - public LinkedList lines; - - /* Config information */ - public int config_major; - public int config_minor; - public int main_deploy; - public int apogee_delay; - public int radio_channel; - public int radio_setting; - public int radio_frequency; - public String callsign; - public int accel_cal_plus, accel_cal_minus; - public int radio_calibration; - public int flight_log_max; - public int ignite_mode; - public int stored_flight; - public int storage_size; - public int storage_erase_unit; - - public static String get_string(String line, String label) throws ParseException { - if (line.startsWith(label)) { - String quoted = line.substring(label.length()).trim(); - - if (quoted.startsWith("\"")) - quoted = quoted.substring(1); - if (quoted.endsWith("\"")) - quoted = quoted.substring(0,quoted.length()-1); - return quoted; - } - throw new ParseException("mismatch", 0); - } - - public static int get_int(String line, String label) throws NumberFormatException, ParseException { - if (line.startsWith(label)) { - String tail = line.substring(label.length()).trim(); - String[] tokens = tail.split("\\s+"); - if (tokens.length > 0) - return Integer.parseInt(tokens[0]); - } - throw new ParseException("mismatch", 0); - } - - public Iterator iterator() { - return lines.iterator(); - } - - public int log_available() { - switch (log_format) { - case AltosLib.AO_LOG_FORMAT_TINY: - if (stored_flight == 0) - return 1; - return 0; - default: - if (flight_log_max <= 0) - return 1; - int log_space = storage_size - storage_erase_unit; - int log_used = stored_flight * flight_log_max; - - if (log_used >= log_space) - return 0; - return (log_space - log_used) / flight_log_max; - } - } - - int[] parse_version(String v) { - String[] parts = v.split("\\."); - int r[] = new int[parts.length]; - - for (int i = 0; i < parts.length; i++) { - try { - r[i] = Altos.fromdec(parts[i]); - } catch (NumberFormatException n) { - r[i] = 0; - } - } - - return r; - } - - public int compare_version(String other) { - int[] me = parse_version(version); - int[] them = parse_version(other); - - int l = Math.min(me.length, them.length); - - for (int i = 0; i < l; i++) { - int d = me[i] - them[i]; - if (d != 0) - return d; - } - if (me.length > l) - return 1; - if (them.length > l) - return -1; - return 0; - } - - public AltosConfigData(AltosLink link) throws InterruptedException, TimeoutException { - link.printf("c s\np\nf\nl\nv\n"); - lines = new LinkedList(); - radio_setting = 0; - radio_frequency = 0; - stored_flight = 0; - for (;;) { - String line = link.get_reply(); - if (line == null) - throw new TimeoutException(); - if (line.contains("Syntax error")) - continue; - lines.add(line); - try { serial = get_int(line, "serial-number"); } catch (Exception e) {} - try { log_format = get_int(line, "log-format"); } catch (Exception e) {} - try { main_deploy = get_int(line, "Main deploy:"); } catch (Exception e) {} - try { apogee_delay = get_int(line, "Apogee delay:"); } catch (Exception e) {} - try { radio_channel = get_int(line, "Radio channel:"); } catch (Exception e) {} - try { radio_setting = get_int(line, "Radio setting:"); } catch (Exception e) {} - try { - radio_frequency = get_int(line, "Frequency:"); - if (radio_frequency < 0) - radio_frequency = 434550; - } catch (Exception e) {} - try { - if (line.startsWith("Accel cal")) { - String[] bits = line.split("\\s+"); - if (bits.length >= 6) { - accel_cal_plus = Integer.parseInt(bits[3]); - accel_cal_minus = Integer.parseInt(bits[5]); - } - } - } catch (Exception e) {} - try { radio_calibration = get_int(line, "Radio cal:"); } catch (Exception e) {} - try { flight_log_max = get_int(line, "Max flight log:"); } catch (Exception e) {} - try { ignite_mode = get_int(line, "Ignite mode:"); } catch (Exception e) {} - try { callsign = get_string(line, "Callsign:"); } catch (Exception e) {} - try { version = get_string(line,"software-version"); } catch (Exception e) {} - try { product = get_string(line,"product"); } catch (Exception e) {} - - try { get_int(line, "flight"); stored_flight++; } catch (Exception e) {} - try { storage_size = get_int(line, "Storage size:"); } catch (Exception e) {} - try { storage_erase_unit = get_int(line, "Storage erase unit"); } catch (Exception e) {} - - /* signals the end of the version info */ - if (line.startsWith("software-version")) - break; - } - } - -} \ No newline at end of file diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosConvert.java b/altoslib/src/org/altusmetrum/AltosLib/AltosConvert.java deleted file mode 100644 index 3527b575..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosConvert.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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. - */ - -/* - * Sensor data conversion functions - */ -package org.altusmetrum.AltosLib; - -public class AltosConvert { - /* - * Pressure Sensor Model, version 1.1 - * - * written by Holly Grimes - * - * Uses the International Standard Atmosphere as described in - * "A Quick Derivation relating altitude to air pressure" (version 1.03) - * from the Portland State Aerospace Society, except that the atmosphere - * is divided into layers with each layer having a different lapse rate. - * - * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007 - * at site MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */ - return 0; - - /* calculate the base temperature and pressure for the atmospheric layer - associated with the inputted altitude */ - for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) { - delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; - if (lapse_rate[layer_number] == 0.0) { - exponent = GRAVITATIONAL_ACCELERATION * delta_z - / AIR_GAS_CONSTANT / base_temperature; - base_pressure *= Math.exp(exponent); - } - else { - base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; - exponent = GRAVITATIONAL_ACCELERATION / - (AIR_GAS_CONSTANT * lapse_rate[layer_number]); - base_pressure *= Math.pow(base, exponent); - } - base_temperature += delta_z * lapse_rate[layer_number]; - } - - /* calculate the pressure at the inputted altitude */ - delta_z = altitude - base_altitude[layer_number]; - if (lapse_rate[layer_number] == 0.0) { - exponent = GRAVITATIONAL_ACCELERATION * delta_z - / AIR_GAS_CONSTANT / base_temperature; - pressure = base_pressure * Math.exp(exponent); - } - else { - base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; - exponent = GRAVITATIONAL_ACCELERATION / - (AIR_GAS_CONSTANT * lapse_rate[layer_number]); - pressure = base_pressure * Math.pow(base, exponent); - } - - return pressure; - } - - -/* outputs the altitude associated with the given pressure. the altitude - returned is measured with respect to the mean sea level */ - public static double - pressure_to_altitude(double pressure) - { - - double next_base_temperature = LAYER0_BASE_TEMPERATURE; - double next_base_pressure = LAYER0_BASE_PRESSURE; - - double altitude; - double base_pressure; - double base_temperature; - double base; /* base for function to determine base pressure of next layer */ - double exponent; /* exponent for function to determine base pressure - of next layer */ - double coefficient; - int layer_number; /* identifies layer in the atmosphere */ - int delta_z; /* difference between two altitudes */ - - if (pressure < 0) /* illegal pressure */ - return -1; - if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */ - return MAXIMUM_ALTITUDE; - - /* calculate the base temperature and pressure for the atmospheric layer - associated with the inputted pressure. */ - layer_number = -1; - do { - layer_number++; - base_pressure = next_base_pressure; - base_temperature = next_base_temperature; - delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; - if (lapse_rate[layer_number] == 0.0) { - exponent = GRAVITATIONAL_ACCELERATION * delta_z - / AIR_GAS_CONSTANT / base_temperature; - next_base_pressure *= Math.exp(exponent); - } - else { - base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; - exponent = GRAVITATIONAL_ACCELERATION / - (AIR_GAS_CONSTANT * lapse_rate[layer_number]); - next_base_pressure *= Math.pow(base, exponent); - } - next_base_temperature += delta_z * lapse_rate[layer_number]; - } - while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure); - - /* calculate the altitude associated with the inputted pressure */ - if (lapse_rate[layer_number] == 0.0) { - coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION) - * base_temperature; - altitude = base_altitude[layer_number] - + coefficient * Math.log(pressure / base_pressure); - } - else { - base = pressure / base_pressure; - exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number] - / GRAVITATIONAL_ACCELERATION; - coefficient = base_temperature / lapse_rate[layer_number]; - altitude = base_altitude[layer_number] - + coefficient * (Math.pow(base, exponent) - 1); - } - - return altitude; - } - - public static double - cc_battery_to_voltage(double battery) - { - return battery / 32767.0 * 5.0; - } - - public static double - cc_ignitor_to_voltage(double ignite) - { - return ignite / 32767 * 15.0; - } - - public static double radio_to_frequency(int freq, int setting, int cal, int channel) { - double f; - - if (freq > 0) - f = freq / 1000.0; - else { - if (setting <= 0) - setting = cal; - f = 434.550 * setting / cal; - /* Round to nearest 50KHz */ - f = Math.floor (20.0 * f + 0.5) / 20.0; - } - return f + channel * 0.100; - } - - public static int radio_frequency_to_setting(double frequency, int cal) { - double set = frequency / 434.550 * cal; - - return (int) Math.floor (set + 0.5); - } - - public static int radio_frequency_to_channel(double frequency) { - int channel = (int) Math.floor ((frequency - 434.550) / 0.100 + 0.5); - - if (channel < 0) - channel = 0; - if (channel > 9) - channel = 9; - return channel; - } - - public static double radio_channel_to_frequency(int channel) { - return 434.550 + channel * 0.100; - } - - public static int[] ParseHex(String line) { - String[] tokens = line.split("\\s+"); - int[] array = new int[tokens.length]; - - for (int i = 0; i < tokens.length; i++) - try { - array[i] = Integer.parseInt(tokens[i], 16); - } catch (NumberFormatException ne) { - return null; - } - return array; - } - - public static double meters_to_feet(double meters) { - return meters * (100 / (2.54 * 12)); - } - - public static double meters_to_mach(double meters) { - return meters / 343; /* something close to mach at usual rocket sites */ - } - - public static double meters_to_g(double meters) { - return meters / 9.80665; - } - - public static int checksum(int[] data, int start, int length) { - int csum = 0x5a; - for (int i = 0; i < length; i++) - csum += data[i + start]; - return csum & 0xff; - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosEepromChunk.java b/altoslib/src/org/altusmetrum/AltosLib/AltosEepromChunk.java deleted file mode 100644 index 6d889723..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosEepromChunk.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.concurrent.*; - -public class AltosEepromChunk { - - public static final int chunk_size = 256; - public static final int per_line = 8; - - public int data[]; - public int address; - public ParseException parse_exception = null; - - int[] ParseHex(String line) { - String[] tokens = line.split("\\s+"); - int[] array = new int[tokens.length]; - - for (int i = 0; i < tokens.length; i++) - try { - array[i] = Integer.parseInt(tokens[i], 16); - } catch (NumberFormatException ne) { - return null; - } - return array; - } - - public int data(int offset) { - return data[offset]; - } - - public int data16(int offset) { - return data[offset] | (data[offset + 1] << 8); - } - - public int data32(int offset) { - return data[offset] | (data[offset + 1] << 8) | - (data[offset+2] << 16) | (data[offset+3] << 24); - } - - public boolean erased(int start, int len) { - for (int i = 0; i < len; i++) - if (data[start+i] != 0xff) - return false; - return true; - } - - public AltosEepromChunk(AltosLink link, int block, boolean flush) - throws TimeoutException, InterruptedException { - - int offset; - - data = new int[chunk_size]; - address = block * chunk_size; - if (flush) - link.flush_input(); - link.printf("e %x\n", block); - - for (offset = 0; offset < chunk_size; offset += per_line) { - try { - String line = link.get_reply(5000); - - if (line == null) - throw new TimeoutException(); - - int[] values = ParseHex(line); - - if (values == null || values.length != per_line + 1) - throw new ParseException(String.format("invalid line %s", line), 0); - if (values[0] != offset) - throw new ParseException(String.format("data address out of sync at 0x%x", - address + offset), 0); - for (int i = 0; i < per_line; i++) - data[offset + i] = values[1 + i]; - } catch (ParseException pe) { - for (int i = 0; i < per_line; i++) - data[offset + i] = 0xff; - if (parse_exception == null) - parse_exception = pe; - } - } - } -} \ No newline at end of file diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosEepromIterable.java b/altoslib/src/org/altusmetrum/AltosLib/AltosEepromIterable.java deleted file mode 100644 index f1397c7b..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosEepromIterable.java +++ /dev/null @@ -1,475 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.util.concurrent.LinkedBlockingQueue; - -/* - * AltosRecords with an index field so they can be sorted by tick while preserving - * the original ordering for elements with matching ticks - */ -class AltosOrderedRecord extends AltosEepromRecord implements Comparable { - - public int index; - - public AltosOrderedRecord(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 AltosOrderedRecord(int in_cmd, int in_tick, int in_a, int in_b, int in_index) { - super(in_cmd, in_tick, in_a, in_b); - index = in_index; - } - - public String toString() { - return String.format("%d.%d %04x %04x %04x", - cmd, index, tick, a, b); - } - - public int compareTo(AltosOrderedRecord o) { - int tick_diff = tick - o.tick; - if (tick_diff != 0) - return tick_diff; - return index - o.index; - } -} - -public class AltosEepromIterable 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; - - AltosEepromRecord flight_record; - AltosEepromRecord gps_date_record; - - TreeSet records; - - LinkedList 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(AltosRecord state, AltosEepromRecord record, EepromState eeprom) { - state.tick = record.tick; - switch (record.cmd) { - case AltosLib.AO_LOG_FLIGHT: - eeprom.seen |= seen_flight; - state.ground_accel = record.a; - state.flight_accel = record.a; - state.flight = record.b; - eeprom.boost_tick = record.tick; - break; - case 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 & 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_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 |= seen_sensor; - break; - case AltosLib.AO_LOG_TEMP_VOLT: - state.temp = record.a; - state.batt = record.b; - eeprom.seen |= seen_temp_volt; - break; - case AltosLib.AO_LOG_DEPLOY: - state.drogue = record.a; - state.main = record.b; - eeprom.seen |= 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; - 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.new_gps = true; - has_gps = true; - break; - case AltosLib.AO_LOG_GPS_LAT: - int lat32 = record.a | (record.b << 16); - state.gps.lat = (double) lat32 / 1e7; - break; - case AltosLib.AO_LOG_GPS_LON: - int lon32 = record.a | (record.b << 16); - state.gps.lon = (double) lon32 / 1e7; - break; - case AltosLib.AO_LOG_GPS_ALT: - 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; - state.gps.add_sat(svid, c_n0); - } - break; - case AltosLib.AO_LOG_GPS_DATE: - state.gps.year = (record.a & 0xff) + 2000; - state.gps.month = record.a >> 8; - state.gps.day = record.b & 0xff; - break; - - case 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 make_list() { - LinkedList list = new LinkedList(); - Iterator iterator = records.iterator(); - AltosOrderedRecord record = null; - AltosRecord state = new AltosRecord(); - 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) { - AltosRecord r = new AltosRecord(state); - r.time = (r.tick - eeprom.boost_tick) / 100.0; - list.add(r); - } - update_state(state, record, eeprom); - } - AltosRecord r = new AltosRecord(state); - r.time = (r.tick - eeprom.boost_tick) / 100.0; - list.add(r); - return list; - } - - public Iterator 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 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 Altos.AO_LOG_BARO_RESERVED: - out.printf ("# Baro reserved: %d\n", record.a); - break; - case Altos.AO_LOG_BARO_SENS: - out.printf ("# Baro sens: %d\n", record.a); - break; - case Altos.AO_LOG_BARO_OFF: - out.printf ("# Baro off: %d\n", record.a); - break; - case Altos.AO_LOG_BARO_TCS: - out.printf ("# Baro tcs: %d\n", record.a); - break; - case Altos.AO_LOG_BARO_TCO: - out.printf ("# Baro tco: %d\n", record.a); - break; - case Altos.AO_LOG_BARO_TREF: - out.printf ("# Baro tref: %d\n", record.a); - break; - case Altos.AO_LOG_BARO_TEMPSENS: - out.printf ("# Baro tempsens: %d\n", record.a); - break; - case Altos.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 AltosEepromIterable (FileInputStream input) { - records = new TreeSet(); - - 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 = AltosRecord.gets(input); - if (line == null) - break; - AltosOrderedRecord record = new AltosOrderedRecord(line, index++, prev_tick, prev_tick_valid); - if (record == null) - break; - 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 iterator = records.iterator(); - while (iterator.hasNext()) { - AltosOrderedRecord old = iterator.next(); - if (old.cmd == AltosLib.AO_LOG_GPS_TIME && - old.a == -1 && old.b == -1) - { - update_time(record, old); - } - } - missing_time = false; - } - } - - if (record.cmd == AltosLib.AO_LOG_GPS_LAT) { - if (last_gps_time == null || last_gps_time.tick != record.tick) { - AltosOrderedRecord add_gps_time = new AltosOrderedRecord(AltosLib.AO_LOG_GPS_TIME, - record.tick, - -1, -1, index-1); - if (last_gps_time != null) - update_time(last_gps_time, add_gps_time); - else - missing_time = true; - - records.add(add_gps_time); - record.index = index++; - } - } - records.add(record); - - /* Bail after reading the 'landed' record; we're all done */ - if (record.cmd == AltosLib.AO_LOG_STATE && - record.a == AltosLib.ao_flight_landed) - break; - } - } catch (IOException io) { - } catch (ParseException pe) { - } - try { - input.close(); - } catch (IOException ie) { - } - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosEepromLog.java b/altoslib/src/org/altusmetrum/AltosLib/AltosEepromLog.java deleted file mode 100644 index 7fca4bd9..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosEepromLog.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.util.concurrent.*; - -/* - * Extract a bit of information from an eeprom-stored flight log. - */ - -public class AltosEepromLog { - public int serial; - public boolean has_flight; - public int flight; - public int start_block; - public int end_block; - - public int year, month, day; - - public boolean selected; - - public AltosEepromLog(AltosConfigData config_data, - AltosLink link, - int in_flight, int in_start_block, - int in_end_block) - throws InterruptedException, TimeoutException { - - int block; - boolean has_date = false; - - flight = in_flight; - if (flight != 0) - has_flight = true; - start_block = in_start_block; - end_block = in_end_block; - serial = config_data.serial; - - /* - * Select all flights for download - */ - selected = true; - - /* - * Look in TeleMetrum log data for date - */ - if (config_data.log_format == AltosLib.AO_LOG_FORMAT_UNKNOWN || - config_data.log_format == AltosLib.AO_LOG_FORMAT_FULL) - { - /* - * Only look in the first two blocks so that this - * process doesn't take a long time - */ - if (in_end_block > in_start_block + 2) - in_end_block = in_start_block + 2; - - for (block = in_start_block; block < in_end_block; block++) { - AltosEepromChunk eechunk = new AltosEepromChunk(link, block, block == in_start_block); - - for (int i = 0; i < eechunk.chunk_size; i += AltosEepromRecord.record_length) { - try { - AltosEepromRecord r = new AltosEepromRecord(eechunk, i); - - if (r.cmd == AltosLib.AO_LOG_FLIGHT) { - flight = r.b; - has_flight = true; - } - if (r.cmd == AltosLib.AO_LOG_GPS_DATE) { - year = 2000 + (r.a & 0xff); - month = (r.a >> 8) & 0xff; - day = (r.b & 0xff); - has_date = true; - } - } catch (ParseException pe) { - } - } - if (has_date && has_flight) - break; - } - } - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosEepromRecord.java b/altoslib/src/org/altusmetrum/AltosLib/AltosEepromRecord.java deleted file mode 100644 index 1e845f46..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosEepromRecord.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.util.concurrent.*; - -public class AltosEepromRecord { - public int cmd; - public int tick; - public int a; - public int b; - public String data; - public boolean tick_valid; - - public static final int record_length = 8; - - public AltosEepromRecord (AltosEepromChunk chunk, int start) throws ParseException { - - cmd = chunk.data(start); - tick_valid = true; - - tick_valid = !chunk.erased(start, record_length); - if (tick_valid) { - if (AltosConvert.checksum(chunk.data, start, record_length) != 0) - throw new ParseException(String.format("invalid checksum at 0x%x", - chunk.address + start), 0); - } else { - cmd = AltosLib.AO_LOG_INVALID; - } - - tick = chunk.data16(start + 2); - a = chunk.data16(start + 4); - b = chunk.data16(start + 6); - - data = null; - } - - public AltosEepromRecord (String line) { - tick_valid = false; - tick = 0; - a = 0; - b = 0; - data = null; - if (line == null) { - cmd = AltosLib.AO_LOG_INVALID; - data = ""; - } else { - try { - String[] tokens = line.split("\\s+"); - - if (tokens[0].length() == 1) { - if (tokens.length != 4) { - cmd = AltosLib.AO_LOG_INVALID; - data = line; - } else { - cmd = tokens[0].codePointAt(0); - tick = Integer.parseInt(tokens[1],16); - tick_valid = true; - a = Integer.parseInt(tokens[2],16); - b = Integer.parseInt(tokens[3],16); - } - } else if (tokens[0].equals("Config") && tokens[1].equals("version:")) { - cmd = AltosLib.AO_LOG_CONFIG_VERSION; - data = tokens[2]; - } else if (tokens[0].equals("Main") && tokens[1].equals("deploy:")) { - cmd = AltosLib.AO_LOG_MAIN_DEPLOY; - a = Integer.parseInt(tokens[2]); - } else if (tokens[0].equals("Apogee") && tokens[1].equals("delay:")) { - cmd = AltosLib.AO_LOG_APOGEE_DELAY; - a = Integer.parseInt(tokens[2]); - } else if (tokens[0].equals("Radio") && tokens[1].equals("channel:")) { - cmd = AltosLib.AO_LOG_RADIO_CHANNEL; - a = Integer.parseInt(tokens[2]); - } else if (tokens[0].equals("Callsign:")) { - cmd = AltosLib.AO_LOG_CALLSIGN; - data = tokens[1].replaceAll("\"",""); - } else if (tokens[0].equals("Accel") && tokens[1].equals("cal")) { - cmd = AltosLib.AO_LOG_ACCEL_CAL; - a = Integer.parseInt(tokens[3]); - b = Integer.parseInt(tokens[5]); - } else if (tokens[0].equals("Radio") && tokens[1].equals("cal:")) { - cmd = AltosLib.AO_LOG_RADIO_CAL; - a = Integer.parseInt(tokens[2]); - } else if (tokens[0].equals("Max") && tokens[1].equals("flight") && tokens[2].equals("log:")) { - cmd = AltosLib.AO_LOG_MAX_FLIGHT_LOG; - a = Integer.parseInt(tokens[3]); - } else if (tokens[0].equals("manufacturer")) { - cmd = AltosLib.AO_LOG_MANUFACTURER; - data = tokens[1]; - } else if (tokens[0].equals("product")) { - cmd = AltosLib.AO_LOG_PRODUCT; - data = tokens[1]; - } else if (tokens[0].equals("serial-number")) { - cmd = AltosLib.AO_LOG_SERIAL_NUMBER; - a = Integer.parseInt(tokens[1]); - } else if (tokens[0].equals("log-format")) { - cmd = AltosLib.AO_LOG_LOG_FORMAT; - a = Integer.parseInt(tokens[1]); - } else if (tokens[0].equals("software-version")) { - cmd = AltosLib.AO_LOG_SOFTWARE_VERSION; - data = tokens[1]; - } else { - cmd = AltosLib.AO_LOG_INVALID; - data = line; - } - } catch (NumberFormatException ne) { - cmd = AltosLib.AO_LOG_INVALID; - data = line; - } - } - } - - public AltosEepromRecord(int in_cmd, int in_tick, int in_a, int in_b) { - tick_valid = true; - cmd = in_cmd; - tick = in_tick; - a = in_a; - b = in_b; - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosEepromTeleScience.java b/altoslib/src/org/altusmetrum/AltosLib/AltosEepromTeleScience.java deleted file mode 100644 index 1758fa34..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosEepromTeleScience.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.util.concurrent.*; - -public class AltosEepromTeleScience { - public int type; - public int tick; - public int tm_state; - public int tm_tick; - public int[] data; - public boolean valid; - - public static final int AO_LOG_TELESCIENCE_START = 's'; - public static final int AO_LOG_TELESCIENCE_DATA = 'd'; - - static final int max_data = 12; - public static final int record_length = 32; - - public AltosEepromTeleScience (AltosEepromChunk chunk, int start) throws ParseException { - type = 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 { - type = AltosLib.AO_LOG_INVALID; - } - - tick = chunk.data16(start+2); - tm_tick = chunk.data16(start+4); - tm_state = chunk.data(start+6); - data = new int[max_data]; - for (int i = 0; i < max_data; i++) - data[i] = chunk.data16(start + 8 + i * 2); - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosFile.java b/altoslib/src/org/altusmetrum/AltosLib/AltosFile.java deleted file mode 100644 index d2e4f2f7..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosFile.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.lang.*; -import java.io.File; -import java.util.*; - -public class AltosFile extends File { - - 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)); - } - - public AltosFile(int serial, int flight, String extension) { - this(Calendar.getInstance().get(Calendar.YEAR), - Calendar.getInstance().get(Calendar.MONTH) + 1, - Calendar.getInstance().get(Calendar.DAY_OF_MONTH), - serial, - flight, - extension); - } - - public AltosFile(AltosRecord telem) { - this(telem.serial, telem.flight, "telem"); - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosFlightReader.java b/altoslib/src/org/altusmetrum/AltosLib/AltosFlightReader.java deleted file mode 100644 index 3fdea469..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosFlightReader.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.lang.*; -import java.text.*; -import java.io.*; -import java.util.concurrent.*; - -public class AltosFlightReader { - public String name; - - public int serial; - - public void init() { } - - public AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; } - - public void close(boolean interrupted) { } - - public void set_frequency(double frequency) throws InterruptedException, TimeoutException { } - - public void save_frequency() { } - - public void set_telemetry(int telemetry) { } - - public void save_telemetry() { } - - public void update(AltosState state) throws InterruptedException { } - - public boolean supports_telemetry(int telemetry) { return false; } - - public File backing_file() { return null; } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosFrequency.java b/altoslib/src/org/altusmetrum/AltosLib/AltosFrequency.java deleted file mode 100644 index f08ff116..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosFrequency.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - -import java.io.*; -import java.util.*; -import java.text.*; - -public class AltosFrequency { - public double frequency; - public String description; - - public String toString() { - return String.format("%7.3f MHz %-20s", - frequency, description); - } - - public String toShortString() { - return String.format("%7.3f MHz %s", - frequency, description); - } - - public boolean close(double f) { - double diff = Math.abs(frequency - f); - - return diff < 0.010; - } - - public AltosFrequency(double f, String d) { - frequency = f; - description = d; - } -} \ No newline at end of file diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosGPS.java b/altoslib/src/org/altusmetrum/AltosLib/AltosGPS.java deleted file mode 100644 index f078a469..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosGPS.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.lang.*; -import java.text.*; - -public class AltosGPS { - - public final static int MISSING = AltosRecord.MISSING; - - public int nsat; - public boolean locked; - public boolean connected; - public double lat; /* degrees (+N -S) */ - public double lon; /* degrees (+E -W) */ - public int alt; /* m */ - public int year; - public int month; - public int day; - public int hour; - public int minute; - public int second; - - public double ground_speed; /* m/s */ - public int course; /* degrees */ - public double climb_rate; /* m/s */ - public double hdop; /* unitless */ - public double vdop; /* unitless */ - public int h_error; /* m */ - public int v_error; /* m */ - - public AltosGPSSat[] cc_gps_sat; /* tracking data */ - - public void ParseGPSDate(String date) throws ParseException { - String[] ymd = date.split("-"); - if (ymd.length != 3) - throw new ParseException("error parsing GPS date " + date + " got " + ymd.length, 0); - year = AltosParse.parse_int(ymd[0]); - month = AltosParse.parse_int(ymd[1]); - day = AltosParse.parse_int(ymd[2]); - } - - public void ParseGPSTime(String time) throws ParseException { - String[] hms = time.split(":"); - if (hms.length != 3) - throw new ParseException("Error parsing GPS time " + time + " got " + hms.length, 0); - hour = AltosParse.parse_int(hms[0]); - minute = AltosParse.parse_int(hms[1]); - second = AltosParse.parse_int(hms[2]); - } - - public void ClearGPSTime() { - year = month = day = 0; - hour = minute = second = 0; - } - - public AltosGPS(AltosTelemetryMap map) throws ParseException { - String state = map.get_string(AltosTelemetry.AO_TELEM_GPS_STATE, - AltosTelemetry.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)) { - 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); - 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); - - 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); - - ground_speed = map.get_double(AltosTelemetry.AO_TELEM_GPS_HORIZONTAL_SPEED, - AltosRecord.MISSING, 1/100.0); - course = map.get_int(AltosTelemetry.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)) { - connected = true; - locked = false; - } else { - connected = false; - locked = false; - } - } - - public AltosGPS(String[] words, int i, int version) throws ParseException { - AltosParse.word(words[i++], "GPS"); - nsat = AltosParse.parse_int(words[i++]); - AltosParse.word(words[i++], "sat"); - - connected = false; - locked = false; - lat = lon = 0; - alt = 0; - ClearGPSTime(); - if ((words[i]).equals("unlocked")) { - connected = true; - i++; - } else if ((words[i]).equals("not-connected")) { - i++; - } else if (words.length >= 40) { - locked = true; - connected = true; - - if (version > 1) - ParseGPSDate(words[i++]); - else - year = month = day = 0; - ParseGPSTime(words[i++]); - lat = AltosParse.parse_coord(words[i++]); - lon = AltosParse.parse_coord(words[i++]); - alt = AltosParse.parse_int(words[i++]); - if (version > 1 || (i < words.length && !words[i].equals("SAT"))) { - ground_speed = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(H)")); - course = AltosParse.parse_int(words[i++]); - climb_rate = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(V)")); - hdop = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "(hdop)")); - h_error = AltosParse.parse_int(words[i++]); - v_error = AltosParse.parse_int(words[i++]); - } - } else { - i++; - } - if (i < words.length) { - AltosParse.word(words[i++], "SAT"); - int tracking_channels = 0; - if (words[i].equals("not-connected")) - tracking_channels = 0; - else - tracking_channels = AltosParse.parse_int(words[i]); - i++; - cc_gps_sat = new AltosGPSSat[tracking_channels]; - for (int chan = 0; chan < tracking_channels; chan++) { - cc_gps_sat[chan] = new AltosGPSSat(); - cc_gps_sat[chan].svid = AltosParse.parse_int(words[i++]); - /* Older versions included SiRF status bits */ - if (version < 2) - i++; - cc_gps_sat[chan].c_n0 = AltosParse.parse_int(words[i++]); - } - } else - cc_gps_sat = new AltosGPSSat[0]; - } - - public void set_latitude(int in_lat) { - lat = in_lat / 10.0e7; - } - - public void set_longitude(int in_lon) { - lon = in_lon / 10.0e7; - } - - public void set_time(int hour, int minute, int second) { - hour = hour; - minute = minute; - second = second; - } - - public void set_date(int year, int month, int day) { - year = year; - month = month; - day = day; - } - - public void set_flags(int flags) { - flags = flags; - } - - public void set_altitude(int altitude) { - altitude = altitude; - } - - public void add_sat(int svid, int c_n0) { - if (cc_gps_sat == null) { - cc_gps_sat = new AltosGPSSat[1]; - } else { - AltosGPSSat[] new_gps_sat = new AltosGPSSat[cc_gps_sat.length + 1]; - for (int i = 0; i < cc_gps_sat.length; i++) - new_gps_sat[i] = cc_gps_sat[i]; - cc_gps_sat = new_gps_sat; - } - AltosGPSSat sat = new AltosGPSSat(); - sat.svid = svid; - sat.c_n0 = c_n0; - cc_gps_sat[cc_gps_sat.length - 1] = sat; - } - - public AltosGPS() { - ClearGPSTime(); - cc_gps_sat = null; - } - - public AltosGPS(AltosGPS old) { - nsat = old.nsat; - locked = old.locked; - connected = old.connected; - lat = old.lat; /* degrees (+N -S) */ - lon = old.lon; /* degrees (+E -W) */ - alt = old.alt; /* m */ - year = old.year; - month = old.month; - day = old.day; - hour = old.hour; - minute = old.minute; - second = old.second; - - ground_speed = old.ground_speed; /* m/s */ - course = old.course; /* degrees */ - climb_rate = old.climb_rate; /* m/s */ - hdop = old.hdop; /* unitless? */ - h_error = old.h_error; /* m */ - v_error = old.v_error; /* m */ - - if (old.cc_gps_sat != null) { - cc_gps_sat = new AltosGPSSat[old.cc_gps_sat.length]; - for (int i = 0; i < old.cc_gps_sat.length; i++) { - cc_gps_sat[i] = new AltosGPSSat(); - cc_gps_sat[i].svid = old.cc_gps_sat[i].svid; - cc_gps_sat[i].c_n0 = old.cc_gps_sat[i].c_n0; - } - } - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosGPSSat.java b/altoslib/src/org/altusmetrum/AltosLib/AltosGPSSat.java deleted file mode 100644 index faa1ec8d..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosGPSSat.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - -public class AltosGPSSat { - public int svid; - public int c_n0; - - public AltosGPSSat(int s, int c) { - svid = s; - c_n0= c; - } - - public AltosGPSSat() { - } -} - diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosGreatCircle.java b/altoslib/src/org/altusmetrum/AltosLib/AltosGreatCircle.java deleted file mode 100644 index 76b71859..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosGreatCircle.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.lang.Math; - -public class AltosGreatCircle { - public double distance; - public double bearing; - - double sqr(double a) { return a * a; } - - static final double rad = Math.PI / 180; - static final double earth_radius = 6371.2 * 1000; /* in meters */ - - public static final int BEARING_LONG = 0; - public static final int BEARING_SHORT = 1; - public static final int BEARING_VOICE = 2; - - public String bearing_words(int length) { - String [][] bearing_string = { - { - "North", "North North East", "North East", "East North East", - "East", "East South East", "South East", "South South East", - "South", "South South West", "South West", "West South West", - "West", "West North West", "North West", "North North West" - }, { - "N", "NNE", "NE", "ENE", - "E", "ESE", "SE", "SSE", - "S", "SSW", "SW", "WSW", - "W", "WNW", "NW", "NNW" - }, { - "north", "nor nor east", "north east", "east nor east", - "east", "east sow east", "south east", "sow sow east", - "south", "sow sow west", "south west", "west sow west", - "west", "west nor west", "north west", "nor nor west " - } - }; - return bearing_string[length][(int)((bearing / 90 * 8 + 1) / 2)%16]; - } - - public AltosGreatCircle (double start_lat, double start_lon, - double end_lat, double end_lon) - { - double lat1 = rad * start_lat; - double lon1 = rad * -start_lon; - double lat2 = rad * end_lat; - double lon2 = rad * -end_lon; - - double d_lon = lon2 - lon1; - - /* From http://en.wikipedia.org/wiki/Great-circle_distance */ - double vdn = Math.sqrt(sqr(Math.cos(lat2) * Math.sin(d_lon)) + - sqr(Math.cos(lat1) * Math.sin(lat2) - - Math.sin(lat1) * Math.cos(lat2) * Math.cos(d_lon))); - double vdd = Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(d_lon); - double d = Math.atan2(vdn,vdd); - double course; - - if (Math.cos(lat1) < 1e-20) { - if (lat1 > 0) - course = Math.PI; - else - course = -Math.PI; - } else { - if (d < 1e-10) - course = 0; - else - course = Math.acos((Math.sin(lat2)-Math.sin(lat1)*Math.cos(d)) / - (Math.sin(d)*Math.cos(lat1))); - if (Math.sin(lon2-lon1) > 0) - course = 2 * Math.PI-course; - } - distance = d * earth_radius; - bearing = course * 180/Math.PI; - } - - public AltosGreatCircle(AltosGPS start, AltosGPS end) { - this(start.lat, start.lon, end.lat, end.lon); - } - - public AltosGreatCircle() { - distance = 0; - bearing = 0; - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosLib.java b/altoslib/src/org/altusmetrum/AltosLib/AltosLib.java deleted file mode 100644 index 2921d040..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosLib.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.awt.*; -import java.util.*; -import java.text.*; -import java.nio.charset.Charset; - -public class AltosLib { - /* EEProm command letters */ - public static final int AO_LOG_FLIGHT = 'F'; - public static final int AO_LOG_SENSOR = 'A'; - public static final int AO_LOG_TEMP_VOLT = 'T'; - public static final int AO_LOG_DEPLOY = 'D'; - public static final int AO_LOG_STATE = 'S'; - public static final int AO_LOG_GPS_TIME = 'G'; - public static final int AO_LOG_GPS_LAT = 'N'; - public static final int AO_LOG_GPS_LON = 'W'; - public static final int AO_LOG_GPS_ALT = 'H'; - public static final int AO_LOG_GPS_SAT = 'V'; - public static final int AO_LOG_GPS_DATE = 'Y'; - public static final int AO_LOG_PRESSURE = 'P'; - - /* Added for header fields in eeprom files */ - public static final int AO_LOG_CONFIG_VERSION = 1000; - public static final int AO_LOG_MAIN_DEPLOY = 1001; - public static final int AO_LOG_APOGEE_DELAY = 1002; - public static final int AO_LOG_RADIO_CHANNEL = 1003; - public static final int AO_LOG_CALLSIGN = 1004; - public static final int AO_LOG_ACCEL_CAL = 1005; - public static final int AO_LOG_RADIO_CAL = 1006; - public static final int AO_LOG_MAX_FLIGHT_LOG = 1007; - public static final int AO_LOG_MANUFACTURER = 2000; - public static final int AO_LOG_PRODUCT = 2001; - public static final int AO_LOG_SERIAL_NUMBER = 2002; - public static final int AO_LOG_LOG_FORMAT = 2003; - public static final int AO_LOG_SOFTWARE_VERSION = 9999; - - /* Added to flag invalid records */ - public static final int AO_LOG_INVALID = -1; - - /* Flight state numbers and names */ - public static final int ao_flight_startup = 0; - public static final int ao_flight_idle = 1; - public static final int ao_flight_pad = 2; - public static final int ao_flight_boost = 3; - public static final int ao_flight_fast = 4; - public static final int ao_flight_coast = 5; - public static final int ao_flight_drogue = 6; - public static final int ao_flight_main = 7; - public static final int ao_flight_landed = 8; - public static final int ao_flight_invalid = 9; - - /* Telemetry modes */ - public static final int ao_telemetry_off = 0; - public static final int ao_telemetry_min = 1; - public static final int ao_telemetry_standard = 1; - public static final int ao_telemetry_0_9 = 2; - public static final int ao_telemetry_0_8 = 3; - public static final int ao_telemetry_max = 3; - - public static final String[] ao_telemetry_name = { - "Off", "Standard Telemetry", "TeleMetrum v0.9", "TeleMetrum v0.8" - }; - - public static final String launch_sites_url = "http://www.altusmetrum.org/AltOS/launch-sites.txt"; - - public static final int ao_telemetry_standard_len = 32; - public static final int ao_telemetry_0_9_len = 95; - public static final int ao_telemetry_0_8_len = 94; - - public static final int[] ao_telemetry_len = { - 0, 32, 95, 94 - }; - - public static HashMap string_to_state = new HashMap(); - - public static boolean map_initialized = false; - - public static void initialize_map() - { - string_to_state.put("startup", ao_flight_startup); - string_to_state.put("idle", ao_flight_idle); - string_to_state.put("pad", ao_flight_pad); - string_to_state.put("boost", ao_flight_boost); - string_to_state.put("fast", ao_flight_fast); - string_to_state.put("coast", ao_flight_coast); - string_to_state.put("drogue", ao_flight_drogue); - string_to_state.put("apogee", ao_flight_coast); - string_to_state.put("main", ao_flight_main); - string_to_state.put("landed", ao_flight_landed); - string_to_state.put("invalid", ao_flight_invalid); - map_initialized = true; - } - - public static int telemetry_len(int telemetry) { - if (telemetry <= ao_telemetry_max) - return ao_telemetry_len[telemetry]; - throw new IllegalArgumentException(String.format("Invalid telemetry %d", - telemetry)); - } - - public static String telemetry_name(int telemetry) { - if (telemetry <= ao_telemetry_max) - return ao_telemetry_name[telemetry]; - throw new IllegalArgumentException(String.format("Invalid telemetry %d", - telemetry)); - } - - public static String[] state_to_string = { - "startup", - "idle", - "pad", - "boost", - "fast", - "coast", - "drogue", - "main", - "landed", - "invalid", - }; - - public static String[] state_to_string_capital = { - "Startup", - "Idle", - "Pad", - "Boost", - "Fast", - "Coast", - "Drogue", - "Main", - "Landed", - "Invalid", - }; - - public static int state(String state) { - if (!map_initialized) - initialize_map(); - if (string_to_state.containsKey(state)) - return string_to_state.get(state); - return ao_flight_invalid; - } - - public static String state_name(int state) { - if (state < 0 || state_to_string.length <= state) - return "invalid"; - return state_to_string[state]; - } - - public static final int AO_GPS_VALID = (1 << 4); - public static final int AO_GPS_RUNNING = (1 << 5); - public static final int AO_GPS_DATE_VALID = (1 << 6); - public static final int AO_GPS_NUM_SAT_SHIFT = 0; - public static final int AO_GPS_NUM_SAT_MASK = 0xf; - - public static final int AO_LOG_FORMAT_UNKNOWN = 0; - public static final int AO_LOG_FORMAT_FULL = 1; - public static final int AO_LOG_FORMAT_TINY = 2; - 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_NONE = 127; - - public static boolean isspace(int c) { - switch (c) { - case ' ': - case '\t': - return true; - } - return false; - } - - public static boolean ishex(int c) { - if ('0' <= c && c <= '9') - return true; - if ('a' <= c && c <= 'f') - return true; - if ('A' <= c && c <= 'F') - return true; - return false; - } - - public static boolean ishex(String s) { - for (int i = 0; i < s.length(); i++) - if (!ishex(s.charAt(i))) - return false; - return true; - } - - public static int fromhex(int c) { - if ('0' <= c && c <= '9') - return c - '0'; - if ('a' <= c && c <= 'f') - return c - 'a' + 10; - if ('A' <= c && c <= 'F') - return c - 'A' + 10; - return -1; - } - - public static int fromhex(String s) throws NumberFormatException { - int c, v = 0; - for (int i = 0; i < s.length(); i++) { - c = s.charAt(i); - if (!ishex(c)) { - if (i == 0) - throw new NumberFormatException(String.format("invalid hex \"%s\"", s)); - return v; - } - v = v * 16 + fromhex(c); - } - return v; - } - - public static boolean isdec(int c) { - if ('0' <= c && c <= '9') - return true; - return false; - } - - public static boolean isdec(String s) { - for (int i = 0; i < s.length(); i++) - if (!isdec(s.charAt(i))) - return false; - return true; - } - - public static int fromdec(int c) { - if ('0' <= c && c <= '9') - return c - '0'; - return -1; - } - - public static int int8(int[] bytes, int i) { - return (int) (byte) bytes[i]; - } - - public static int uint8(int[] bytes, int i) { - return bytes[i]; - } - - public static int int16(int[] bytes, int i) { - return (int) (short) (bytes[i] + (bytes[i+1] << 8)); - } - - public static int uint16(int[] bytes, int i) { - return bytes[i] + (bytes[i+1] << 8); - } - - public static int uint32(int[] bytes, int i) { - return bytes[i] + - (bytes[i+1] << 8) + - (bytes[i+2] << 16) + - (bytes[i+3] << 24); - } - - public static final Charset unicode_set = Charset.forName("UTF-8"); - - public static String string(int[] bytes, int s, int l) { - if (s + l > bytes.length) { - if (s > bytes.length) { - s = bytes.length; - l = 0; - } else { - l = bytes.length - s; - } - } - - int i; - for (i = l - 1; i >= 0; i--) - if (bytes[s+i] != 0) - break; - - l = i + 1; - byte[] b = new byte[l]; - - for (i = 0; i < l; i++) - b[i] = (byte) bytes[s+i]; - String n = new String(b, unicode_set); - return n; - } - - public static int hexbyte(String s, int i) { - int c0, c1; - - if (s.length() < i + 2) - throw new NumberFormatException(String.format("invalid hex \"%s\"", s)); - c0 = s.charAt(i); - if (!ishex(c0)) - throw new NumberFormatException(String.format("invalid hex \"%c\"", c0)); - c1 = s.charAt(i+1); - if (!ishex(c1)) - throw new NumberFormatException(String.format("invalid hex \"%c\"", c1)); - return fromhex(c0) * 16 + fromhex(c1); - } - - public static int[] hexbytes(String s) { - int n; - int[] r; - int i; - - if ((s.length() & 1) != 0) - throw new NumberFormatException(String.format("invalid line \"%s\"", s)); - n = s.length() / 2; - r = new int[n]; - for (i = 0; i < n; i++) - r[i] = hexbyte(s, i * 2); - return r; - } - - public static int fromdec(String s) throws NumberFormatException { - int c, v = 0; - int sign = 1; - for (int i = 0; i < s.length(); i++) { - c = s.charAt(i); - if (i == 0 && c == '-') { - sign = -1; - } else if (!isdec(c)) { - if (i == 0) - throw new NumberFormatException(String.format("invalid number \"%s\"", s)); - return v; - } else - v = v * 10 + fromdec(c); - } - return v * sign; - } - - public static String replace_extension(String input, String extension) { - int dot = input.lastIndexOf("."); - if (dot > 0) - input = input.substring(0,dot); - return input.concat(extension); - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosLine.java b/altoslib/src/org/altusmetrum/AltosLib/AltosLine.java deleted file mode 100644 index 5627795a..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosLine.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -public class AltosLine { - public String line; - - public AltosLine() { - line = null; - } - - public AltosLine(String s) { - line = s; - } -} \ No newline at end of file diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosLink.java b/altoslib/src/org/altusmetrum/AltosLib/AltosLink.java deleted file mode 100644 index 9b80e916..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosLink.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - -import java.lang.*; -import java.io.*; -import java.util.concurrent.*; -import java.util.*; -import java.text.*; - -public abstract class AltosLink { - public abstract void print(String data); - public abstract void close(); - - public static boolean debug = false; - public static void set_debug(boolean in_debug) { debug = in_debug; } - LinkedList pending_output = new LinkedList(); - - public LinkedList> monitors = new LinkedList> ();; - public LinkedBlockingQueue reply_queue = new LinkedBlockingQueue(); - - public void add_monitor(LinkedBlockingQueue q) { - set_monitor(true); - monitors.add(q); - } - - public void remove_monitor(LinkedBlockingQueue q) { - monitors.remove(q); - if (monitors.isEmpty()) - set_monitor(false); - } - - public void printf(String format, Object ... arguments) { - String line = String.format(format, arguments); - if (debug) - pending_output.add(line); - print(line); - } - - public String get_reply_no_dialog(int timeout) throws InterruptedException, TimeoutException { - flush_output(); - AltosLine line = reply_queue.poll(timeout, TimeUnit.MILLISECONDS); - if (line != null) - return line.line; - return null; - } - - public String get_reply(int timeout) throws InterruptedException { - try { - return get_reply_no_dialog(timeout); - } catch (TimeoutException te) { - return null; - } - } - - public String get_reply() throws InterruptedException { - return get_reply(5000); - } - - public void add_telem(AltosLine line) throws InterruptedException { - for (int e = 0; e < monitors.size(); e++) { - LinkedBlockingQueue q = monitors.get(e); - q.put(line); - } - } - - public void add_reply(AltosLine line) throws InterruptedException { - reply_queue.put (line); - } - - public void add_string(String line) throws InterruptedException { - if (line.startsWith("TELEM") || line.startsWith("VERSION") || line.startsWith("CRC")) { - add_telem(new AltosLine(line)); - } else { - add_reply(new AltosLine(line)); - } - } - - public void add_bytes(byte[] bytes, int len) throws InterruptedException { - String line; - try { - line = new String(bytes, 0, len, "UTF-8"); - } catch (UnsupportedEncodingException ue) { - line = ""; - for (int i = 0; i < len; i++) - line = line + bytes[i]; - } - if (debug) - System.out.printf("\t\t\t\t\t%s\n", line); - add_string(line); - } - - public void flush_output() { - for (String s : pending_output) - System.out.print(s); - pending_output.clear(); - } - - public void flush_input(int timeout) throws InterruptedException { - flush_output(); - boolean got_some; - - do { - Thread.sleep(timeout); - got_some = !reply_queue.isEmpty(); - reply_queue.clear(); - } while (got_some); - } - - - public void flush_input() throws InterruptedException { - flush_input(100); - } - - - /* - * Various command-level operations on - * the link - */ - public boolean monitor_mode = false; - public int telemetry = AltosLib.ao_telemetry_standard; - public double frequency; - AltosConfigData config_data; - - private int telemetry_len() { - return AltosLib.telemetry_len(telemetry); - } - - public void set_telemetry(int in_telemetry) { - telemetry = in_telemetry; - if (monitor_mode) - printf("m 0\nm %x\n", telemetry_len()); - flush_output(); - } - - public void set_monitor(boolean monitor) { - monitor_mode = monitor; - if (monitor) - printf("m %x\n", telemetry_len()); - else - printf("m 0\n"); - flush_output(); - } - - private void set_channel(int channel) { - if (monitor_mode) - printf("m 0\nc r %d\nm %x\n", - channel, telemetry_len()); - else - printf("c r %d\n", channel); - flush_output(); - } - - private void set_radio_setting(int setting) { - if (monitor_mode) - printf("m 0\nc R %d\nm %x\n", - setting, telemetry_len()); - else - printf("c R %d\n", setting); - flush_output(); - } - - public void set_radio_frequency(double frequency, - boolean has_setting, - int cal) { - if (debug) - System.out.printf("set_radio_frequency %7.3f %b %d\n", frequency, has_setting, cal); - if (has_setting) - set_radio_setting(AltosConvert.radio_frequency_to_setting(frequency, cal)); - else - set_channel(AltosConvert.radio_frequency_to_channel(frequency)); - } - - public AltosConfigData config_data() throws InterruptedException, TimeoutException { - if (config_data == null) - config_data = new AltosConfigData(this); - return config_data; - } - - public void set_radio_frequency(double in_frequency) throws InterruptedException, TimeoutException { - frequency = in_frequency; - config_data(); - set_radio_frequency(frequency, - config_data.radio_setting != 0, - config_data.radio_calibration); - } - - public void set_callsign(String callsign) { - printf ("c c %s\n", callsign); - flush_output(); - } - - public boolean remote; - public int serial; - public String name; - - public void start_remote() throws TimeoutException, InterruptedException { - if (debug) - System.out.printf("start remote %7.3f\n", frequency); - if (frequency == 0.0) - frequency = AltosPreferences.frequency(serial); - set_radio_frequency(frequency); - set_callsign(AltosPreferences.callsign()); - printf("p\nE 0\n"); - flush_input(); - remote = true; - } - - public void stop_remote() throws InterruptedException { - if (debug) - System.out.printf("stop remote\n"); - try { - flush_input(); - } finally { - printf ("~\n"); - flush_output(); - } - remote = false; - } - - public AltosLink() { - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosLog.java b/altoslib/src/org/altusmetrum/AltosLib/AltosLog.java deleted file mode 100644 index 08c45ca8..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosLog.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.io.*; -import java.lang.*; -import java.util.*; -import java.text.ParseException; -import java.util.concurrent.LinkedBlockingQueue; - -/* - * This creates a thread to capture telemetry data and write it to - * a log file - */ -class AltosLog implements Runnable { - - LinkedBlockingQueue input_queue; - LinkedBlockingQueue pending_queue; - int serial; - int flight; - FileWriter log_file; - Thread log_thread; - AltosFile file; - - private void close_log_file() { - if (log_file != null) { - try { - log_file.close(); - } catch (IOException io) { - } - log_file = null; - } - } - - void close() { - close_log_file(); - if (log_thread != null) { - log_thread.interrupt(); - log_thread = null; - } - } - - File file() { - return file; - } - - boolean open (AltosRecord telem) throws IOException { - AltosFile a = new AltosFile(telem); - - System.out.printf("open %s\n", a.toString()); - log_file = new FileWriter(a, true); - if (log_file != null) { - while (!pending_queue.isEmpty()) { - try { - String s = pending_queue.take(); - log_file.write(s); - log_file.write('\n'); - } catch (InterruptedException ie) { - } - } - log_file.flush(); - file = a; - } - return log_file != null; - } - - public void run () { - try { - AltosRecord previous = null; - for (;;) { - AltosLine line = input_queue.take(); - if (line.line == null) - continue; - try { - AltosRecord telem = AltosTelemetry.parse(line.line, previous); - if (telem.serial != 0 && telem.flight != 0 && - (telem.serial != serial || telem.flight != flight || log_file == null)) - { - close_log_file(); - serial = telem.serial; - flight = telem.flight; - open(telem); - } - previous = telem; - } catch (ParseException pe) { - } catch (AltosCRCException ce) { - } - if (log_file != null) { - log_file.write(line.line); - log_file.write('\n'); - log_file.flush(); - } else - pending_queue.put(line.line); - } - } catch (InterruptedException ie) { - } catch (IOException ie) { - } - close(); - } - - public AltosLog (AltosLink link) { - pending_queue = new LinkedBlockingQueue (); - input_queue = new LinkedBlockingQueue (); - link.add_monitor(input_queue); - serial = -1; - flight = -1; - log_file = null; - log_thread = new Thread(this); - log_thread.start(); - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosParse.java b/altoslib/src/org/altusmetrum/AltosLib/AltosParse.java deleted file mode 100644 index 7d832f1a..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosParse.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.text.*; -import java.lang.*; - -public class AltosParse { - public static boolean isdigit(char c) { - return '0' <= c && c <= '9'; - } - - public static int parse_int(String v) throws ParseException { - try { - return AltosLib.fromdec(v); - } catch (NumberFormatException e) { - throw new ParseException("error parsing int " + v, 0); - } - } - - public static int parse_hex(String v) throws ParseException { - try { - return AltosLib.fromhex(v); - } catch (NumberFormatException e) { - throw new ParseException("error parsing hex " + v, 0); - } - } - - public static double parse_double(String v) throws ParseException { - try { - return Double.parseDouble(v); - } catch (NumberFormatException e) { - throw new ParseException("error parsing double " + v, 0); - } - } - - public static double parse_coord(String coord) throws ParseException { - String[] dsf = coord.split("\\D+"); - - if (dsf.length != 3) { - throw new ParseException("error parsing coord " + coord, 0); - } - int deg = parse_int(dsf[0]); - int min = parse_int(dsf[1]); - int frac = parse_int(dsf[2]); - - double r = deg + (min + frac / 10000.0) / 60.0; - if (coord.endsWith("S") || coord.endsWith("W")) - r = -r; - return r; - } - - public static String strip_suffix(String v, String suffix) { - if (v.endsWith(suffix)) - return v.substring(0, v.length() - suffix.length()); - return v; - } - - public static void word(String v, String m) throws ParseException { - if (!v.equals(m)) { - throw new ParseException("error matching '" + v + "' '" + m + "'", 0); - } - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosPreferences.java b/altoslib/src/org/altusmetrum/AltosLib/AltosPreferences.java deleted file mode 100644 index 43c7088d..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosPreferences.java +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.util.concurrent.LinkedBlockingQueue; -import java.awt.Component; -import javax.swing.*; -import javax.swing.filechooser.FileSystemView; - -public class AltosPreferences { - public static Preferences preferences; - - /* logdir preference name */ - public final static String logdirPreference = "LOGDIR"; - - /* channel preference name */ - public final static String channelPreferenceFormat = "CHANNEL-%d"; - - /* frequency preference name */ - public final static String frequencyPreferenceFormat = "FREQUENCY-%d"; - - /* telemetry format preference name */ - public final static String telemetryPreferenceFormat = "TELEMETRY-%d"; - - /* voice preference name */ - public final static String voicePreference = "VOICE"; - - /* callsign preference name */ - public final static String callsignPreference = "CALLSIGN"; - - /* firmware directory preference name */ - public final static String firmwaredirPreference = "FIRMWARE"; - - /* serial debug preference name */ - public final static String serialDebugPreference = "SERIAL-DEBUG"; - - /* scanning telemetry preferences name */ - public final static String scanningTelemetryPreference = "SCANNING-TELEMETRY"; - - /* Launcher serial preference name */ - public final static String launcherSerialPreference = "LAUNCHER-SERIAL"; - - /* Launcher channel preference name */ - public final static String launcherChannelPreference = "LAUNCHER-CHANNEL"; - - /* Default logdir is ~/TeleMetrum */ - public final static String logdirName = "TeleMetrum"; - - /* Log directory */ - public static File logdir; - - /* Map directory -- hangs of logdir */ - public static File mapdir; - - /* Frequency (map serial to frequency) */ - public static Hashtable frequencies; - - /* Telemetry (map serial to telemetry format) */ - public static Hashtable telemetries; - - /* Voice preference */ - public static boolean voice; - - /* Callsign preference */ - public static String callsign; - - /* Firmware directory */ - public static File firmwaredir; - - /* Scanning telemetry */ - public static int scanning_telemetry; - - /* List of frequencies */ - public final static String common_frequencies_node_name = "COMMON-FREQUENCIES"; - public static AltosFrequency[] common_frequencies; - - public final static String frequency_count = "COUNT"; - public final static String frequency_format = "FREQUENCY-%d"; - public final static String description_format = "DESCRIPTION-%d"; - - public static AltosFrequency[] load_common_frequencies() { - AltosFrequency[] frequencies = null; - boolean existing = false; - try { - existing = preferences.nodeExists(common_frequencies_node_name); - } catch (BackingStoreException be) { - existing = false; - } - if (existing) { - Preferences node = preferences.node(common_frequencies_node_name); - int count = node.getInt(frequency_count, 0); - - frequencies = new AltosFrequency[count]; - for (int i = 0; i < count; i++) { - double frequency; - String description; - - frequency = node.getDouble(String.format(frequency_format, i), 0.0); - description = node.get(String.format(description_format, i), null); - frequencies[i] = new AltosFrequency(frequency, description); - } - } else { - frequencies = new AltosFrequency[10]; - for (int i = 0; i < 10; i++) { - frequencies[i] = new AltosFrequency(434.550 + i * .1, - String.format("Channel %d", i)); - } - } - return frequencies; - } - - public static void save_common_frequencies(AltosFrequency[] frequencies) { - Preferences node = preferences.node(common_frequencies_node_name); - - node.putInt(frequency_count, frequencies.length); - for (int i = 0; i < frequencies.length; i++) { - node.putDouble(String.format(frequency_format, i), frequencies[i].frequency); - node.put(String.format(description_format, i), frequencies[i].description); - } - } - public static int launcher_serial; - - public static int launcher_channel; - - public static void init() { - preferences = Preferences.userRoot().node("/org/altusmetrum/altosui"); - - /* Initialize logdir from preferences */ - String logdir_string = preferences.get(logdirPreference, null); - if (logdir_string != null) - logdir = new File(logdir_string); - else { - /* Use the file system view default directory */ - logdir = new File(FileSystemView.getFileSystemView().getDefaultDirectory(), logdirName); - if (!logdir.exists()) - logdir.mkdirs(); - } - mapdir = new File(logdir, "maps"); - if (!mapdir.exists()) - mapdir.mkdirs(); - - frequencies = new Hashtable(); - - telemetries = new Hashtable(); - - voice = preferences.getBoolean(voicePreference, true); - - callsign = preferences.get(callsignPreference,"N0CALL"); - - scanning_telemetry = preferences.getInt(scanningTelemetryPreference,(1 << AltosLib.ao_telemetry_standard)); - - launcher_serial = preferences.getInt(launcherSerialPreference, 0); - - launcher_channel = preferences.getInt(launcherChannelPreference, 0); - - String firmwaredir_string = preferences.get(firmwaredirPreference, null); - if (firmwaredir_string != null) - firmwaredir = new File(firmwaredir_string); - else - firmwaredir = null; - - common_frequencies = load_common_frequencies(); - - } - - static { init(); } - - public static void flush_preferences() { - try { - preferences.flush(); - } catch (BackingStoreException ee) { -/* - if (component != null) - JOptionPane.showMessageDialog(component, - preferences.absolutePath(), - "Cannot save prefernces", - JOptionPane.ERROR_MESSAGE); - else -*/ - System.err.printf("Cannot save preferences\n"); - } - } - - public static void set_logdir(File new_logdir) { - logdir = new_logdir; - mapdir = new File(logdir, "maps"); - if (!mapdir.exists()) - mapdir.mkdirs(); - synchronized (preferences) { - preferences.put(logdirPreference, logdir.getPath()); - flush_preferences(); - } - } - - public static File logdir() { - return logdir; - } - - public static File mapdir() { - return mapdir; - } - - public static void set_frequency(int serial, double new_frequency) { - frequencies.put(serial, new_frequency); - synchronized (preferences) { - preferences.putDouble(String.format(frequencyPreferenceFormat, serial), new_frequency); - flush_preferences(); - } - } - - public static double frequency(int serial) { - if (frequencies.containsKey(serial)) - return frequencies.get(serial); - double frequency = preferences.getDouble(String.format(frequencyPreferenceFormat, serial), 0); - if (frequency == 0.0) { - int channel = preferences.getInt(String.format(channelPreferenceFormat, serial), 0); - frequency = AltosConvert.radio_channel_to_frequency(channel); - } - frequencies.put(serial, frequency); - return frequency; - } - - public static void set_telemetry(int serial, int new_telemetry) { - telemetries.put(serial, new_telemetry); - synchronized (preferences) { - preferences.putInt(String.format(telemetryPreferenceFormat, serial), new_telemetry); - flush_preferences(); - } - } - - public static int telemetry(int serial) { - if (telemetries.containsKey(serial)) - return telemetries.get(serial); - int telemetry = preferences.getInt(String.format(telemetryPreferenceFormat, serial), - AltosLib.ao_telemetry_standard); - telemetries.put(serial, telemetry); - return telemetry; - } - - public static void set_scanning_telemetry(int new_scanning_telemetry) { - scanning_telemetry = new_scanning_telemetry; - synchronized (preferences) { - preferences.putInt(scanningTelemetryPreference, scanning_telemetry); - flush_preferences(); - } - } - - public static int scanning_telemetry() { - return scanning_telemetry; - } - - public static void set_voice(boolean new_voice) { - voice = new_voice; - synchronized (preferences) { - preferences.putBoolean(voicePreference, voice); - flush_preferences(); - } - } - - public static boolean voice() { - return voice; - } - - public static void set_callsign(String new_callsign) { - callsign = new_callsign; - synchronized(preferences) { - preferences.put(callsignPreference, callsign); - flush_preferences(); - } - } - - public static String callsign() { - return callsign; - } - - public static void set_firmwaredir(File new_firmwaredir) { - firmwaredir = new_firmwaredir; - synchronized (preferences) { - preferences.put(firmwaredirPreference, firmwaredir.getPath()); - flush_preferences(); - } - } - - public static File firmwaredir() { - return firmwaredir; - } - - public static void set_launcher_serial(int new_launcher_serial) { - launcher_serial = new_launcher_serial; - System.out.printf("set launcher serial to %d\n", new_launcher_serial); - synchronized (preferences) { - preferences.putInt(launcherSerialPreference, launcher_serial); - flush_preferences(); - } - } - - public static int launcher_serial() { - return launcher_serial; - } - - public static void set_launcher_channel(int new_launcher_channel) { - launcher_channel = new_launcher_channel; - System.out.printf("set launcher channel to %d\n", new_launcher_channel); - synchronized (preferences) { - preferences.putInt(launcherChannelPreference, launcher_channel); - flush_preferences(); - } - } - - public static int launcher_channel() { - return launcher_channel; - } - - public static Preferences bt_devices() { - return preferences.node("bt_devices"); - } - - public static AltosFrequency[] common_frequencies() { - return common_frequencies; - } - - public static void set_common_frequencies(AltosFrequency[] frequencies) { - common_frequencies = frequencies; - synchronized(preferences) { - save_common_frequencies(frequencies); - flush_preferences(); - } - } - - public static void add_common_frequency(AltosFrequency frequency) { - AltosFrequency[] new_frequencies = new AltosFrequency[common_frequencies.length + 1]; - int i; - - for (i = 0; i < common_frequencies.length; i++) { - if (frequency.frequency == common_frequencies[i].frequency) - return; - if (frequency.frequency < common_frequencies[i].frequency) - break; - new_frequencies[i] = common_frequencies[i]; - } - new_frequencies[i] = frequency; - for (; i < common_frequencies.length; i++) - new_frequencies[i+1] = common_frequencies[i]; - set_common_frequencies(new_frequencies); - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosRecord.java b/altoslib/src/org/altusmetrum/AltosLib/AltosRecord.java deleted file mode 100644 index e4915af0..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosRecord.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.lang.*; -import java.text.*; -import java.util.HashMap; -import java.io.*; - -public class AltosRecord implements Comparable { - public final static int MISSING = 0x7fffffff; - - public static final int seen_flight = 1; - public static final int seen_sensor = 2; - public static final int seen_temp_volt = 4; - public static final int seen_deploy = 8; - public static final int seen_gps_time = 16; - public static final int seen_gps_lat = 32; - public static final int seen_gps_lon = 64; - public static final int seen_companion = 128; - public int seen; - - public int version; - public String callsign; - public int serial; - public int flight; - public int rssi; - public int status; - public int state; - public int tick; - - public int accel; - public int pres; - public int temp; - public int batt; - public int drogue; - public int main; - - public int ground_accel; - public int ground_pres; - public int accel_plus_g; - public int accel_minus_g; - - public double acceleration; - public double speed; - public double height; - - public int flight_accel; - public int flight_vel; - public int flight_pres; - - public AltosGPS gps; - public boolean new_gps; - - public AltosIMU imu; - public AltosMag mag; - - public double time; /* seconds since boost */ - - public int device_type; - public int config_major; - public int config_minor; - public int apogee_delay; - public int main_deploy; - public int flight_log_max; - public String firmware_version; - - public AltosRecordCompanion companion; - ->>>>>>> 5a249bc... altosui: Complete split out of separate java library - /* - * Values for our MP3H6115A pressure sensor - * - * From the data sheet: - * - * Pressure range: 15-115 kPa - * Voltage at 115kPa: 2.82 - * Output scale: 27mV/kPa - * - * - * 27 mV/kPa * 2047 / 3300 counts/mV = 16.75 counts/kPa - * 2.82V * 2047 / 3.3 counts/V = 1749 counts/115 kPa - */ - - public static final double counts_per_kPa = 27 * 2047 / 3300; - public static final double counts_at_101_3kPa = 1674.0; - - public static double - barometer_to_pressure(double count) - { - return ((count / 16.0) / 2047.0 + 0.095) / 0.009 * 1000.0; - } - - public double raw_pressure() { - if (pres == MISSING) - return MISSING; - return barometer_to_pressure(pres); - } - - public double filtered_pressure() { - if (flight_pres == MISSING) - return MISSING; - return barometer_to_pressure(flight_pres); - } - - public double ground_pressure() { - if (ground_pres == MISSING) - return MISSING; - return barometer_to_pressure(ground_pres); - } - - public double raw_altitude() { - double p = raw_pressure(); - if (p == MISSING) - return MISSING; - return AltosConvert.pressure_to_altitude(p); - } - - public double ground_altitude() { - double p = ground_pressure(); - if (p == MISSING) - return MISSING; - return AltosConvert.pressure_to_altitude(p); - } - - public double filtered_altitude() { - if (height != MISSING && ground_pres != MISSING) - return height + ground_altitude(); - - double p = filtered_pressure(); - if (p == MISSING) - return MISSING; - return AltosConvert.pressure_to_altitude(p); - } - - public double filtered_height() { - if (height != MISSING) - return height; - - double f = filtered_altitude(); - double g = ground_altitude(); - if (f == MISSING || g == MISSING) - return MISSING; - return f - g; - } - - public double raw_height() { - double r = raw_altitude(); - double g = ground_altitude(); - - if (r == MISSING || g == MISSING) - return height; - return r - g; - } - - public double battery_voltage() { - if (batt == MISSING) - return MISSING; - return AltosConvert.cc_battery_to_voltage(batt); - } - - public double main_voltage() { - if (main == MISSING) - return MISSING; - return AltosConvert.cc_ignitor_to_voltage(main); - } - - public double drogue_voltage() { - if (drogue == MISSING) - return MISSING; - return AltosConvert.cc_ignitor_to_voltage(drogue); - } - - /* Value for the CC1111 built-in temperature sensor - * Output voltage at 0°C = 0.755V - * Coefficient = 0.00247V/°C - * Reference voltage = 1.25V - * - * temp = ((value / 32767) * 1.25 - 0.755) / 0.00247 - * = (value - 19791.268) / 32768 * 1.25 / 0.00247 - */ - - public static double - thermometer_to_temperature(double thermo) - { - return (thermo - 19791.268) / 32728.0 * 1.25 / 0.00247; - } - - public double temperature() { - if (temp == MISSING) - return MISSING; - return thermometer_to_temperature(temp); - } - - public 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 (acceleration != MISSING) - return acceleration; - - if (ground_accel == MISSING || accel == MISSING) - return MISSING; - return (ground_accel - accel) / accel_counts_per_mss(); - } - - public double accel_speed() { - if (speed != MISSING) - return speed; - if (flight_vel == MISSING) - return MISSING; - return flight_vel / (accel_counts_per_mss() * 100.0); - } - - public String state() { - return AltosLib.state_name(state); - } - - public static String gets(FileInputStream s) throws IOException { - int c; - String line = ""; - - while ((c = s.read()) != -1) { - if (c == '\r') - continue; - if (c == '\n') { - return line; - } - line = line + (char) c; - } - return null; - } - - public int compareTo(AltosRecord o) { - return tick - o.tick; - } - - public AltosRecord(AltosRecord old) { - version = old.version; - seen = old.seen; - callsign = old.callsign; - serial = old.serial; - flight = old.flight; - rssi = old.rssi; - status = old.status; - state = old.state; - tick = old.tick; - accel = old.accel; - pres = old.pres; - temp = old.temp; - batt = old.batt; - drogue = old.drogue; - main = old.main; - flight_accel = old.flight_accel; - ground_accel = old.ground_accel; - flight_vel = old.flight_vel; - flight_pres = old.flight_pres; - ground_pres = old.ground_pres; - accel_plus_g = old.accel_plus_g; - accel_minus_g = old.accel_minus_g; - acceleration = old.acceleration; - speed = old.speed; - height = old.height; - gps = new AltosGPS(old.gps); - new_gps = false; - companion = old.companion; - imu = old.imu; - mag = old.mag; - } - - public AltosRecord() { - version = 0; - seen = 0; - callsign = "N0CALL"; - serial = 0; - flight = 0; - rssi = 0; - status = 0; - state = AltosLib.ao_flight_startup; - tick = 0; - accel = MISSING; - pres = MISSING; - temp = MISSING; - batt = MISSING; - drogue = MISSING; - main = MISSING; - flight_accel = 0; - ground_accel = 0; - flight_vel = 0; - flight_pres = 0; - ground_pres = 0; - accel_plus_g = 0; - accel_minus_g = 0; - acceleration = MISSING; - speed = MISSING; - height = MISSING; - gps = new AltosGPS(); - new_gps = false; - companion = null; - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosRecordCompanion.java b/altoslib/src/org/altusmetrum/AltosLib/AltosRecordCompanion.java deleted file mode 100644 index c8cc6cac..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosRecordCompanion.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - -public class AltosRecordCompanion { - 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 AltosRecordCompanion(int in_channels) { - channels = in_channels; - if (channels < 0) - channels = 0; - if (channels > MAX_CHANNELS) - channels = MAX_CHANNELS; - companion_data = new int[channels]; - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosRecordIterable.java b/altoslib/src/org/altusmetrum/AltosLib/AltosRecordIterable.java deleted file mode 100644 index ed1787ed..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosRecordIterable.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.io.*; -import java.util.*; - -public abstract class AltosRecordIterable implements Iterable { - public abstract Iterator iterator(); - public void write_comments(PrintStream out) { } - public boolean has_accel() { return false; } - public boolean has_gps() { return false; } - public boolean has_ignite() { return false; }; -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosReplayReader.java b/altoslib/src/org/altusmetrum/AltosLib/AltosReplayReader.java deleted file mode 100644 index 1585f9eb..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosReplayReader.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.util.concurrent.LinkedBlockingQueue; - -/* - * Open an existing telemetry file and replay it in realtime - */ - -public class AltosReplayReader extends AltosFlightReader { - Iterator iterator; - File file; - - public AltosRecord read() { - if (iterator.hasNext()) - return iterator.next(); - return null; - } - - public void close (boolean interrupted) { - } - - public void update(AltosState state) throws InterruptedException { - /* Make it run in realtime after the rocket leaves the pad */ - if (state.state > AltosLib.ao_flight_pad) - Thread.sleep((int) (Math.min(state.time_change,10) * 1000)); - } - - public File backing_file() { return file; } - - public AltosReplayReader(Iterator in_iterator, File in_file) { - iterator = in_iterator; - file = in_file; - name = file.getName(); - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosState.java b/altoslib/src/org/altusmetrum/AltosLib/AltosState.java deleted file mode 100644 index 0645e448..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosState.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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. - */ - -/* - * Track flight state from telemetry or eeprom data stream - */ - -package org.altusmetrum.AltosLib; - -public class AltosState { - public AltosRecord data; - - /* derived data */ - - public long report_time; - - public double time; - public double time_change; - public int tick; - - public int state; - public boolean landed; - public boolean ascent; /* going up? */ - public boolean boost; /* under power */ - - public double ground_altitude; - public double height; - public double speed; - public double acceleration; - public double battery; - public double temperature; - public double main_sense; - public double drogue_sense; - public double baro_speed; - - public double max_height; - public double max_acceleration; - public double max_speed; - public double max_baro_speed; - - public AltosGPS gps; - - public AltosIMU imu; - public AltosMag mag; - - public static final int MIN_PAD_SAMPLES = 10; - - public int npad; - public int ngps; - public int gps_waiting; - public boolean gps_ready; - - public AltosGreatCircle from_pad; - public double elevation; /* from pad */ - public double range; /* total distance */ - - public double gps_height; - - public int speak_tick; - public double speak_altitude; - - public void init (AltosRecord cur, AltosState prev_state) { - int i; - AltosRecord prev; - - data = cur; - - ground_altitude = data.ground_altitude(); - height = data.filtered_height(); - - report_time = System.currentTimeMillis(); - - acceleration = data.acceleration(); - speed = data.accel_speed(); - temperature = data.temperature(); - drogue_sense = data.drogue_voltage(); - main_sense = data.main_voltage(); - battery = data.battery_voltage(); - 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; - 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_speed = prev_state.max_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; - - /* compute barometric speed */ - - double height_change = height - prev_state.height; - if (data.speed != AltosRecord.MISSING) - baro_speed = data.speed; - else { - if (time_change > 0) - baro_speed = (prev_state.baro_speed * 3 + (height_change / time_change)) / 4.0; - else - baro_speed = prev_state.baro_speed; - } - } else { - npad = 0; - ngps = 0; - gps = null; - baro_speed = 0; - time_change = 0; - } - - time = tick / 100.0; - - if (cur.new_gps && (state == AltosLib.ao_flight_pad || state == AltosLib.ao_flight_idle)) { - - /* Track consecutive 'good' gps reports, waiting for 10 of them */ - if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) - npad++; - else - npad = 0; - - /* Average GPS data while on the pad */ - if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) { - if (ngps > 1) { - /* 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++; - } - } - - gps_waiting = MIN_PAD_SAMPLES - npad; - if (gps_waiting < 0) - gps_waiting = 0; - - gps_ready = gps_waiting == 0; - - ascent = (AltosLib.ao_flight_boost <= state && - state <= AltosLib.ao_flight_coast); - boost = (AltosLib.ao_flight_boost == state); - - /* Only look at accelerometer data under boost */ - if (boost && acceleration > max_acceleration) - max_acceleration = acceleration; - if (boost && speed > max_speed) - max_speed = speed; - if (boost && baro_speed > max_baro_speed) - max_baro_speed = baro_speed; - - if (height > max_height) - max_height = height; - if (data.gps != null) { - if (gps == null || !gps.locked || data.gps.locked) - gps = data.gps; - if (ngps > 0 && gps.locked) { - from_pad = new AltosGreatCircle(pad_lat, pad_lon, gps.lat, gps.lon); - } - } - elevation = 0; - range = -1; - if (ngps > 0) { - gps_height = gps.alt - pad_alt; - if (from_pad != null) { - elevation = Math.atan2(height, from_pad.distance) * 180 / Math.PI; - range = Math.sqrt(height * height + from_pad.distance * from_pad.distance); - } - } else { - gps_height = 0; - } - } - - public AltosState(AltosRecord cur) { - init(cur, null); - } - - public AltosState (AltosRecord cur, AltosState prev) { - init(cur, prev); - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetry.java b/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetry.java deleted file mode 100644 index 04abb1f3..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetry.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.lang.*; -import java.text.*; -import java.util.HashMap; - -/* - * 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 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); - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryIterable.java b/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryIterable.java deleted file mode 100644 index f4b4029f..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryIterable.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.io.*; -import java.util.*; -import java.text.*; - -public class AltosTelemetryIterable extends AltosRecordIterable { - TreeSet records; - - public Iterator iterator () { - return records.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 (); - - try { - for (;;) { - String line = AltosRecord.gets(input); - if (line == null) { - break; - } - try { - AltosRecord record = AltosTelemetry.parse(line, previous); - if (record == 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.accel != AltosRecord.MISSING) - has_accel = true; - if (record.gps != null) - has_gps = true; - if (record.main != AltosRecord.MISSING) - has_ignite = true; - if (previous != null && previous.tick != record.tick) - records.add(previous); - previous = record; - } 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) { - } - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryMap.java b/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryMap.java deleted file mode 100644 index 003cb6a9..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryMap.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; -import java.lang.*; -import java.text.*; -import java.util.HashMap; - -public class AltosTelemetryMap extends HashMap { - public boolean has(String key) { - return containsKey(key); - } - - public String get_string(String key) throws ParseException { - if (!has(key)) - throw new ParseException ("missing " + key, 0); - return (String) get(key); - } - - public String get_string(String key, String def) { - if (has(key)) - return get(key); - else - return def; - } - - public int get_int(String key) throws ParseException { - return AltosParse.parse_int(get_string(key)); - } - - public int get_int(String key, int def) throws ParseException { - if (has(key)) - return get_int(key); - else - return def; - } - - public double get_double(String key, double def, double scale) throws ParseException { - if (has(key)) - return get_int(key) * scale; - else - return def; - } - - public AltosTelemetryMap(String[] words, int start) { - for (int i = start; i < words.length - 1; i += 2) - put(words[i], words[i+1]); - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryReader.java b/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryReader.java deleted file mode 100644 index 67ac1b65..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryReader.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.lang.*; -import java.text.*; -import java.io.*; -import java.util.concurrent.*; - -public class AltosTelemetryReader extends AltosFlightReader { - AltosLink link; - AltosLog log; - AltosRecord previous; - double frequency; - int telemetry; - - LinkedBlockingQueue telem; - - public AltosRecord 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; - } - - public void flush() { - telem.clear(); - } - - public void close(boolean interrupted) { - link.remove_monitor(telem); - log.close(); - link.close(); - } - - public void set_frequency(double in_frequency) throws InterruptedException, TimeoutException { - frequency = in_frequency; - link.set_radio_frequency(frequency); - } - - public boolean supports_telemetry(int telemetry) { - - try { - /* Version 1.0 or later firmware supports all telemetry formats */ - if (serial.config_data().compare_version("1.0") >= 0) - return true; - - /* Version 0.9 firmware only supports 0.9 telemetry */ - if (serial.config_data().compare_version("0.9") >= 0) { - if (telemetry == Altos.ao_telemetry_0_9) - return true; - else - return false; - } - - /* Version 0.8 firmware only supports 0.8 telemetry */ - if (telemetry == Altos.ao_telemetry_0_8) - return true; - else - return false; - } catch (InterruptedException ie) { - return true; - } catch (TimeoutException te) { - return true; - } - } - - public void save_frequency() { - AltosPreferences.set_frequency(link.serial, frequency); - } - - public void set_telemetry(int in_telemetry) { - telemetry = in_telemetry; - link.set_telemetry(telemetry); - } - - public void save_telemetry() { - AltosPreferences.set_telemetry(link.serial, telemetry); - } - - public void set_monitor(boolean monitor) { - link.set_monitor(monitor); - } - - public File backing_file() { - return log.file(); - } - - public AltosTelemetryReader (AltosLink in_link) - throws IOException, InterruptedException, TimeoutException { - link = in_link; - log = new AltosLog(link); - name = link.name; - previous = null; - telem = new LinkedBlockingQueue(); - frequency = AltosPreferences.frequency(link.serial); - set_frequency(frequency); - telemetry = AltosPreferences.telemetry(link.serial); - set_telemetry(telemetry); - link.add_monitor(telem); - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecord.java b/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecord.java deleted file mode 100644 index 367c148d..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecord.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - -public abstract class AltosTelemetryRecord { - - long received_time; - abstract public AltosRecord update_state(AltosRecord previous); - - 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]; - } - - 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; - - static AltosTelemetryRecord parse_hex(String hex) throws ParseException, AltosCRCException { - AltosTelemetryRecord r; - - int[] bytes; - try { - bytes = Altos.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 = Altos.int8(bytes, bytes.length - 3) / 2 - 74; - int status = Altos.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 Altos.ao_telemetry_standard_len + 4: - int type = Altos.uint8(bytes, 4 + 1); - switch (type) { - case packet_type_TM_sensor: - case packet_type_Tm_sensor: - case packet_type_Tn_sensor: - r = new AltosTelemetryRecordSensor(bytes, rssi); - break; - case packet_type_configuration: - r = new AltosTelemetryRecordConfiguration(bytes); - break; - case packet_type_location: - r = new AltosTelemetryRecordLocation(bytes); - break; - case packet_type_satellite: - r = new AltosTelemetryRecordSatellite(bytes); - break; - case packet_type_companion: - r = new AltosTelemetryRecordCompanion(bytes); - break; - default: - r = new AltosTelemetryRecordRaw(bytes); - break; - } - break; - case Altos.ao_telemetry_0_9_len + 4: - r = new AltosTelemetryRecordLegacy(bytes, rssi, status); - break; - case Altos.ao_telemetry_0_8_len + 4: - r = new AltosTelemetryRecordLegacy(bytes, rssi, status); - break; - default: - throw new ParseException(String.format("Invalid packet length %d", bytes.length), 0); - } - r.received_time = System.currentTimeMillis(); - return r; - } - - public static AltosTelemetryRecord parse(String line) throws ParseException, AltosCRCException { - AltosTelemetryRecord r; - - 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++])); - } - - if (word[i].equals("TELEM")) - r = parse_hex(word[i+1]); - else - r = new AltosTelemetryRecordLegacy(line); - return r; - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordCompanion.java b/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordCompanion.java deleted file mode 100644 index 6ad17244..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordCompanion.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - -public class AltosTelemetryRecordCompanion extends AltosTelemetryRecordRaw { - - AltosRecordCompanion companion; - - public AltosTelemetryRecordCompanion(int[] in_bytes) { - super(in_bytes); - - int off = 0; - if (uint8(6) == 0) - off = 1; - int channels = uint8(7+off); - - if (off != 0 && channels >= 12) - channels = 11; - - companion = new AltosRecordCompanion(channels); - companion.tick = tick; - companion.board_id = uint8(5); - companion.update_period = uint8(6+off); - for (int i = 0; i < companion.companion_data.length; i++) - companion.companion_data[i] = uint16(8 + off + i * 2); - } - - public AltosRecord update_state(AltosRecord previous) { - AltosRecord next = super.update_state(previous); - - next.companion = companion; - next.seen |= AltosRecord.seen_sensor | AltosRecord.seen_temp_volt; - - companion.tick = tick; - return next; - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordConfiguration.java b/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordConfiguration.java deleted file mode 100644 index 25242edc..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordConfiguration.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - - -public class AltosTelemetryRecordConfiguration extends AltosTelemetryRecordRaw { - int device_type; - int flight; - int config_major; - int config_minor; - int apogee_delay; - int main_deploy; - int flight_log_max; - String callsign; - String version; - - public AltosTelemetryRecordConfiguration(int[] in_bytes) { - super(in_bytes); - - device_type = uint8(5); - flight = uint16(6); - config_major = uint8(8); - config_minor = uint8(9); - apogee_delay = uint16(10); - main_deploy = uint16(12); - flight_log_max = uint16(14); - callsign = string(16, 8); - version = string(24, 8); - } - - public AltosRecord update_state(AltosRecord previous) { - AltosRecord next = super.update_state(previous); - - next.device_type = device_type; - next.flight = flight; - next.config_major = config_major; - next.config_minor = config_minor; - next.apogee_delay = apogee_delay; - next.main_deploy = main_deploy; - next.flight_log_max = flight_log_max; - - next.callsign = callsign; - next.firmware_version = version; - - next.seen |= AltosRecord.seen_deploy | AltosRecord.seen_flight; - - return next; - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordGeneral.java b/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordGeneral.java deleted file mode 100644 index 5e157a54..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordGeneral.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - -import java.lang.*; -import java.text.*; -import java.util.HashMap; - -public class AltosTelemetryRecordGeneral { - - static AltosTelemetryRecord parse(String line) throws ParseException, AltosCRCException { - AltosTelemetryRecord r; - - 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++])); - } - - if (word[i].equals("TELEM")) - r = AltosTelemetryRecordRaw.parse(word[i+1]); - else - r = new AltosTelemetryRecordLegacy(line); - return r; - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordLegacy.java b/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordLegacy.java deleted file mode 100644 index 8e3713cc..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordLegacy.java +++ /dev/null @@ -1,521 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; - -import java.lang.*; -import java.text.*; -import java.util.HashMap; - -/* - * 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 AltosTelemetryRecordLegacy extends AltosTelemetryRecord { - /* - * 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"; - - AltosRecord record; - - private void parse_v4(String[] words, int i) throws ParseException { - AltosTelemetryMap map = new AltosTelemetryMap(words, i); - - record.callsign = map.get_string(AO_TELEM_CALL, "N0CALL"); - record.serial = map.get_int(AO_TELEM_SERIAL, AltosRecord.MISSING); - record.flight = map.get_int(AO_TELEM_FLIGHT, AltosRecord.MISSING); - record.rssi = map.get_int(AO_TELEM_RSSI, AltosRecord.MISSING); - record.state = Altos.state(map.get_string(AO_TELEM_STATE, "invalid")); - record.tick = map.get_int(AO_TELEM_TICK, 0); - - /* raw sensor values */ - record.accel = map.get_int(AO_TELEM_RAW_ACCEL, AltosRecord.MISSING); - record.pres = map.get_int(AO_TELEM_RAW_BARO, AltosRecord.MISSING); - record.temp = map.get_int(AO_TELEM_RAW_THERMO, AltosRecord.MISSING); - record.batt = map.get_int(AO_TELEM_RAW_BATT, AltosRecord.MISSING); - record.drogue = map.get_int(AO_TELEM_RAW_DROGUE, AltosRecord.MISSING); - record.main = map.get_int(AO_TELEM_RAW_MAIN, AltosRecord.MISSING); - - /* sensor calibration information */ - record.ground_accel = map.get_int(AO_TELEM_CAL_ACCEL_GROUND, AltosRecord.MISSING); - record.ground_pres = map.get_int(AO_TELEM_CAL_BARO_GROUND, AltosRecord.MISSING); - record.accel_plus_g = map.get_int(AO_TELEM_CAL_ACCEL_PLUS, AltosRecord.MISSING); - record.accel_minus_g = map.get_int(AO_TELEM_CAL_ACCEL_MINUS, AltosRecord.MISSING); - - /* flight computer values */ - record.acceleration = map.get_double(AO_TELEM_KALMAN_ACCEL, AltosRecord.MISSING, 1/16.0); - record.speed = map.get_double(AO_TELEM_KALMAN_SPEED, AltosRecord.MISSING, 1/16.0); - record.height = map.get_int(AO_TELEM_KALMAN_HEIGHT, AltosRecord.MISSING); - - record.flight_accel = map.get_int(AO_TELEM_ADHOC_ACCEL, AltosRecord.MISSING); - record.flight_vel = map.get_int(AO_TELEM_ADHOC_SPEED, AltosRecord.MISSING); - record.flight_pres = map.get_int(AO_TELEM_ADHOC_BARO, AltosRecord.MISSING); - - if (map.has(AO_TELEM_GPS_STATE)) { - record.gps = new AltosGPS(map); - record.new_gps = true; - } - else - record.gps = null; - } - - private void parse_legacy(String[] words, int i) throws ParseException { - - AltosParse.word (words[i++], "CALL"); - record.callsign = words[i++]; - - AltosParse.word (words[i++], "SERIAL"); - record.serial = AltosParse.parse_int(words[i++]); - - if (record.version >= 2) { - AltosParse.word (words[i++], "FLIGHT"); - record.flight = AltosParse.parse_int(words[i++]); - } else - record.flight = 0; - - AltosParse.word(words[i++], "RSSI"); - record.rssi = AltosParse.parse_int(words[i++]); - - /* Older telemetry data had mis-computed RSSI value */ - if (record.version <= 2) - record.rssi = (record.rssi + 74) / 2 - 74; - - AltosParse.word(words[i++], "STATUS"); - record.status = AltosParse.parse_hex(words[i++]); - - AltosParse.word(words[i++], "STATE"); - record.state = Altos.state(words[i++]); - - record.tick = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "a:"); - record.accel = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "p:"); - record.pres = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "t:"); - record.temp = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "v:"); - record.batt = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "d:"); - record.drogue = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "m:"); - record.main = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "fa:"); - record.flight_accel = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "ga:"); - record.ground_accel = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "fv:"); - record.flight_vel = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "fp:"); - record.flight_pres = AltosParse.parse_int(words[i++]); - - /* Old TeleDongle code with kalman-reporting TeleMetrum code */ - if ((record.flight_vel & 0xffff0000) == 0x80000000) { - record.speed = ((short) record.flight_vel) / 16.0; - record.acceleration = record.flight_accel / 16.0; - record.height = record.flight_pres; - record.flight_vel = AltosRecord.MISSING; - record.flight_pres = AltosRecord.MISSING; - record.flight_accel = AltosRecord.MISSING; - } - - AltosParse.word(words[i++], "gp:"); - record.ground_pres = AltosParse.parse_int(words[i++]); - - if (record.version >= 1) { - AltosParse.word(words[i++], "a+:"); - record.accel_plus_g = AltosParse.parse_int(words[i++]); - - AltosParse.word(words[i++], "a-:"); - record.accel_minus_g = AltosParse.parse_int(words[i++]); - } else { - record.accel_plus_g = record.ground_accel; - record.accel_minus_g = record.ground_accel + 530; - } - - record.gps = new AltosGPS(words, i, record.version); - record.new_gps = true; - } - - public AltosTelemetryRecordLegacy(String line) throws ParseException, AltosCRCException { - String[] words = line.split("\\s+"); - int i = 0; - - record = new AltosRecord(); - - if (words[i].equals("CRC") && words[i+1].equals("INVALID")) { - i += 2; - AltosParse.word(words[i++], "RSSI"); - record.rssi = AltosParse.parse_int(words[i++]); - throw new AltosCRCException(record.rssi); - } - if (words[i].equals("CALL")) { - record.version = 0; - } else { - AltosParse.word (words[i++], "VERSION"); - record.version = AltosParse.parse_int(words[i++]); - } - - if (record.version < 4) - parse_legacy(words, i); - else - parse_v4(words, i); - } - - /* - * Given a hex dump of a legacy telemetry line, construct an AltosRecord 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 AltosTelemetryRecordLegacy(int[] in_bytes, int in_rssi, int in_status) { - record = new AltosRecord(); - - bytes = in_bytes; - record.version = 4; - adjust = 0; - - if (bytes.length == AltosLib.ao_telemetry_0_8_len + 4) { - record.serial = uint8(0); - adjust = -1; - } else - record.serial = uint16(0); - - record.seen = AltosRecord.seen_flight | AltosRecord.seen_sensor | AltosRecord.seen_temp_volt | AltosRecord.seen_deploy; - - record.callsign = string(62, 8); - record.flight = uint16(2); - record.rssi = in_rssi; - record.status = in_status; - record.state = uint8(4); - record.tick = uint16(21); - record.accel = int16(23); - record.pres = int16(25); - record.temp = int16(27); - record.batt = int16(29); - record.drogue = int16(31); - record.main = int16(33); - - record.ground_accel = int16(7); - record.ground_pres = int16(15); - record.accel_plus_g = int16(17); - record.accel_minus_g = int16(19); - - if (uint16(11) == 0x8000) { - record.acceleration = int16(5); - record.speed = int16(9); - record.height = int16(13); - record.flight_accel = AltosRecord.MISSING; - record.flight_vel = AltosRecord.MISSING; - record.flight_pres = AltosRecord.MISSING; - } else { - record.flight_accel = int16(5); - record.flight_vel = uint32(9); - record.flight_pres = int16(13); - record.acceleration = AltosRecord.MISSING; - record.speed = AltosRecord.MISSING; - record.height = AltosRecord.MISSING; - } - - record.gps = null; - - int gps_flags = uint8(41); - - if ((gps_flags & (AO_GPS_VALID|AO_GPS_RUNNING)) != 0) { - record.gps = new AltosGPS(); - record.new_gps = true; - - record.seen |= record.seen_gps_time | record.seen_gps_lat | record.seen_gps_lon; - record.gps.nsat = (gps_flags & AO_GPS_NUM_SAT_MASK); - record.gps.locked = (gps_flags & AO_GPS_VALID) != 0; - record.gps.connected = true; - record.gps.lat = uint32(42) / 1.0e7; - record.gps.lon = uint32(46) / 1.0e7; - record.gps.alt = int16(50); - record.gps.ground_speed = uint16(52) / 100.0; - record.gps.course = uint8(54) * 2; - record.gps.hdop = uint8(55) / 5.0; - record.gps.h_error = uint16(58); - record.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) { - record.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) - record.gps.cc_gps_sat[n_tracking_actual++] = new AltosGPSSat(svid, c_n0); - } - } - } - - record.time = 0.0; - } - - public AltosRecord update_state(AltosRecord previous) { - return record; - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordLocation.java b/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordLocation.java deleted file mode 100644 index cddb773d..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordLocation.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - - -public class AltosTelemetryRecordLocation extends AltosTelemetryRecordRaw { - int flags; - int altitude; - int latitude; - int longitude; - int year; - int month; - int day; - int hour; - int minute; - int second; - int pdop; - int hdop; - int vdop; - int mode; - int ground_speed; - int climb_rate; - int course; - - public AltosTelemetryRecordLocation(int[] in_bytes) { - super(in_bytes); - - flags = uint8(5); - altitude = int16(6); - latitude = uint32(8); - longitude = uint32(12); - year = uint8(16); - month = uint8(17); - day = uint8(18); - hour = uint8(19); - minute = uint8(20); - second = uint8(21); - pdop = uint8(22); - hdop = uint8(23); - vdop = uint8(24); - mode = uint8(25); - ground_speed = uint16(26); - climb_rate = int16(28); - course = uint8(30); - } - - public AltosRecord update_state(AltosRecord previous) { - AltosRecord next = super.update_state(previous); - - if (next.gps == null) - next.gps = new AltosGPS(); - - next.gps.nsat = flags & 0xf; - next.gps.locked = (flags & (1 << 4)) != 0; - next.gps.connected = (flags & (1 << 5)) != 0; - - if (next.gps.locked) { - next.gps.lat = latitude * 1.0e-7; - next.gps.lon = longitude * 1.0e-7; - next.gps.alt = altitude; - next.gps.year = 2000 + year; - next.gps.month = month; - next.gps.day = day; - next.gps.hour = hour; - next.gps.minute = minute; - next.gps.second = second; - next.gps.ground_speed = ground_speed * 1.0e-2; - next.gps.course = course * 2; - next.gps.climb_rate = climb_rate * 1.0e-2; - next.gps.hdop = hdop; - next.gps.vdop = vdop; - next.seen |= AltosRecord.seen_gps_time | AltosRecord.seen_gps_lat | AltosRecord.seen_gps_lon; - next.new_gps = true; - } - - return next; - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordRaw.java b/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordRaw.java deleted file mode 100644 index 43d0f17a..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordRaw.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - -import java.lang.*; -import java.text.*; -import java.util.HashMap; - -public class AltosTelemetryRecordRaw extends AltosTelemetryRecord { - int[] bytes; - int serial; - int tick; - int type; - - long received_time; - - public int int8(int off) { - return AltosLib.int8(bytes, off + 1); - } - - public int uint8(int off) { - return AltosLib.uint8(bytes, off + 1); - } - - public int int16(int off) { - return AltosLib.int16(bytes, off + 1); - } - - public int uint16(int off) { - return AltosLib.uint16(bytes, off + 1); - } - - public int uint32(int off) { - return AltosLib.uint32(bytes, off + 1); - } - - public String string(int off, int l) { - return AltosLib.string(bytes, off + 1, l); - } - - public AltosTelemetryRecordRaw(int[] in_bytes) { - bytes = in_bytes; - serial = uint16(0); - tick = uint16(2); - type = uint8(4); - } - - public AltosRecord update_state(AltosRecord previous) { - AltosRecord next; - if (previous != null) - next = new AltosRecord(previous); - else - next = new AltosRecord(); - next.serial = serial; - next.tick = tick; - return next; - } - - public long received_time() { - return received_time; - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordSatellite.java b/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordSatellite.java deleted file mode 100644 index 2526afb6..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordSatellite.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - -public class AltosTelemetryRecordSatellite extends AltosTelemetryRecordRaw { - int channels; - AltosGPSSat[] sats; - - public AltosTelemetryRecordSatellite(int[] in_bytes) { - super(in_bytes); - - channels = uint8(5); - if (channels > 12) - channels = 12; - if (channels == 0) - sats = null; - else { - sats = new AltosGPSSat[channels]; - for (int i = 0; i < channels; i++) { - int svid = uint8(6 + i * 2 + 0); - int c_n_1 = uint8(6 + i * 2 + 1); - sats[i] = new AltosGPSSat(svid, c_n_1); - } - } - } - - public AltosRecord update_state(AltosRecord previous) { - AltosRecord next = super.update_state(previous); - - if (next.gps == null) - next.gps = new AltosGPS(); - - next.gps.cc_gps_sat = sats; - - return next; - } -} diff --git a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordSensor.java b/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordSensor.java deleted file mode 100644 index cfaf90b0..00000000 --- a/altoslib/src/org/altusmetrum/AltosLib/AltosTelemetryRecordSensor.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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; - - -public class AltosTelemetryRecordSensor extends AltosTelemetryRecordRaw { - int state; - int accel; - int pres; - int temp; - int v_batt; - int sense_d; - int sense_m; - - int acceleration; - int speed; - int height; - - int ground_accel; - int ground_pres; - int accel_plus_g; - int accel_minus_g; - - int rssi; - - public AltosTelemetryRecordSensor(int[] in_bytes, int in_rssi) { - super(in_bytes); - state = uint8(5); - - accel = int16(6); - pres = int16(8); - temp = int16(10); - v_batt = int16(12); - sense_d = int16(14); - sense_m = int16(16); - - acceleration = int16(18); - speed = int16(20); - height = int16(22); - - ground_pres = int16(24); - ground_accel = int16(26); - accel_plus_g = int16(28); - accel_minus_g = int16(30); - - rssi = in_rssi; - } - - public AltosRecord update_state(AltosRecord previous) { - AltosRecord next = super.update_state(previous); - - next.state = state; - if (type == packet_type_TM_sensor) - next.accel = accel; - else - next.accel = AltosRecord.MISSING; - next.pres = pres; - next.temp = temp; - next.batt = v_batt; - if (type == packet_type_TM_sensor || type == packet_type_Tm_sensor) { - next.drogue = sense_d; - next.main = sense_m; - } else { - next.drogue = AltosRecord.MISSING; - next.main = AltosRecord.MISSING; - } - - next.acceleration = acceleration / 16.0; - next.speed = speed / 16.0; - next.height = height; - - next.ground_pres = ground_pres; - if (type == packet_type_TM_sensor) { - next.ground_accel = ground_accel; - next.accel_plus_g = accel_plus_g; - next.accel_minus_g = accel_minus_g; - } else { - next.ground_accel = AltosRecord.MISSING; - next.accel_plus_g = AltosRecord.MISSING; - next.accel_minus_g = AltosRecord.MISSING; - } - - next.rssi = rssi; - - next.seen |= AltosRecord.seen_sensor | AltosRecord.seen_temp_volt; - - return next; - } -}