diff options
Diffstat (limited to 'altoslib')
34 files changed, 743 insertions, 446 deletions
diff --git a/altoslib/AltosCSV.java b/altoslib/AltosCSV.java index f55b4785..a8de23f0 100644 --- a/altoslib/AltosCSV.java +++ b/altoslib/AltosCSV.java @@ -29,9 +29,11 @@ public class AltosCSV implements AltosWriter { int boost_tick; boolean has_basic; + boolean has_radio; boolean has_battery; boolean has_flight_state; boolean has_advanced; + boolean has_igniter; boolean has_gps; boolean has_gps_sat; boolean has_companion; @@ -39,7 +41,7 @@ public class AltosCSV implements AltosWriter { AltosFlightSeries series; int[] indices; - static final int ALTOS_CSV_VERSION = 5; + static final int ALTOS_CSV_VERSION = 6; /* Version 4 format: * @@ -49,7 +51,8 @@ public class AltosCSV implements AltosWriter { * flight number * callsign * time (seconds since boost) - * clock (tick count / 100) + * + * Radio info (if available) * rssi * link quality * @@ -81,6 +84,14 @@ public class AltosCSV implements AltosWriter { * mag_x (g) * mag_y (g) * mag_z (g) + * tilt (d) + * + * Extra igniter voltages (if available) + * pyro (V) + * igniter_a (V) + * igniter_b (V) + * igniter_c (V) + * igniter_d (V) * * GPS data (if available) * connected (1/0) @@ -115,13 +126,26 @@ public class AltosCSV implements AltosWriter { */ void write_general_header() { - out.printf("version,serial,flight,call,time,clock,rssi,lqi"); + out.printf("version,serial,flight,call,time"); } double time() { return series.time(indices); } + void write_general() { + out.printf("%s, %d, %d, %s, %8.2f", + ALTOS_CSV_VERSION, + series.cal_data().serial, + series.cal_data().flight, + series.cal_data().callsign, + time()); + } + + void write_radio_header() { + out.printf("rssi,lqi"); + } + int rssi() { return (int) series.value(AltosFlightSeries.rssi_name, indices); } @@ -130,12 +154,8 @@ public class AltosCSV implements AltosWriter { return (int) series.value(AltosFlightSeries.status_name, indices); } - void write_general() { - double time = time(); - out.printf("%s, %d, %d, %s, %8.2f, %8.2f, %4d, %3d", - ALTOS_CSV_VERSION, series.cal_data().serial, - series.cal_data().flight, series.cal_data().callsign, - time, time, + void write_radio() { + out.printf("%4d, %3d", rssi(), status() & 0x7f); } @@ -149,7 +169,7 @@ public class AltosCSV implements AltosWriter { void write_flight() { int state = state(); - out.printf("%d,%8s", state, AltosLib.state_name(state)); + out.printf("%2d,%8s", state, AltosLib.state_name(state)); } void write_basic_header() { @@ -189,7 +209,7 @@ public class AltosCSV implements AltosWriter { } void write_advanced_header() { - out.printf("accel_x,accel_y,accel_z,gyro_x,gyro_y,gyro_z,mag_x,mag_y,mag_z"); + out.printf("accel_x,accel_y,accel_z,gyro_x,gyro_y,gyro_z,mag_x,mag_y,mag_z,tilt"); } double accel_along() { return series.value(AltosFlightSeries.accel_along_name, indices); } @@ -204,11 +224,30 @@ public class AltosCSV implements AltosWriter { double mag_across() { return series.value(AltosFlightSeries.mag_across_name, indices); } double mag_through() { return series.value(AltosFlightSeries.mag_through_name, indices); } + double tilt() { return series.value(AltosFlightSeries.orient_name, indices); } + void write_advanced() { - out.printf("%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f", + out.printf("%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f", accel_along(), accel_across(), accel_through(), gyro_roll(), gyro_pitch(), gyro_yaw(), - mag_along(), mag_across(), mag_through()); + mag_along(), mag_across(), mag_through(), + tilt()); + } + + void write_igniter_header() { + out.printf("pyro"); + for (int i = 0; i < series.igniter_voltage.length; i++) + out.printf(",%s", AltosLib.igniter_short_name(i)); + } + + double pyro() { return series.value(AltosFlightSeries.pyro_voltage_name, indices); } + + double igniter_value(int channel) { return series.value(series.igniter_voltage_name(channel), indices); } + + void write_igniter() { + out.printf("%5.2f", pyro()); + for (int i = 0; i < series.igniter_voltage.length; i++) + out.printf(",%5.2f", igniter_value(i)); } void write_gps_header() { @@ -306,6 +345,10 @@ public class AltosCSV implements AltosWriter { void write_header() { out.printf("#"); write_general_header(); + if (has_radio) { + out.printf(","); + write_radio_header(); + } if (has_flight_state) { out.printf(","); write_flight_header(); @@ -322,6 +365,10 @@ public class AltosCSV implements AltosWriter { out.printf(","); write_advanced_header(); } + if (has_igniter) { + out.printf(","); + write_igniter_header(); + } if (has_gps) { out.printf(","); write_gps_header(); @@ -339,6 +386,10 @@ public class AltosCSV implements AltosWriter { void write_one() { write_general(); + if (has_radio) { + out.printf(","); + write_radio(); + } if (has_flight_state) { out.printf(","); write_flight(); @@ -355,6 +406,10 @@ public class AltosCSV implements AltosWriter { out.printf(","); write_advanced(); } + if (has_igniter) { + out.printf(","); + write_igniter(); + } if (has_gps) { out.printf(","); write_gps(); @@ -395,14 +450,18 @@ public class AltosCSV implements AltosWriter { series.finish(); + has_radio = false; has_flight_state = false; has_basic = false; has_battery = false; has_advanced = false; + has_igniter = false; has_gps = false; has_gps_sat = false; has_companion = false; + if (series.has_series(AltosFlightSeries.rssi_name)) + has_radio = true; if (series.has_series(AltosFlightSeries.state_name)) has_flight_state = true; if (series.has_series(AltosFlightSeries.accel_name) || series.has_series(AltosFlightSeries.pressure_name)) @@ -411,6 +470,8 @@ public class AltosCSV implements AltosWriter { has_battery = true; if (series.has_series(AltosFlightSeries.accel_across_name)) has_advanced = true; + if (series.has_series(AltosFlightSeries.pyro_voltage_name)) + has_igniter = true; if (series.gps_series != null) has_gps = true; diff --git a/altoslib/AltosCalData.java b/altoslib/AltosCalData.java index 6258c1a8..03e2cbd7 100644 --- a/altoslib/AltosCalData.java +++ b/altoslib/AltosCalData.java @@ -72,6 +72,13 @@ public class AltosCalData { } } + public int log_format = AltosLib.MISSING; + + public void set_log_format(int log_format) { + if (log_format != AltosLib.MISSING) + this.log_format = log_format; + } + public int config_major = AltosLib.MISSING; public int config_minor = AltosLib.MISSING; public int flight_log_max = AltosLib.MISSING; @@ -192,7 +199,6 @@ public class AltosCalData { tick = AltosLib.MISSING; prev_tick = AltosLib.MISSING; temp_gps = null; - prev_gps = null; temp_gps_sat_tick = AltosLib.MISSING; accel = AltosLib.MISSING; } @@ -235,13 +241,19 @@ public class AltosCalData { public AltosGPS gps_pad = null; + public AltosGPS prev_gps = null; + public double gps_pad_altitude = AltosLib.MISSING; - public void set_gps(AltosGPS gps) { - if ((state != AltosLib.MISSING && state < AltosLib.ao_flight_boost) || gps_pad == null) - gps_pad = gps; - if (gps_pad_altitude == AltosLib.MISSING && gps.alt != AltosLib.MISSING) - gps_pad_altitude = gps.alt; + public void set_cal_gps(AltosGPS gps) { + if (gps.locked && gps.nsat >= 4) { + if ((state != AltosLib.MISSING && state < AltosLib.ao_flight_boost) || gps_pad == null) + gps_pad = gps; + if (gps_pad_altitude == AltosLib.MISSING && gps.alt != AltosLib.MISSING) + gps_pad_altitude = gps.alt; + } + temp_gps = null; + prev_gps = gps; } /* @@ -249,33 +261,24 @@ public class AltosCalData { * object and then deliver the result atomically to the listener */ AltosGPS temp_gps = null; - AltosGPS prev_gps = null; int temp_gps_sat_tick = AltosLib.MISSING; - public AltosGPS temp_gps() { + public AltosGPS temp_cal_gps() { return temp_gps; } - public void reset_temp_gps() { - if (temp_gps != null) { - if (temp_gps.locked && temp_gps.nsat >= 4) - set_gps(temp_gps); - prev_gps = temp_gps; - temp_gps = null; - } + public void reset_temp_cal_gps() { + if (temp_gps != null) + set_cal_gps(temp_gps); } - public boolean gps_pending() { + public boolean cal_gps_pending() { return temp_gps != null; } - public AltosGPS make_temp_gps(int tick, boolean sats) { - if (temp_gps == null) { - if (prev_gps != null) - temp_gps = prev_gps.clone(); - else - temp_gps = new AltosGPS(); - } + public AltosGPS make_temp_cal_gps(int tick, boolean sats) { + if (temp_gps == null) + temp_gps = new AltosGPS(prev_gps); if (sats) { if (tick != temp_gps_sat_tick) temp_gps.cc_gps_sat = null; diff --git a/altoslib/AltosDataListener.java b/altoslib/AltosDataListener.java index be6d840f..9a1e1465 100644 --- a/altoslib/AltosDataListener.java +++ b/altoslib/AltosDataListener.java @@ -19,10 +19,16 @@ public abstract class AltosDataListener { private AltosCalData cal_data = null; public double time = AltosLib.MISSING; - public int state = AltosLib.MISSING; public double frequency = AltosLib.MISSING; + public int raw_tick = AltosLib.MISSING; + + public int tick() { + return raw_tick; + } + public void set_tick(int tick) { + raw_tick = tick; cal_data.set_tick(tick); set_time(cal_data.time()); } @@ -42,14 +48,34 @@ public abstract class AltosDataListener { cal_data().set_serial(serial); } + public void set_device_type(int device_type) { + cal_data().set_device_type(device_type); + switch (device_type) { + case AltosLib.product_telegps: + set_state(AltosLib.ao_flight_stateless); + break; + } + } + + public void set_log_format(int log_format) { + cal_data().set_log_format(log_format); + switch (log_format) { + case AltosLib.AO_LOG_FORMAT_TELEGPS: + set_state(AltosLib.ao_flight_stateless); + break; + } + } + public double time() { return time; } public void set_state(int state) { cal_data().set_state(state); - if (state != AltosLib.MISSING) - this.state = state; + } + + public int state() { + return cal_data().state; } public void set_flight(int flight) { @@ -64,6 +90,12 @@ public abstract class AltosDataListener { public void finish() { } + public void init() { + set_state(AltosLib.ao_flight_invalid); + time = AltosLib.MISSING; + frequency = AltosLib.MISSING; + } + public abstract void set_rssi(int rssi, int status); public abstract void set_received_time(long received_time); @@ -79,7 +111,18 @@ public abstract class AltosDataListener { public abstract void set_apogee_voltage(double volts); public abstract void set_main_voltage(double volts); - public abstract void set_gps(AltosGPS gps); + public void set_gps(AltosGPS gps) { + AltosCalData cal_data = cal_data(); + cal_data.set_cal_gps(gps); + } + + public AltosGPS make_temp_gps(boolean sats) { + return cal_data().make_temp_cal_gps(tick(), sats); + } + + public AltosGPS temp_gps() { + return cal_data().temp_cal_gps(); + } public abstract void set_orient(double orient); public abstract void set_gyro(double roll, double pitch, double yaw); diff --git a/altoslib/AltosEeprom.java b/altoslib/AltosEeprom.java index ad7bf881..124bd478 100644 --- a/altoslib/AltosEeprom.java +++ b/altoslib/AltosEeprom.java @@ -22,6 +22,7 @@ public class AltosEeprom { private AltosJson config; ArrayList<Byte> data; private AltosConfigData config_data; + int errors = 0; /* * Public accessor APIs diff --git a/altoslib/AltosEepromDownload.java b/altoslib/AltosEepromDownload.java index 33f0dd17..547b523f 100644 --- a/altoslib/AltosEepromDownload.java +++ b/altoslib/AltosEepromDownload.java @@ -40,6 +40,7 @@ class AltosEepromNameData extends AltosDataListener { public void set_main_voltage(double volts) { } public void set_gps(AltosGPS gps) { + super.set_gps(gps); if (gps != null && gps.year != AltosLib.MISSING && gps.month != AltosLib.MISSING && @@ -199,25 +200,47 @@ public class AltosEepromDownload implements Runnable { AltosFile f = MakeFile(flights.config_data.serial, log.flight, name_data); - monitor.set_filename(f.toString()); + log.set_file(f); - FileWriter w = new FileWriter(f); + boolean do_write = true; - eeprom.write(w); - w.close(); + if (f.exists()) + do_write = monitor.check_overwrite(f); + + if (do_write) { + FileWriter w = new FileWriter(f); + + eeprom.write(w); + w.close(); + } + + if (eeprom.errors != 0) + throw new ParseException(String.format("%d CRC Errors", eeprom.errors), 0); + } + + static String label(int flight) { + if (flight < 0) + return "Corrupt"; + else + return "Flight"; + } + + static int flight(int flight) { + if (flight < 0) + return -flight; + return flight; } public void run () { boolean success = false; try { - boolean failed = false; if (remote) link.start_remote(); for (AltosEepromLog log : flights) { parse_errors = null; - if (log.selected) { + if (log.download_selected) { monitor.reset(); try { CaptureLog(log); @@ -225,16 +248,16 @@ public class AltosEepromDownload implements Runnable { LogError(e.getMessage()); } } + success = true; if (parse_errors != null) { - failed = true; - monitor.show_message(String.format("Flight %d download error. Valid log data saved\n%s", - log.flight, + monitor.show_message(String.format("%s %d download error. Valid log data saved\n%s", + label(log.flight), + flight(log.flight), parse_errors), link.name, AltosEepromMonitor.WARNING_MESSAGE); } } - success = !failed; } catch (IOException ee) { monitor.show_message(ee.getLocalizedMessage(), link.name, diff --git a/altoslib/AltosEepromList.java b/altoslib/AltosEepromList.java index 55d47e20..c55bcaaa 100644 --- a/altoslib/AltosEepromList.java +++ b/altoslib/AltosEepromList.java @@ -87,7 +87,7 @@ public class AltosEepromList extends ArrayList<AltosEepromLog> { start = AltosParse.parse_hex(tokens[3]); if (tokens[4].equals("end")) end = AltosParse.parse_hex(tokens[5]); - if (flight > 0 && start >= 0 && end > 0) + if (flight != 0 && start >= 0 && end > 0) flights.add(new AltosEepromFlight(flight, start, end)); } catch (ParseException pe) { System.out.printf("Parse error %s\n", pe.toString()); } } @@ -115,4 +115,4 @@ public class AltosEepromList extends ArrayList<AltosEepromLog> { link.flush_output(); } } -}
\ No newline at end of file +} diff --git a/altoslib/AltosEepromLog.java b/altoslib/AltosEepromLog.java index 8d1f3fc4..ba722b89 100644 --- a/altoslib/AltosEepromLog.java +++ b/altoslib/AltosEepromLog.java @@ -18,6 +18,7 @@ package org.altusmetrum.altoslib_12; +import java.io.*; import java.text.*; import java.util.concurrent.*; @@ -32,7 +33,15 @@ public class AltosEepromLog { public int start_block; public int end_block; - public boolean selected; + public boolean download_selected; + public boolean delete_selected; + public boolean graph_selected; + + public File file; + + public void set_file(File file) { + this.file = file; + } public AltosEepromLog(AltosConfigData config_data, AltosLink link, @@ -50,8 +59,11 @@ public class AltosEepromLog { serial = config_data.serial; /* - * Select all flights for download + * Select all flights for download and graph, but not + * for delete */ - selected = true; + download_selected = true; + delete_selected = false; + graph_selected = true; } } diff --git a/altoslib/AltosEepromMonitor.java b/altoslib/AltosEepromMonitor.java index a99ec687..11144a3a 100644 --- a/altoslib/AltosEepromMonitor.java +++ b/altoslib/AltosEepromMonitor.java @@ -18,6 +18,8 @@ package org.altusmetrum.altoslib_12; +import java.io.*; + public interface AltosEepromMonitor { public void set_block(int in_block); @@ -28,8 +30,6 @@ public interface AltosEepromMonitor { public void set_flight(int in_flight); - public void set_filename(String in_file); - public void set_thread(Thread eeprom_thread); final static int INFO_MESSAGE = 0; @@ -38,6 +38,8 @@ public interface AltosEepromMonitor { public void show_message(String message, String title, int message_type); + public Boolean check_overwrite(File file); + public void start(); public void done(boolean success); diff --git a/altoslib/AltosEepromRecord.java b/altoslib/AltosEepromRecord.java index 094584fe..43e8ea4d 100644 --- a/altoslib/AltosEepromRecord.java +++ b/altoslib/AltosEepromRecord.java @@ -50,8 +50,22 @@ public abstract class AltosEepromRecord implements Comparable<AltosEepromRecord> return data8(i) | (data8(i+1) << 8) | (data8(i+2) << 16) | (data8(i+3) << 24); } + public boolean empty(int s) { + for (int i = 0; i < length; i++) + if (eeprom.data8(s + i) != 0xff) + return false; + return true; + } + public boolean valid(int s) { - return AltosConvert.checksum(eeprom.data, s, length) == 0; + int ck = AltosConvert.checksum(eeprom.data, s, length); + + if (ck != 0) { + ++eeprom.errors; + System.out.printf("invalid checksum 0x%x at 0x%x\n", ck, s); + return false; + } + return true; } public boolean valid() { @@ -83,18 +97,16 @@ public abstract class AltosEepromRecord implements Comparable<AltosEepromRecord> /* AltosDataProvider */ public void provide_data(AltosDataListener listener, AltosCalData cal_data) { - cal_data.set_tick(tick()); + listener.set_tick(tick()); if (cmd() == AltosLib.AO_LOG_FLIGHT) cal_data.set_boost_tick(); listener.set_time(cal_data.time()); /* Flush any pending GPS changes */ if (!AltosLib.is_gps_cmd(cmd())) { - AltosGPS gps = cal_data.temp_gps(); - if (gps != null) { + AltosGPS gps = listener.temp_gps(); + if (gps != null) listener.set_gps(gps); - cal_data.reset_temp_gps(); - } } } @@ -102,25 +114,18 @@ public abstract class AltosEepromRecord implements Comparable<AltosEepromRecord> int s = start + length; while (s + length <= eeprom.data.size()) { - if (valid(s)) + if (!empty(s) && valid(s)) return s; s += length; } return -1; } - public boolean hasNext() { - return next_start() >= 0; - } - public abstract AltosEepromRecord next(); public AltosEepromRecord(AltosEeprom eeprom, int start, int length) { this.eeprom = eeprom; this.start = start; this.length = length; - - while (start + length < eeprom.data.size() && !valid()) - start += length; } } diff --git a/altoslib/AltosEepromRecordFull.java b/altoslib/AltosEepromRecordFull.java index 32df9578..7e92d353 100644 --- a/altoslib/AltosEepromRecordFull.java +++ b/altoslib/AltosEepromRecordFull.java @@ -53,7 +53,7 @@ public class AltosEepromRecordFull extends AltosEepromRecord { listener.set_state(data16(0)); break; case AltosLib.AO_LOG_GPS_TIME: - gps = cal_data.make_temp_gps(tick(),false); + gps = listener.make_temp_gps(false); gps.hour = data8(0); gps.minute = data8(1); @@ -67,29 +67,29 @@ public class AltosEepromRecordFull extends AltosEepromRecord { AltosLib.AO_GPS_NUM_SAT_SHIFT; break; case AltosLib.AO_LOG_GPS_LAT: - gps = cal_data.make_temp_gps(tick(),false); + gps = listener.make_temp_gps(false); int lat32 = data32(0); gps.lat = (double) lat32 / 1e7; break; case AltosLib.AO_LOG_GPS_LON: - gps = cal_data.make_temp_gps(tick(),false); + gps = listener.make_temp_gps(false); int lon32 = data32(0); gps.lon = (double) lon32 / 1e7; break; case AltosLib.AO_LOG_GPS_ALT: - gps = cal_data.make_temp_gps(tick(),false); + gps = listener.make_temp_gps(false); gps.alt = data16(0); break; case AltosLib.AO_LOG_GPS_SAT: - gps = cal_data.make_temp_gps(tick(),true); + gps = listener.make_temp_gps(true); int svid = data16(0); int c_n0 = data16(2); gps.add_sat(svid, c_n0); break; case AltosLib.AO_LOG_GPS_DATE: - gps = cal_data.make_temp_gps(tick(),false); + gps = listener.make_temp_gps(false); gps.year = data8(0) + 2000; gps.month = data8(1); gps.day = data8(2); diff --git a/altoslib/AltosEepromRecordMega.java b/altoslib/AltosEepromRecordMega.java index ad3e23fd..86343fe0 100644 --- a/altoslib/AltosEepromRecordMega.java +++ b/altoslib/AltosEepromRecordMega.java @@ -31,6 +31,7 @@ public class AltosEepromRecordMega extends AltosEepromRecord { private int ground_roll() { switch (log_format) { case AltosLib.AO_LOG_FORMAT_TELEMEGA: + case AltosLib.AO_LOG_FORMAT_TELEMEGA_3: return data32(16); case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD: return data16(14); @@ -41,6 +42,7 @@ public class AltosEepromRecordMega extends AltosEepromRecord { private int ground_pitch() { switch (log_format) { case AltosLib.AO_LOG_FORMAT_TELEMEGA: + case AltosLib.AO_LOG_FORMAT_TELEMEGA_3: return data32(20); case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD: return data16(16); @@ -51,6 +53,7 @@ public class AltosEepromRecordMega extends AltosEepromRecord { private int ground_yaw() { switch (log_format) { case AltosLib.AO_LOG_FORMAT_TELEMEGA: + case AltosLib.AO_LOG_FORMAT_TELEMEGA_3: return data32(24); case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD: return data16(18); @@ -188,7 +191,7 @@ public class AltosEepromRecordMega extends AltosEepromRecord { listener.set_pyro_fired(pyro()); break; case AltosLib.AO_LOG_GPS_TIME: - gps = cal_data.make_temp_gps(tick(), false); + gps = listener.make_temp_gps(false); gps.lat = latitude() / 1e7; gps.lon = longitude() / 1e7; @@ -231,7 +234,7 @@ public class AltosEepromRecordMega extends AltosEepromRecord { } break; case AltosLib.AO_LOG_GPS_SAT: - gps = cal_data.make_temp_gps(tick(), true); + gps = listener.make_temp_gps(true); int n = nsat(); if (n > max_sat) diff --git a/altoslib/AltosEepromRecordMetrum.java b/altoslib/AltosEepromRecordMetrum.java index 3da50544..888a06cc 100644 --- a/altoslib/AltosEepromRecordMetrum.java +++ b/altoslib/AltosEepromRecordMetrum.java @@ -91,7 +91,7 @@ public class AltosEepromRecordMetrum extends AltosEepromRecord { listener.set_main_voltage(AltosConvert.mega_pyro_voltage(sense_m())); break; case AltosLib.AO_LOG_GPS_POS: - gps = cal_data.make_temp_gps(tick(), false); + gps = listener.make_temp_gps(false); gps.lat = latitude() / 1e7; gps.lon = longitude() / 1e7; if (config_data().altitude_32()) @@ -100,7 +100,7 @@ public class AltosEepromRecordMetrum extends AltosEepromRecord { gps.alt = altitude_low(); break; case AltosLib.AO_LOG_GPS_TIME: - gps = cal_data.make_temp_gps(tick(), false); + gps = listener.make_temp_gps(false); gps.hour = hour(); gps.minute = minute(); @@ -119,7 +119,7 @@ public class AltosEepromRecordMetrum extends AltosEepromRecord { gps.pdop = pdop() / 10.0; break; case AltosLib.AO_LOG_GPS_SAT: - gps = cal_data.make_temp_gps(tick(), true); + gps = listener.make_temp_gps(true); int n = nsat(); for (int i = 0; i < n; i++) diff --git a/altoslib/AltosEepromRecordSet.java b/altoslib/AltosEepromRecordSet.java index 48e90c05..36075931 100644 --- a/altoslib/AltosEepromRecordSet.java +++ b/altoslib/AltosEepromRecordSet.java @@ -44,6 +44,8 @@ public class AltosEepromRecordSet implements AltosRecordSet { AltosCalData cal_data = cal_data(); cal_data.reset(); + listener.set_log_format(config_data().log_format); + for (AltosEepromRecord record : ordered) { record.provide_data(listener, cal_data); } @@ -67,6 +69,7 @@ public class AltosEepromRecordSet implements AltosRecordSet { case AltosLib.AO_LOG_FORMAT_TELEMETRY: case AltosLib.AO_LOG_FORMAT_TELESCIENCE: case AltosLib.AO_LOG_FORMAT_TELEMEGA: + case AltosLib.AO_LOG_FORMAT_TELEMEGA_3: case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD: record = new AltosEepromRecordMega(eeprom); break; @@ -95,7 +98,7 @@ public class AltosEepromRecordSet implements AltosRecordSet { int tick = 0; boolean first = true; - for (;;) { + do { int t = record.tick(); if (first) { @@ -108,10 +111,8 @@ public class AltosEepromRecordSet implements AltosRecordSet { } record.wide_tick = tick; ordered.add(record); - if (!record.hasNext()) - break; record = record.next(); - } + } while (record != null); } public AltosEepromRecordSet(InputStream input) throws IOException { diff --git a/altoslib/AltosFile.java b/altoslib/AltosFile.java index 69f779c1..6f98b87a 100644 --- a/altoslib/AltosFile.java +++ b/altoslib/AltosFile.java @@ -36,10 +36,23 @@ public class AltosFile extends File { return String.format("-via-%04d", receiver); } + static private String label(int flight) { + if (flight < 0) + return "corrupt"; + else + return "flight"; + } + + static private int flight(int flight) { + if (flight < 0) + return -flight; + return flight; + } + public AltosFile(int year, int month, int day, int serial, int flight, int receiver, String extension) { super (AltosPreferences.logdir(), - String.format("%04d-%02d-%02d-serial-%s-flight-%s%s.%s", - year, month, day, number(serial), number(flight), receiver(receiver), extension)); + String.format("%04d-%02d-%02d-serial-%s-%s-%s%s.%s", + year, month, day, number(serial), label(flight), number(flight(flight)), receiver(receiver), extension)); } public AltosFile(int year, int month, int day, int serial, int flight, String extension) { diff --git a/altoslib/AltosFilterListener.java b/altoslib/AltosFilterListener.java new file mode 100644 index 00000000..fe91100a --- /dev/null +++ b/altoslib/AltosFilterListener.java @@ -0,0 +1,22 @@ +/* + * Copyright © 2017 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +package org.altusmetrum.altoslib_12; + +public interface AltosFilterListener { + void filter_changed(double speed_filter, double accel_filter); + + double speed_filter(); + double accel_filter(); +} diff --git a/altoslib/AltosFlightListener.java b/altoslib/AltosFlightListener.java deleted file mode 100644 index d61831a9..00000000 --- a/altoslib/AltosFlightListener.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright © 2017 Keith Packard <keithp@keithp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -package org.altusmetrum.altoslib_12; - -public abstract class AltosFlightListener { - - public int flight; - public int serial; - public int tick; - public int boost_tick; - public int state; - - public double accel_plus_g; - public double accel_minus_g; - public double accel; - - public double ground_pressure; - public double ground_altitude; - - AltosGPS temp_gps; - int temp_gps_sat_tick; - int gps_sequence; - - /* AltosEepromRecord */ - public void set_boost_tick(int boost_tick) { - if (boost_tick != AltosLib.MISSING) - this.boost_tick = boost_tick; - } - - public void set_tick(int tick) { - if (tick != AltosLib.MISSING) - this.tick = tick; - } - - public double time() { - if (tick == AltosLib.MISSING) - return AltosLib.MISSING; - if (boost_tick != AltosLib.MISSING) - return (tick - boost_tick) / 100.0; - else - return tick / 100.0; - } - - public double boost_time() { - if (boost_tick == AltosLib.MISSING) - return AltosLib.MISSING; - return boost_tick / 100.0; - } - - public abstract void set_rssi(int rssi, int status); - public abstract void set_received_time(long received_time); - - /* AltosEepromRecordFull */ - - public void set_serial(int serial) { - if (serial != AltosLib.MISSING) - this.serial = serial; - } - - public void set_state(int state) { - if (state != AltosLib.MISSING) - this.state = state; - } - - public int state() { return state; } - - public abstract void set_ground_accel(double ground_accel); - public void set_flight(int flight) { - if (flight != AltosLib.MISSING) - this.flight = flight; - } - public int flight() { - return flight; - } - - public abstract void set_accel(double accel); - public abstract void set_acceleration(double accel); - public abstract void set_accel_g(double accel_plus_g, double accel_minus_g); - public abstract void set_pressure(double pa); - public abstract void set_thrust(double N); - - public abstract void set_temperature(double deg_c); - public abstract void set_battery_voltage(double volts); - - public abstract void set_apogee_voltage(double volts); - public abstract void set_main_voltage(double volts); - - public void set_temp_gps() { - temp_gps = null; - } - - public boolean gps_pending() { - return temp_gps != null; - } - - public AltosGPS make_temp_gps(boolean sats) { - if (temp_gps == null) { - temp_gps = new AltosGPS(); - } - if (sats) { - if (tick != temp_gps_sat_tick) - temp_gps.cc_gps_sat = null; - temp_gps_sat_tick = tick; - } - return temp_gps; - } - - public void set_ground_pressure(double ground_pressure) { - if (ground_pressure != AltosLib.MISSING) { - this.ground_pressure = ground_pressure; - this.ground_altitude = AltosConvert.pressure_to_altitude(ground_pressure); - } - } - - public abstract void set_accel_ground(double along, double across, double through); - public abstract void set_gyro_zero(double roll, double pitch, double yaw); - public abstract void check_imu_wrap(AltosIMU imu); - public abstract void set_imu(AltosIMU imu); - public abstract void set_mag(AltosMag mag); - public abstract void set_pyro_voltage(double volts); - public abstract void set_igniter_voltage(double[] voltage); - public abstract void set_pyro_fired(int pyro_mask); - - public void copy(AltosFlightListener old) { - flight = old.flight; - serial = old.serial; - tick = old.tick; - boost_tick = old.boost_tick; - accel_plus_g = old.accel_plus_g; - accel_minus_g = old.accel_minus_g; - ground_pressure = old.ground_pressure; - ground_altitude = old.ground_altitude; - temp_gps = old.temp_gps; - temp_gps_sat_tick = old.temp_gps_sat_tick; - } - - public void init() { - flight = AltosLib.MISSING; - serial = AltosLib.MISSING; - tick = AltosLib.MISSING; - boost_tick = AltosLib.MISSING; - accel_plus_g = AltosLib.MISSING; - accel_minus_g = AltosLib.MISSING; - accel = AltosLib.MISSING; - ground_pressure = AltosLib.MISSING; - ground_altitude = AltosLib.MISSING; - temp_gps = null; - temp_gps_sat_tick = AltosLib.MISSING; - } -} diff --git a/altoslib/AltosFlightSeries.java b/altoslib/AltosFlightSeries.java index 57f1a491..d130d3ad 100644 --- a/altoslib/AltosFlightSeries.java +++ b/altoslib/AltosFlightSeries.java @@ -21,7 +21,7 @@ public class AltosFlightSeries extends AltosDataListener { public ArrayList<AltosTimeSeries> series = new ArrayList<AltosTimeSeries>(); public double speed_filter_width = 4.0; - public double accel_filter_width = 4.0; + public double accel_filter_width = 1.0; public int[] indices() { int[] indices = new int[series.size()]; @@ -150,18 +150,17 @@ public class AltosFlightSeries extends AltosDataListener { public void set_state(int state) { - if (state == AltosLib.ao_flight_pad) - return; - - if (state_series == null) - state_series = add_series(state_name, AltosConvert.state_name); - else if (this.state == state) - return; - this.state = state; - state_series.add(time(), state); + if (state != AltosLib.ao_flight_pad && state != AltosLib.MISSING && state != AltosLib.ao_flight_stateless) { + if (state_series == null) + state_series = add_series(state_name, AltosConvert.state_name); + if (this.state() != state) + state_series.add(time(), state); + } + super.set_state(state); } public AltosTimeSeries accel_series; + public boolean accel_computed; public static final String accel_name = "Accel"; @@ -176,23 +175,61 @@ public class AltosFlightSeries extends AltosDataListener { accel_series = add_series(accel_name, AltosConvert.accel); accel_series.add(time(), acceleration); + accel_computed = false; } - private void compute_accel() { - if (accel_series != null) - return; + private AltosTimeSeries compute_accel() { + AltosTimeSeries new_accel_series = null; if (speed_series != null) { - AltosTimeSeries temp_series = make_series(speed_name, AltosConvert.speed); - speed_series.filter(temp_series, accel_filter_width); - accel_series = add_series(accel_name, AltosConvert.accel); - temp_series.differentiate(accel_series); + AltosTimeSeries temp_series; + if (accel_filter_width > 0) { + temp_series = make_series(speed_name, AltosConvert.speed); + speed_series.filter(temp_series, accel_filter_width); + } else + temp_series = speed_series; + + new_accel_series = make_series(accel_name, AltosConvert.accel); + temp_series.differentiate(new_accel_series); + } + return new_accel_series; + } + + public void set_filter(double speed_filter, double accel_filter) { + this.speed_filter_width = speed_filter; + this.accel_filter_width = accel_filter; + + AltosTimeSeries new_speed_series = compute_speed(); + + if (new_speed_series != null) { + speed_series.erase_values(); + for (AltosTimeValue tv : new_speed_series) + speed_series.add(tv); + } + if (accel_computed) { + AltosTimeSeries new_accel_series = compute_accel(); + if (new_accel_series != null) { + accel_series.erase_values(); + for (AltosTimeValue tv : new_accel_series) + accel_series.add(tv); + } } } public void set_received_time(long received_time) { } + public AltosTimeSeries tick_series; + + public static final String tick_name = "Tick"; + + public void set_tick(int tick) { + super.set_tick(tick); + if (tick_series == null) + tick_series = add_series(tick_name, null); + tick_series.add(time(), tick); + } + public AltosTimeSeries rssi_series; public static final String rssi_name = "RSSI"; @@ -259,21 +296,24 @@ public class AltosFlightSeries extends AltosDataListener { public static final String speed_name = "Speed"; - private void compute_speed() { - if (speed_series != null) - return; - + private AltosTimeSeries compute_speed() { + AltosTimeSeries new_speed_series = null; AltosTimeSeries alt_speed_series = null; AltosTimeSeries accel_speed_series = null; if (altitude_series != null) { - AltosTimeSeries temp_series = make_series(altitude_name, AltosConvert.height); - altitude_series.filter(temp_series, speed_filter_width); + AltosTimeSeries temp_series; + + if (speed_filter_width > 0) { + temp_series = make_series(speed_name, AltosConvert.height); + altitude_series.filter(temp_series, speed_filter_width); + } else + temp_series = altitude_series; alt_speed_series = make_series(speed_name, AltosConvert.speed); temp_series.differentiate(alt_speed_series); } - if (accel_series != null) { + if (accel_series != null && !accel_computed) { if (orient_series != null) { vert_accel_series = add_series(vert_accel_name, AltosConvert.accel); @@ -309,26 +349,25 @@ public class AltosFlightSeries extends AltosDataListener { } } if (apogee_time == AltosLib.MISSING) { - speed_series = alt_speed_series; + new_speed_series = alt_speed_series; } else { - speed_series = make_series(speed_name, AltosConvert.speed); + new_speed_series = make_series(speed_name, AltosConvert.speed); for (AltosTimeValue d : accel_speed_series) { if (d.time <= apogee_time) - speed_series.add(d); + new_speed_series.add(d); } for (AltosTimeValue d : alt_speed_series) { if (d.time > apogee_time) - speed_series.add(d); + new_speed_series.add(d); } } } else if (alt_speed_series != null) { - speed_series = alt_speed_series; + new_speed_series = alt_speed_series; } else if (accel_speed_series != null) { - speed_series = accel_speed_series; + new_speed_series = accel_speed_series; } - if (speed_series != null) - add_series(speed_series); + return new_speed_series; } public AltosTimeSeries orient_series; @@ -450,13 +489,24 @@ public class AltosFlightSeries extends AltosDataListener { public ArrayList<AltosGPSTimeValue> gps_series; public AltosGPS gps_before(double time) { - AltosGPS gps = null; - for (AltosGPSTimeValue gtv : gps_series) - if (gtv.time <= time) - gps = gtv.gps; - else - break; - return gps; + AltosGPSTimeValue nearest = null; + for (AltosGPSTimeValue gtv : gps_series) { + if (nearest == null) + nearest = gtv; + else { + if (gtv.time <= time) { + if (nearest.time <= time && gtv.time > nearest.time) + nearest = gtv; + } else { + if (nearest.time > time && gtv.time < nearest.time) + nearest = gtv; + } + } + } + if (nearest != null) + return nearest.gps; + else + return null; } public AltosTimeSeries sats_in_view; @@ -482,6 +532,7 @@ public class AltosFlightSeries extends AltosDataListener { public static final String gps_hdop_name = "GPS Horizontal Dilution of Precision"; public void set_gps(AltosGPS gps) { + super.set_gps(gps); if (gps_series == null) gps_series = new ArrayList<AltosGPSTimeValue>(); gps_series.add(new AltosGPSTimeValue(time(), gps)); @@ -643,7 +694,7 @@ public class AltosFlightSeries extends AltosDataListener { public void set_igniter_voltage(double[] voltage) { int channels = voltage.length; if (igniter_voltage == null || igniter_voltage.length <= channels) { - AltosTimeSeries[] new_igniter_voltage = new AltosTimeSeries[channels + 1]; + AltosTimeSeries[] new_igniter_voltage = new AltosTimeSeries[channels]; int i = 0; if (igniter_voltage != null) { @@ -681,8 +732,18 @@ public class AltosFlightSeries extends AltosDataListener { public void finish() { compute_orient(); - compute_speed(); - compute_accel(); + if (speed_series == null) { + speed_series = compute_speed(); + if (speed_series != null) + add_series(speed_series); + } + if (accel_series == null) { + accel_series = compute_accel(); + if (accel_series != null) { + add_series(accel_series); + accel_computed = true; + } + } compute_height(); } diff --git a/altoslib/AltosFlightStats.java b/altoslib/AltosFlightStats.java index ea1a9675..6bb83581 100644 --- a/altoslib/AltosFlightStats.java +++ b/altoslib/AltosFlightStats.java @@ -27,6 +27,8 @@ public class AltosFlightStats { public double max_acceleration; public double[] state_speed = new double[AltosLib.ao_flight_invalid + 1]; public double[] state_enter_speed = new double[AltosLib.ao_flight_invalid + 1]; + public double[] state_enter_height = new double[AltosLib.ao_flight_invalid + 1]; + public double[] state_enter_gps_height = new double[AltosLib.ao_flight_invalid + 1]; public double[] state_accel = new double[AltosLib.ao_flight_invalid + 1]; public double[] state_time = new double[AltosLib.ao_flight_invalid + 1]; public String product; @@ -134,6 +136,11 @@ public class AltosFlightStats { if (0 <= state && state <= AltosLib.ao_flight_invalid && delta_time > 0) { if (state_enter_speed[state] == AltosLib.MISSING) state_enter_speed[state] = series.speed_series.value(start_time); + if (state_enter_height[state] == AltosLib.MISSING) + state_enter_height[state] = series.height_series.value(start_time); + if (state_enter_gps_height[state] == AltosLib.MISSING) + if (series.gps_height != null) + state_enter_gps_height[state] = series.gps_height.value(start_time); speeds[state].value += series.speed_series.average(start_time, end_time) * delta_time; speeds[state].time += delta_time; accels[state].value += series.accel_series.average(start_time, end_time) * delta_time; diff --git a/altoslib/AltosIMU.java b/altoslib/AltosIMU.java index dee28a92..ba6f1a82 100644 --- a/altoslib/AltosIMU.java +++ b/altoslib/AltosIMU.java @@ -83,9 +83,9 @@ public class AltosIMU implements Cloneable { listener.set_gyro(cal_data.gyro_roll(imu.gyro_y), cal_data.gyro_pitch(imu.gyro_x), cal_data.gyro_yaw(imu.gyro_z)); - listener.set_accel_ground(cal_data.accel_along(imu.accel_y), - cal_data.accel_across(imu.accel_x), - cal_data.accel_through(imu.accel_z)); + listener.set_accel_ground(imu.accel_y, + imu.accel_x, + imu.accel_z); } } catch (TimeoutException te) { } diff --git a/altoslib/AltosIdleMonitor.java b/altoslib/AltosIdleMonitor.java index fc5d4cc8..834d9aa5 100644 --- a/altoslib/AltosIdleMonitor.java +++ b/altoslib/AltosIdleMonitor.java @@ -33,6 +33,7 @@ public class AltosIdleMonitor extends Thread { double frequency; String callsign; + AltosState state; AltosListenerState listener_state; AltosConfigData config_data; AltosGPS gps; @@ -52,20 +53,23 @@ public class AltosIdleMonitor extends Thread { return link.reply_abort; } - boolean provide_data(AltosDataListener listener) throws InterruptedException, TimeoutException, AltosUnknownProduct { + boolean provide_data() throws InterruptedException, TimeoutException, AltosUnknownProduct { boolean worked = false; boolean aborted = false; try { start_link(); - fetch.provide_data(listener); + link.config_data(); + if (state == null) + state = new AltosState(new AltosCalData(link.config_data())); + fetch.provide_data(state); if (!link.has_error && !link.reply_abort) worked = true; } finally { aborted = stop_link(); if (worked) { if (remote) - listener.set_rssi(link.rssi(), 0); + state.set_rssi(link.rssi(), 0); listener_state.battery = link.monitor_battery(); } } @@ -92,14 +96,11 @@ public class AltosIdleMonitor extends Thread { } public void run() { - AltosState state = null; + state = null; try { for (;;) { try { - link.config_data(); - if (state == null) - state = new AltosState(new AltosCalData(link.config_data())); - provide_data(state); + provide_data(); listener.update(state, listener_state); } catch (TimeoutException te) { } catch (AltosUnknownProduct ae) { diff --git a/altoslib/AltosKML.java b/altoslib/AltosKML.java index 587b845b..d5248a17 100644 --- a/altoslib/AltosKML.java +++ b/altoslib/AltosKML.java @@ -38,16 +38,18 @@ public class AltosKML implements AltosWriter { int flight_state = -1; AltosGPS prev = null; double gps_start_altitude = AltosLib.MISSING; + AltosFlightSeries series; AltosFlightStats stats; + AltosCalData cal_data; static final String[] kml_state_colors = { "FF000000", // startup "FF000000", // idle "FF000000", // pad "FF0000FF", // boost + "FF8040FF", // coast "FF4080FF", // fast - "FF00FFFF", // coast - "FFFF0000", // drogue + "FF00FFFF", // drogue "FF00FF00", // main "FF000000", // landed "FFFFFFFF", // invalid @@ -60,85 +62,169 @@ public class AltosKML implements AltosWriter { return kml_state_colors[state]; } + static final String[] kml_style_colors = { + "FF0000FF", // baro + "FFFF0000", // gps + }; + + static String style_color(int style) { + if (style < 0 || kml_style_colors.length <= style) + return kml_style_colors[0]; + return kml_style_colors[style]; + } + static final String kml_header_start = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n" + "<Document>\n" + " <name>AO Flight#%d S/N: %03d</name>\n" + - " <description>\n"; + " <Snippet maxLines=\"8\">\n"; + static final String kml_header_end = - " </description>\n" + - " <open>0</open>\n"; - - static final String kml_style_start = - " <Style id=\"ao-flightstate-%s\">\n" + - " <LineStyle><color>%s</color><width>4</width></LineStyle>\n" + - " <BalloonStyle>\n" + - " <text>\n"; - - static final String kml_style_end = - " </text>\n" + - " </BalloonStyle>\n" + - " </Style>\n"; - - static final String kml_placemark_start = - " <Placemark>\n" + - " <name>%s</name>\n" + - " <styleUrl>#ao-flightstate-%s</styleUrl>\n" + - " <LineString>\n" + - " <tessellate>1</tessellate>\n" + - " <altitudeMode>absolute</altitudeMode>\n" + - " <coordinates>\n"; + " </Snippet>\n" + + " <open>1</open>\n"; + + static final String kml_folder_start = + " <Folder>\n" + + " <name>%s</name>\n"; + + static final String kml_path_style_start = + " <Style id=\"ao-style-%s\">\n" + + " <LineStyle><color>%s</color><width>8</width></LineStyle>\n" + + " <BalloonStyle>\n" + + " <text>\n"; + + static final String kml_path_style_end = + " </text>\n" + + " </BalloonStyle>\n" + + " </Style>\n"; + + static final String kml_point_style_start = + " <Style id=\"ao-style-%s\">\n" + + " <LabelStyle><color>%s</color></LabelStyle>\n" + + " <IconStyle><color>%s</color></IconStyle>\n" + + " <BalloonStyle>\n" + + " <text>\n"; + + static final String kml_point_style_end = + " </text>\n" + + " </BalloonStyle>\n" + + " </Style>\n"; + + static final String kml_path_start = + " <Placemark>\n" + + " <name>%s</name>\n" + + " <styleUrl>#ao-style-%s</styleUrl>\n" + + " <LineString>\n" + + " <tessellate>1</tessellate>\n" + + " <altitudeMode>absolute</altitudeMode>\n" + + " <coordinates>\n"; static final String kml_coord_fmt = - " %.7f,%.7f,%.7f <!-- alt %12.7f time %12.7f sats %d -->\n"; + " %.7f,%.7f,%.7f <!-- alt %12.7f time %12.7f sats %d -->\n"; - static final String kml_placemark_end = - " </coordinates>\n" + - " </LineString>\n" + - " </Placemark>\n"; + static final String kml_path_end = + " </coordinates>\n" + + " </LineString>\n" + + " </Placemark>\n"; + + static final String kml_point_start = + " <Placemark>\n" + + " <name>%s</name>\n" + + " <styleUrl>#ao-style-%s</styleUrl>\n" + + " <Point>\n" + + " <tessellate>1</tessellate>\n" + + " <altitudeMode>absolute</altitudeMode>\n" + + " <coordinates>\n"; + + static final String kml_point_end = + " </coordinates>\n" + + " </Point>\n" + + " </Placemark>\n"; + + static final String kml_folder_end = + " </Folder>\n"; static final String kml_footer = "</Document>\n" + "</kml>\n"; - void start (AltosCalData cal_data) { + void start () { AltosGPS gps = cal_data.gps_pad; gps_start_altitude = cal_data.gps_pad_altitude; out.printf(kml_header_start, cal_data.flight, cal_data.serial); - out.printf("Date: %04d-%02d-%02d\n", + out.printf("Product: %s\n", stats.product); + out.printf("Firmware: %s\n", stats.firmware_version); + out.printf("Date: %04d-%02d-%02d\n", gps.year, gps.month, gps.day); - out.printf("Time: %2d:%02d:%02d\n", + out.printf("Time: %2d:%02d:%02d\n", gps.hour, gps.minute, gps.second); + if (stats.max_height != AltosLib.MISSING) + out.printf("Max baro height: %s\n", AltosConvert.height.show(6, stats.max_height)); + if (stats.max_gps_height != AltosLib.MISSING) + out.printf("Max GPS Height: %s\n", AltosConvert.height.show(6, stats.max_gps_height)); + if (stats.max_speed != AltosLib.MISSING) + out.printf("Max speed: %s\n", AltosConvert.speed.show(6, stats.max_speed)); + if (stats.max_acceleration != AltosLib.MISSING) + out.printf("Max accel: %s\n", AltosConvert.accel.show(6, stats.max_acceleration)); out.printf("%s", kml_header_end); } - boolean started = false; + void folder_start(String folder_name) { + out.printf(kml_folder_start, folder_name); + } + + void folder_end() { + out.printf(kml_folder_end); + } + + void path_style_start(String style, String color) { + out.printf(kml_path_style_start, style, color); + } + + void path_style_end() { + out.printf(kml_path_style_end); + } - void state_start(int state) { - String state_name = AltosLib.state_name(state); - String state_color = state_color(state); - out.printf(kml_style_start, state_name, state_color); - out.printf("State: %s\n", state_name); - out.printf("Time: %6.2f s\n", stats.state_time[state]); - out.printf("Average speed: %s\n", AltosConvert.speed.show(6, stats.state_speed[state])); - out.printf("Average accel: %s\n", AltosConvert.accel.show(6, stats.state_accel[state])); - out.printf("%s", kml_style_end); - out.printf(kml_placemark_start, state_name, state_name); + void point_style_start(String style, String color) { + out.printf(kml_point_style_start, style, color, color); } - void state_end() { - out.printf("%s", kml_placemark_end); + void point_style_end() { + out.printf(kml_point_style_end); } - void coord(double time, AltosGPS gps, int state, double height) { - double altitude; + void path_start(String name, String style) { + out.printf(kml_path_start, name, style); + } + + void path_end() { + out.printf(kml_path_end); + } + + void point_start(String name, String style) { + out.printf(kml_point_start, name, style); + } + + void point_end() { + out.printf(kml_point_end); + } + + boolean started = false; + + private double baro_altitude(AltosFlightSeries series, double time) { + double height = series.value(AltosFlightSeries.height_name, time); + + if (height == AltosLib.MISSING) + return AltosLib.MISSING; + if (cal_data.gps_pad_altitude == AltosLib.MISSING) + return AltosLib.MISSING; - if (height != AltosLib.MISSING) - altitude = height + gps_start_altitude; - else - altitude = gps.alt; + return height + cal_data.gps_pad_altitude; + } + + void coord(double time, AltosGPS gps, double altitude) { out.printf(kml_coord_fmt, gps.lon, gps.lat, altitude, (double) gps.alt, @@ -150,48 +236,111 @@ public class AltosKML implements AltosWriter { } public void close() { - if (prev != null) { - state_end(); - end(); - prev = null; - } if (out != null) { out.close(); out = null; } } - public void write(AltosGPSTimeValue gtv, AltosCalData cal_data, int state, double height) { - AltosGPS gps = gtv.gps; + public void write(AltosGPS gps, double alt) + { + if (gps == null) + return; if (gps.lat == AltosLib.MISSING) return; if (gps.lon == AltosLib.MISSING) return; - if (state != flight_state) { - flight_state = state; - if (prev != null) { - coord(gtv.time, gps, state, height); - state_end(); - } - state_start(state); + if (alt == AltosLib.MISSING) { + alt = cal_data.gps_pad_altitude; + if (alt == AltosLib.MISSING) + return; } - coord(0, gps, state, height); + coord(0, gps, alt); prev = gps; } - private int state(AltosFlightSeries series, double time) { - return (int) series.value_before(AltosFlightSeries.state_name, time); - } + public void write_point(AltosTimeValue tv, boolean is_gps) { + int state = (int) tv.value; + String style_prefix = is_gps ? "gps-" : "baro-"; + String state_name = AltosLib.state_name(state); + String state_label = AltosLib.state_name_capital(state); + String style_name = style_prefix + state_name; + String folder_name = is_gps ? "GPS" : "Baro"; + String full_name = state_label + " (" + folder_name + ")"; + AltosGPS gps = series.gps_before(tv.time); + double altitude = is_gps ? gps.alt : baro_altitude(series, tv.time); - private double height(AltosFlightSeries series, double time) { - return series.value(AltosFlightSeries.height_name, time); + point_style_start(style_name, state_color(state)); + out.printf("%s\n", full_name); + switch (state) { + case AltosLib.ao_flight_boost: + out.printf("Max accel %s\n", AltosConvert.accel.show(6, stats.max_acceleration)); + out.printf("Max speed %s\n", AltosConvert.speed.show(6, stats.max_speed)); + break; + case AltosLib.ao_flight_coast: + case AltosLib.ao_flight_fast: + out.printf("Entry speed %s\n", AltosConvert.speed.show(6, stats.state_enter_speed[state])); + out.printf("Entry height %s\n", AltosConvert.height.show(6, altitude - cal_data.gps_pad_altitude)); + break; + case AltosLib.ao_flight_drogue: + out.printf("Max height %s\n", AltosConvert.height.show(6, is_gps ? stats.max_gps_height : stats.max_height)); + out.printf("Average descent rate %s\n", AltosConvert.speed.show(6, -stats.state_speed[state])); + break; + case AltosLib.ao_flight_main: + out.printf("Entry speed %s\n", AltosConvert.speed.show(6, -stats.state_enter_speed[state])); + out.printf("Entry height %s\n", AltosConvert.height.show(6, altitude - cal_data.gps_pad_altitude)); + out.printf("Average descent rate %s\n", AltosConvert.speed.show(6, -stats.state_speed[state])); + break; + case AltosLib.ao_flight_landed: + out.printf("Landing speed %s\n", AltosConvert.speed.show(6, -stats.state_enter_speed[state])); + break; + } + point_style_end(); + point_start(full_name, style_name); + gps = series.gps_before(tv.time); + write(gps, altitude); + point_end(); } public void write(AltosFlightSeries series) { + this.series = series; + series.finish(); stats = new AltosFlightStats(series); - start(series.cal_data()); + cal_data = series.cal_data(); + start(); + if (series.height_series != null) { + folder_start("Barometric Altitude"); + path_style_start("baro", style_color(0)); + out.printf("Barometric Altitude\n"); + out.printf("Max height: %s\n", AltosConvert.height.show(6, stats.max_height)); + path_style_end(); + path_start("Barometric Altitude", "baro"); + for (AltosGPSTimeValue gtv : series.gps_series) + write(gtv.gps, baro_altitude(series, gtv.time)); + path_end(); + if (series.state_series != null) { + for (AltosTimeValue tv : series.state_series) { + write_point(tv, false); + } + } + folder_end(); + } + folder_start("GPS Altitude"); + path_style_start("gps", style_color(1)); + out.printf("GPS Altitude"); + out.printf("Max height: %s\n", AltosConvert.height.show(6, stats.max_gps_height)); + path_style_end(); + path_start("GPS Altitude", "gps"); for (AltosGPSTimeValue gtv : series.gps_series) - write(gtv, series.cal_data(), state(series, gtv.time), height(series, gtv.time)); + write(gtv.gps, gtv.gps.alt); + path_end(); + if (series.state_series != null) { + for (AltosTimeValue tv : series.state_series) { + write_point(tv, true); + } + } + folder_end(); + end(); } public AltosKML(File in_name) throws FileNotFoundException { diff --git a/altoslib/AltosLib.java b/altoslib/AltosLib.java index d1063509..c25a6273 100644 --- a/altoslib/AltosLib.java +++ b/altoslib/AltosLib.java @@ -363,6 +363,7 @@ public class AltosLib { public static final int AO_LOG_FORMAT_TELEMINI3 = 12; public static final int AO_LOG_FORMAT_TELEFIRETWO = 13; public static final int AO_LOG_FORMAT_EASYMINI2 = 14; + public static final int AO_LOG_FORMAT_TELEMEGA_3 = 15; public static final int AO_LOG_FORMAT_NONE = 127; public static boolean isspace(int c) { @@ -587,7 +588,11 @@ public class AltosLib { } public static String igniter_name(int i) { - return String.format("Ignitor %c", 'A' + i); + return String.format("Igniter %c", 'A' + i); + } + + public static String igniter_short_name(int i) { + return String.format("igniter_%c", 'a' + i); } public static AltosRecordSet record_set(File file) throws FileNotFoundException, IOException { diff --git a/altoslib/AltosLink.java b/altoslib/AltosLink.java index 5413de9d..829a1a63 100644 --- a/altoslib/AltosLink.java +++ b/altoslib/AltosLink.java @@ -355,7 +355,8 @@ public abstract class AltosLink implements Runnable { public int telemetry_rate = -1; public double frequency; public String callsign; - AltosConfigData config_data; + private AltosConfigData config_data_local; + private AltosConfigData config_data_remote; private Object config_data_lock = new Object(); @@ -390,7 +391,7 @@ public abstract class AltosLink implements Runnable { public void set_radio_frequency(double in_frequency) throws InterruptedException, TimeoutException { frequency = in_frequency; - config_data(); + AltosConfigData config_data = config_data(); set_radio_frequency(frequency, config_data.radio_frequency > 0, config_data.radio_setting > 0, @@ -446,11 +447,24 @@ public abstract class AltosLink implements Runnable { public AltosConfigData config_data() throws InterruptedException, TimeoutException { synchronized(config_data_lock) { - if (config_data == null) { - printf("m 0\n"); - config_data = new AltosConfigData(this); - if (monitor_mode) - set_monitor(true); + AltosConfigData config_data; + + if (remote) { + if (config_data_remote == null) { + printf("m 0\n"); + config_data_remote = new AltosConfigData(this); + if (monitor_mode) + set_monitor(true); + } + config_data = config_data_remote; + } else { + if (config_data_local == null) { + printf("m 0\n"); + config_data_local = new AltosConfigData(this); + if (monitor_mode) + set_monitor(true); + } + config_data = config_data_local; } return config_data; } @@ -551,14 +565,23 @@ public abstract class AltosLink implements Runnable { } public boolean has_monitor_battery() { - return config_data.has_monitor_battery(); + try { + return config_data().has_monitor_battery(); + } catch (InterruptedException ie) { + return false; + } catch (TimeoutException te) { + return false; + } } public double monitor_battery() throws InterruptedException { - int monitor_batt = AltosLib.MISSING; + double volts = AltosLib.MISSING; - if (config_data.has_monitor_battery()) { - try { + try { + AltosConfigData config_data = config_data(); + int monitor_batt = AltosLib.MISSING; + + if (config_data.has_monitor_battery()) { String[] items = adc(); for (int i = 0; i < items.length;) { if (items[i].equals("batt")) { @@ -568,19 +591,17 @@ public abstract class AltosLink implements Runnable { } i++; } - } catch (TimeoutException te) { } - } - if (monitor_batt == AltosLib.MISSING) - return AltosLib.MISSING; + if (monitor_batt != AltosLib.MISSING) { + if (config_data.product.startsWith("TeleBT-v3") || config_data.product.startsWith("TeleBT-v4")) { + volts = AltosConvert.tele_bt_3_battery(monitor_batt); + } else { + volts = AltosConvert.cc_battery_to_voltage(monitor_batt); + } + } - double volts = AltosLib.MISSING; - if (config_data.product.startsWith("TeleBT-v3") || config_data.product.startsWith("TeleBT-v4")) { - volts = AltosConvert.tele_bt_3_battery(monitor_batt); - } else { - volts = AltosConvert.cc_battery_to_voltage(monitor_batt); + } catch (TimeoutException te) { } - return volts; } diff --git a/altoslib/AltosReplayReader.java b/altoslib/AltosReplayReader.java index 24b425b7..fab28cac 100644 --- a/altoslib/AltosReplayReader.java +++ b/altoslib/AltosReplayReader.java @@ -31,7 +31,7 @@ class AltosReplay extends AltosDataListener implements Runnable { AltosState state; AltosRecordSet record_set; double last_time = AltosLib.MISSING; - Semaphore semaphore = new Semaphore(1);; + Semaphore semaphore = new Semaphore(1); boolean done = false; public void set_time(double time) { @@ -70,7 +70,7 @@ class AltosReplay extends AltosDataListener implements Runnable { public void set_apogee_voltage(double volts) { state.set_apogee_voltage(volts); } public void set_main_voltage(double volts) { state.set_main_voltage(volts); } - public void set_gps(AltosGPS gps) { state.set_gps(gps); } + public void set_gps(AltosGPS gps) { super.set_gps(gps); state.set_gps(gps); } public void set_orient(double orient) { state.set_orient(orient); } public void set_gyro(double roll, double pitch, double yaw) { state.set_gyro(roll, pitch, yaw); } diff --git a/altoslib/AltosState.java b/altoslib/AltosState.java index 39ab10da..68097faf 100644 --- a/altoslib/AltosState.java +++ b/altoslib/AltosState.java @@ -480,7 +480,7 @@ public class AltosState extends AltosDataListener { class AltosPressure extends AltosValue { void set(double p, double time) { super.set(p, time); - if (state == AltosLib.ao_flight_pad) + if (state() == AltosLib.ao_flight_pad) ground_pressure.set_filtered(p, time); double a = pressure_to_altitude(p); altitude.set_computed(a, time); @@ -557,7 +557,7 @@ public class AltosState extends AltosDataListener { class AltosSpeed extends AltosCValue { boolean can_max() { - return state < AltosLib.ao_flight_fast || state == AltosLib.ao_flight_stateless; + return state() < AltosLib.ao_flight_fast || state() == AltosLib.ao_flight_stateless; } void set_accel() { @@ -615,7 +615,7 @@ public class AltosState extends AltosDataListener { class AltosAccel extends AltosCValue { boolean can_max() { - return state < AltosLib.ao_flight_fast || state == AltosLib.ao_flight_stateless; + return state() < AltosLib.ao_flight_fast || state() == AltosLib.ao_flight_stateless; } void set_measured(double a, double time) { @@ -712,11 +712,11 @@ public class AltosState extends AltosDataListener { } public void init() { + super.init(); + set = 0; received_time = System.currentTimeMillis(); - time = AltosLib.MISSING; - state = AltosLib.ao_flight_invalid; landed = false; boost = false; rssi = AltosLib.MISSING; @@ -819,9 +819,9 @@ public class AltosState extends AltosDataListener { if (gps.locked && gps.nsat >= 4) { /* Track consecutive 'good' gps reports, waiting for 10 of them */ - if (state == AltosLib.ao_flight_pad || state == AltosLib.ao_flight_stateless) { + if (state() == AltosLib.ao_flight_pad || state() == AltosLib.ao_flight_stateless) { set_npad(npad+1); - if (pad_lat != AltosLib.MISSING && (npad < 10 || state == AltosLib.ao_flight_pad)) { + if (pad_lat != AltosLib.MISSING && (npad < 10 || state() == AltosLib.ao_flight_pad)) { pad_lat = (pad_lat * 31 + gps.lat) / 32; pad_lon = (pad_lon * 31 + gps.lon) / 32; gps_ground_altitude.set_filtered(gps.alt, time); @@ -859,24 +859,14 @@ public class AltosState extends AltosDataListener { } public String state_name() { - return AltosLib.state_name(state); + return AltosLib.state_name(state()); } public void set_state(int state) { - if (state != AltosLib.ao_flight_invalid) { - this.state = state; - ascent = (AltosLib.ao_flight_boost <= state && - state <= AltosLib.ao_flight_coast); - boost = (AltosLib.ao_flight_boost == state); - } - } - - public int state() { - return state; - } - - private void re_init() { - init(); + super.set_state(state); + ascent = (AltosLib.ao_flight_boost <= state() && + state() <= AltosLib.ao_flight_coast); + boost = (AltosLib.ao_flight_boost == state()); } public int rssi() { @@ -897,6 +887,7 @@ public class AltosState extends AltosDataListener { } public void set_gps(AltosGPS gps) { + super.set_gps(gps); if (gps != null) { this.gps = gps; update_gps(); diff --git a/altoslib/AltosTelemetry.java b/altoslib/AltosTelemetry.java index f17e1171..a374519d 100644 --- a/altoslib/AltosTelemetry.java +++ b/altoslib/AltosTelemetry.java @@ -28,8 +28,11 @@ public abstract class AltosTelemetry implements AltosDataProvider { int[] bytes; /* All telemetry packets have these fields */ - public int rssi() { return AltosConvert.telem_to_rssi(AltosLib.int8(bytes, bytes.length - 3)); } - public int status() { return AltosLib.uint8(bytes, bytes.length - 2); } + static public int rssi(int[] bytes) { return AltosConvert.telem_to_rssi(AltosLib.int8(bytes, bytes.length - 3)); } + static public int status(int[] bytes) { return AltosLib.uint8(bytes, bytes.length - 2); } + + public int rssi() { return rssi(bytes); } + public int status() { return status(bytes); } /* All telemetry packets report these fields in some form */ public abstract int serial(); @@ -51,7 +54,7 @@ public abstract class AltosTelemetry implements AltosDataProvider { public void provide_data(AltosDataListener listener) { listener.set_serial(serial()); - if (listener.state == AltosLib.ao_flight_invalid) + if (listener.state() == AltosLib.ao_flight_invalid) listener.set_state(AltosLib.ao_flight_startup); if (frequency != AltosLib.MISSING) listener.set_frequency(frequency); @@ -96,6 +99,9 @@ public abstract class AltosTelemetry implements AltosDataProvider { if (!cksum(bytes)) throw new ParseException(String.format("invalid line \"%s\"", hex), 0); + if ((status(bytes) & PKT_APPEND_STATUS_1_CRC_OK) == 0) + throw new AltosCRCException(rssi(bytes)); + /* length, data ..., rssi, status, checksum -- 4 bytes extra */ switch (bytes.length) { case AltosLib.ao_telemetry_standard_len + 4: diff --git a/altoslib/AltosTelemetryConfiguration.java b/altoslib/AltosTelemetryConfiguration.java index ea307442..c8026a83 100644 --- a/altoslib/AltosTelemetryConfiguration.java +++ b/altoslib/AltosTelemetryConfiguration.java @@ -40,7 +40,7 @@ public class AltosTelemetryConfiguration extends AltosTelemetryStandard { AltosCalData cal_data = listener.cal_data(); - cal_data.set_device_type(device_type()); + listener.set_device_type(device_type()); cal_data.set_flight(flight()); cal_data.set_config(config_major(), config_minor(), flight_log_max()); if (device_type() == AltosLib.product_telegps) diff --git a/altoslib/AltosTelemetryFile.java b/altoslib/AltosTelemetryFile.java index 135b0284..e51455f8 100644 --- a/altoslib/AltosTelemetryFile.java +++ b/altoslib/AltosTelemetryFile.java @@ -128,7 +128,7 @@ public class AltosTelemetryFile implements AltosRecordSet { /* Try to pick up at least one pre-boost value */ if (cal_data.time() >= -2) telem.provide_data(listener); - if (listener.state == AltosLib.ao_flight_landed) + if (listener.state() == AltosLib.ao_flight_landed) break; } listener.finish(); diff --git a/altoslib/AltosTelemetryLocation.java b/altoslib/AltosTelemetryLocation.java index f4366e33..e2925a58 100644 --- a/altoslib/AltosTelemetryLocation.java +++ b/altoslib/AltosTelemetryLocation.java @@ -54,7 +54,7 @@ public class AltosTelemetryLocation extends AltosTelemetryStandard { AltosCalData cal_data = listener.cal_data(); - AltosGPS gps = cal_data.make_temp_gps(tick(), false); + AltosGPS gps = listener.make_temp_gps(false); int flags = flags(); gps.nsat = flags & 0xf; @@ -77,12 +77,7 @@ public class AltosTelemetryLocation extends AltosTelemetryStandard { gps.ground_speed = ground_speed() * 1.0e-2; gps.course = course() * 2; gps.climb_rate = climb_rate() * 1.0e-2; - - if (gps.nsat >= 4) - cal_data.set_gps(gps); } listener.set_gps(gps); - cal_data.set_gps(gps); - cal_data.reset_temp_gps(); } } diff --git a/altoslib/AltosTelemetryMegaData.java b/altoslib/AltosTelemetryMegaData.java index 7ef9c637..f5961c8c 100644 --- a/altoslib/AltosTelemetryMegaData.java +++ b/altoslib/AltosTelemetryMegaData.java @@ -24,7 +24,9 @@ public class AltosTelemetryMegaData extends AltosTelemetryStandard { int v_batt() { return int16(6); } int v_pyro() { return int16(8); } - int sense(int i) { int v = uint8(10+i); return v << 4 | v >> 8; } + + /* pyro sense values are sent in 8 bits, expand to 12 bits */ + int sense(int i) { int v = uint8(10+i); return (v << 4) | (v >> 4); } int ground_pres() { return int32(16); } int ground_accel() { return int16(20); } diff --git a/altoslib/AltosTelemetryReader.java b/altoslib/AltosTelemetryReader.java index 26fe4f26..8fb61a4f 100644 --- a/altoslib/AltosTelemetryReader.java +++ b/altoslib/AltosTelemetryReader.java @@ -41,19 +41,15 @@ public class AltosTelemetryReader extends AltosFlightReader { throw new IOException("IO error"); } while (!link.get_monitor()); AltosTelemetry telem = AltosTelemetry.parse(l.line); - if (state == null) { - System.out.printf("Make state\n"); + if (state == null) state = new AltosState(cal_data()); - } telem.provide_data(state); return state; } public AltosCalData cal_data() { - if (cal_data == null) { - System.out.printf("Make cal data\n"); + if (cal_data == null) cal_data = new AltosCalData(); - } return cal_data; } diff --git a/altoslib/AltosTelemetrySatellite.java b/altoslib/AltosTelemetrySatellite.java index 60bc4a51..0965df9f 100644 --- a/altoslib/AltosTelemetrySatellite.java +++ b/altoslib/AltosTelemetrySatellite.java @@ -49,10 +49,9 @@ public class AltosTelemetrySatellite extends AltosTelemetryStandard { AltosCalData cal_data = listener.cal_data(); - AltosGPS gps = cal_data.make_temp_gps(tick(), true); + AltosGPS gps = listener.make_temp_gps(true); gps.cc_gps_sat = sats(); listener.set_gps(gps); - cal_data.reset_temp_gps(); } } diff --git a/altoslib/AltosTimeSeries.java b/altoslib/AltosTimeSeries.java index 9f3b4d80..c6a780a3 100644 --- a/altoslib/AltosTimeSeries.java +++ b/altoslib/AltosTimeSeries.java @@ -20,15 +20,30 @@ public class AltosTimeSeries implements Iterable<AltosTimeValue>, Comparable<Alt public String label; public AltosUnits units; ArrayList<AltosTimeValue> values; + boolean data_changed; public int compareTo(AltosTimeSeries other) { return label.compareTo(other.label); } public void add(AltosTimeValue tv) { + data_changed = true; values.add(tv); } + public void erase_values() { + data_changed = true; + this.values = new ArrayList<AltosTimeValue>(); + } + + public void clear_changed() { + data_changed = false; + } + +// public boolean changed() { +// return data_changed; +// } + public void add(double time, double value) { add(new AltosTimeValue(time, value)); } @@ -264,14 +279,35 @@ public class AltosTimeSeries implements Iterable<AltosTimeValue>, Comparable<Alt } - private double filter_coeff(double dist, double width) { - double ratio = dist / (width / 2); + private static double i0(double x) { + double ds = 1, d = 0, s = 0; - return Math.cos(ratio * Math.PI / 2); + do { + d += 2; + ds = ds * (x * x) / (d * d); + s += ds; + } while (ds - 0.2e-8 * s > 0); + return s; + } + + private static double kaiser(double n, double m, double beta) { + double alpha = m / 2; + double t = (n - alpha) / alpha; + + if (t > 1 || t < -1) + t = 1; + double k = i0 (beta * Math.sqrt (1 - t*t)) / i0(beta); + return k; + } + + private double filter_coeff(double dist, double width) { + return kaiser(dist + width/2.0, width, 2 * Math.PI); } public AltosTimeSeries filter(AltosTimeSeries f, double width) { + double half_width = width/2; + int half_point = values.size() / 2; for (int i = 0; i < values.size(); i++) { double center_time = values.get(i).time; double left_time = center_time - half_width; diff --git a/altoslib/Makefile.am b/altoslib/Makefile.am index 08af9496..2a1cb8e4 100644 --- a/altoslib/Makefile.am +++ b/altoslib/Makefile.am @@ -55,6 +55,7 @@ altoslib_JAVA = \ AltosEepromList.java \ AltosEepromLog.java \ AltosFile.java \ + AltosFilterListener.java \ AltosFlash.java \ AltosFlashListener.java \ AltosDataListener.java \ |