Stats are really easy with all of the data in memory.
Replay takes a special thread to run the data and dump it into a
single state.
Signed-off-by: Keith Packard <keithp@keithp.com>
ground_yaw() / 512.0);
break;
case AltosLib.AO_LOG_STATE:
- System.out.printf("log state %s\n", AltosLib.state_name(state()));
listener.set_state(state());
break;
case AltosLib.AO_LOG_SENSOR:
public void save_telemetry_rate() { }
- public void update(AltosState state) throws InterruptedException { }
-
public boolean supports_telemetry(int telemetry) { return false; }
public boolean supports_telemetry_rate(int telemetry_rate) { return false; }
state_series = add_series(state_name, AltosConvert.state_name);
else if (this.state == state)
return;
- System.out.printf("state %s\n", AltosLib.state_name(state));
this.state = state;
state_series.add(time(), state);
}
public void set_acceleration(double acceleration) {
if (accel_series == null) {
- System.out.printf("set acceleration %g\n", acceleration);
accel_series = add_series(accel_name, AltosConvert.accel);
}
accel_series.add(time(), acceleration);
public static final String speed_name = "Speed";
private void compute_speed() {
- if (speed_series != null) {
- System.out.printf("speed series already made\n");
+ if (speed_series != null)
return;
- }
AltosTimeSeries alt_speed_series = null;
AltosTimeSeries accel_speed_series = null;
alt_speed_series = make_series(speed_name, AltosConvert.speed);
temp_series.filter(alt_speed_series, 10.0);
- } else {
- System.out.printf("no altitude series\n");
}
if (accel_series != null) {
AltosTimeSeries temp_series = make_series(speed_name, AltosConvert.speed);
accel_speed_series = make_series(speed_name, AltosConvert.speed);
temp_series.filter(accel_speed_series, 0.1);
- } else {
- System.out.printf("no accel series\n");
}
if (alt_speed_series != null && accel_speed_series != null) {
} else if (accel_speed_series != null) {
speed_series = accel_speed_series;
}
- if (speed_series != null) {
+ if (speed_series != null)
add_series(speed_series);
- System.out.printf("speed series for %s set to %s\n", this.toString(), speed_series.toString());
- } else
- System.out.printf("didn't manage to make speed series\n");
}
AltosTimeSeries kalman_height_series, kalman_speed_series, kalman_accel_series;
}
public void fill_in() {
- System.out.printf("fill in %s\n", this.toString());
compute_speed();
compute_accel();
if (cal_data.ground_altitude != AltosLib.MISSING)
public AltosFlightSeries(AltosCalData cal_data) {
super(cal_data);
- System.out.printf("new flight series %s\n", this.toString());
init();
}
}
double end_time = 0;
double landed_time = landed_time(series);
- System.out.printf("flight stats %s\n", series.toString());
-
year = month = day = AltosLib.MISSING;
hour = minute = second = AltosLib.MISSING;
serial = flight = AltosLib.MISSING;
for (int s = AltosLib.ao_flight_startup; s <= AltosLib.ao_flight_landed; s++) {
state_count[s] = 0;
- state_speed[s] = 0.0;
- state_accel[s] = 0.0;
+
+ if (s == AltosLib.ao_flight_boost)
+ state_start[s] = boost_time;
+ else
+ state_start[s] = series.state_series.time_of(s);
+ if (s == AltosLib.ao_flight_landed)
+ state_end[s] = landed_time;
+ else
+ state_end[s] = series.state_series.time_of(s+1);
+
+ if (series.speed_series != null)
+ state_speed[s] = series.speed_series.average(state_start[s], state_end[s]);
+
+ if (series.accel_series != null)
+ state_accel[s] = series.accel_series.average(state_start[s], state_end[s]);
}
serial = cal_data.serial;
has_rssi = series.rssi_series != null;
has_flight_data = series.pressure_series != null;
- if (series.gps_series != null) {
- AltosGPS gps = series.gps_series.get(0).gps;
+ AltosGPS gps = series.cal_data.gps_pad;
+ if (gps != null) {
year = gps.year;
month = gps.month;
day = gps.day;
hour = gps.hour;
minute = gps.minute;
second = gps.second;
+ has_gps = true;
+ lat = pad_lat = gps.lat;
+ lon = pad_lon = gps.lon;
+ for (AltosGPSTimeValue gtv : series.gps_series) {
+ gps = gtv.gps;
+ if (gps.locked && gps.nsat >= 4) {
+ lat = gps.lat;
+ lon = gps.lon;
+ }
+ }
+
}
max_height = AltosLib.MISSING;
if (series.height_series != null)
max_height = series.height_series.max();
max_speed = AltosLib.MISSING;
- if (series.speed_series != null)
- max_speed = series.speed_series.max();
- else
- System.out.printf("missing speed series\n");
+ if (series.speed_series != null) {
+ max_speed = series.speed_series.max(state_start[AltosLib.ao_flight_boost], state_start[AltosLib.ao_flight_drogue]);
+ if (max_speed == AltosLib.MISSING)
+ max_speed = series.speed_series.max();
+ }
max_acceleration = AltosLib.MISSING;
- if (series.accel_series != null)
- max_acceleration = series.accel_series.max();
+ if (series.accel_series != null) {
+ max_acceleration = series.accel_series.max(state_start[AltosLib.ao_flight_boost], state_start[AltosLib.ao_flight_drogue]);
+ if (max_acceleration == AltosLib.MISSING)
+ max_acceleration = series.accel_series.max();
+ }
max_gps_height = AltosLib.MISSING;
if (series.gps_height != null)
max_gps_height = series.gps_height.max();
-/*
- for (AltosState state : states) {
- end_time = state.time;
-
- int state_id = state.state();
- if (boost_time != AltosLib.MISSING && state.time >= boost_time && state_id < AltosLib.ao_flight_boost) {
- state_id = AltosLib.ao_flight_boost;
- }
- if (landed_time != AltosLib.MISSING && state.time >= landed_time && state_id < AltosLib.ao_flight_landed) {
- state_id = AltosLib.ao_flight_landed;
- }
-
- if (0 <= state_id && state_id < AltosLib.ao_flight_invalid) {
- double acceleration = state.acceleration();
- double speed = state.speed();
- if (acceleration != AltosLib.MISSING && speed != AltosLib.MISSING) {
- state_accel[state_id] += acceleration;
- state_speed[state_id] += speed;
- state_count[state_id]++;
- }
- if (state_start[state_id] == 0.0)
- state_start[state_id] = state.time;
- if (state_end[state_id] < state.time)
- state_end[state_id] = state.time;
- }
- if (state.pad_lat != AltosLib.MISSING) {
- pad_lat = state.pad_lat;
- pad_lon = state.pad_lon;
- }
- if (state.gps != null && state.gps.locked && state.gps.nsat >= 4) {
- lat = state.gps.lat;
- lon = state.gps.lon;
- has_gps = true;
- if (state.gps.cc_gps_sat != null)
- has_gps_sats = true;
- if (state.gps.course != AltosLib.MISSING)
- has_gps_detail = true;
- }
- if (state.imu != null)
- has_imu = true;
- if (state.mag != null)
- has_mag = true;
- if (state.orient() != AltosLib.MISSING)
- has_orient = true;
- if (state.igniter_voltage != null && state.igniter_voltage.length > num_igniter)
- num_igniter = state.igniter_voltage.length;
- }
-*/
- for (int s = AltosLib.ao_flight_startup; s <= AltosLib.ao_flight_landed; s++) {
- if (state_count[s] > 0) {
- state_speed[s] /= state_count[s];
- state_accel[s] /= state_count[s];
- } else {
- state_speed[s] = AltosLib.MISSING;
- state_accel[s] = AltosLib.MISSING;
- }
- if (state_start[s] == 0)
- state_start[s] = end_time;
- if (state_end[s] == 0)
- state_end[s] = end_time;
- }
}
}
import java.io.*;
import java.util.*;
+import java.util.concurrent.*;
/*
* Open an existing telemetry file and replay it in realtime
*/
+class AltosReplay extends AltosDataListener implements Runnable {
+
+ AltosState state;
+ AltosRecordSet record_set;
+ double last_time = AltosLib.MISSING;
+ Semaphore semaphore = new Semaphore(1);;
+ boolean done = false;
+
+ public void set_time(double time) {
+ if (last_time != AltosLib.MISSING) {
+ semaphore.release();
+ double delay = Math.min(time - last_time,10);
+ if (delay > 0) {
+ try {
+ Thread.sleep((int) (delay * 1000));
+ } catch (InterruptedException ie) {
+ }
+ }
+ }
+ last_time = time;
+ super.set_time(time);
+ state.set_time(time);
+ }
+
+ public void set_state(int state) {
+ super.set_state(state);
+ this.state.set_state(state);
+ }
+
+ public void set_rssi(int rssi, int status) { state.set_rssi(rssi, status); }
+ public void set_received_time(long received_time) { }
+
+ public void set_acceleration(double accel) { state.set_acceleration(accel); }
+ public void set_pressure(double pa) { state.set_pressure(pa); }
+ public void set_thrust(double N) { state.set_thrust(N); }
+
+ public void set_kalman(double height, double speed, double accel) { state.set_kalman(height, speed, accel); }
+
+ public void set_temperature(double deg_c) { state.set_temperature(deg_c); }
+ public void set_battery_voltage(double volts) { state.set_battery_voltage(volts); }
+
+ 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_orient(double orient) { state.set_orient(orient); }
+ public void set_gyro(double roll, double pitch, double yaw) { state.set_gyro(roll, pitch, yaw); }
+ public void set_accel_ground(double along, double across, double through) { state.set_accel_ground(along, across, through); }
+ public void set_accel(double along, double across, double through) { state.set_accel(along, across, through); }
+ public void set_mag(double along, double across, double through) { state.set_mag(along, across, through); }
+ public void set_pyro_voltage(double volts) { state.set_pyro_voltage(volts); }
+ public void set_igniter_voltage(double[] voltage) { state.set_igniter_voltage(voltage); }
+ public void set_pyro_fired(int pyro_mask) { state.set_pyro_fired(pyro_mask); }
+ public void set_companion(AltosCompanion companion) { state.set_companion(companion); }
+
+ public void run () {
+ System.out.printf("ReplayReader running\n");
+ state = new AltosState(record_set.cal_data());
+
+ /* Tell the display that we're in pad mode */
+ state.set_state(AltosLib.ao_flight_pad);
+ semaphore.release();
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ie) {
+ }
+
+ /* Run the flight */
+ record_set.capture_series(this);
+
+ /* All done, signal that it's over */
+ done = true;
+ semaphore.release();
+ }
+
+ public AltosReplay(AltosRecordSet record_set) {
+ super(record_set.cal_data());
+ try {
+ semaphore.acquire();
+ } catch (InterruptedException ie) { }
+ this.record_set = record_set;
+ Thread t = new Thread(this);
+ t.start();
+ }
+}
+
public class AltosReplayReader extends AltosFlightReader {
- Iterator<AltosState> iterator;
- File file;
+ File file;
+ AltosReplay replay;
public AltosState read() {
- if (iterator.hasNext())
- return iterator.next();
- return null;
+
+ /* When done, let the display know */
+ if (replay.done)
+ return null;
+
+ /* Wait for something to change */
+ try {
+ replay.semaphore.acquire();
+ } catch (InterruptedException ie) {
+ }
+
+ /* Fake out the received time */
+ replay.state.set_received_time(System.currentTimeMillis());
+ return replay.state;
}
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 && state.time_change > 0)
- Thread.sleep((int) (Math.min(state.time_change,10) * 1000));
- state.set_received_time(System.currentTimeMillis());
}
public File backing_file() { return file; }
- public AltosReplayReader(Iterator<AltosState> in_iterator, File in_file) {
- iterator = in_iterator;
+ public AltosReplayReader(AltosRecordSet record_set, File in_file) {
file = in_file;
name = file.getName();
+ replay = new AltosReplay(record_set);
}
}
if (l.cal_data_complete())
break;
}
- System.out.printf("Telemetry boost tick %d\n", cal_data.boost_tick);
}
return cal_data;
}
return values.get(after).value;
}
+ public double time_of(double value) {
+ double last = AltosLib.MISSING;
+ for (AltosTimeValue v : values) {
+ if (v.value >= value)
+ return v.time;
+ last = v.time;
+ }
+ return last;
+ }
+
public int size() {
return values.size();
}
return max;
}
+ public double max(double start_time, double end_time) {
+ double max = AltosLib.MISSING;
+ for (AltosTimeValue tv : values) {
+ if (start_time <= tv.time && tv.time <= end_time)
+ if (max == AltosLib.MISSING || tv.value > max)
+ max = tv.value;
+ }
+ return max;
+ }
+
public double min() {
double min = AltosLib.MISSING;
for (AltosTimeValue tv : values) {
return min;
}
+ public double min(double start_time, double end_time) {
+ double min = AltosLib.MISSING;
+ for (AltosTimeValue tv : values) {
+ if (start_time <= tv.time && tv.time <= end_time)
+ if (min == AltosLib.MISSING || tv.value < min)
+ min = tv.value;
+ }
+ return min;
+ }
+
+ public double average() {
+ double total = 0;
+ int count = 0;
+ for (AltosTimeValue tv : values) {
+ total += tv.value;
+ count++;
+ }
+ if (count == 0)
+ return AltosLib.MISSING;
+ return total / count;
+ }
+
+ public double average(double start_time, double end_time) {
+ double total = 0;
+ int count = 0;
+ for (AltosTimeValue tv : values) {
+ if (start_time <= tv.time && tv.time <= end_time) {
+ total += tv.value;
+ count++;
+ }
+ }
+ if (count == 0)
+ return AltosLib.MISSING;
+ return total / count;
+ }
+
public AltosTimeSeries integrate(AltosTimeSeries integral) {
double value = 0.0;
double pvalue = 0.0;
JComponent tab = which_tab(state);
if (tab != cur_tab) {
+ System.out.printf("checking tab for state %s\n", AltosLib.state_name(state.state()));
if (cur_tab == pane.getSelectedComponent()) {
+ System.out.printf("switch tabs\n");
pane.setSelectedComponent(tab);
}
cur_tab = tab;
AltosRecordSet set = record_set(file);
if (set == null)
return null;
-// return new AltosReplayReader(states.iterator(), file);
- return null;
+ return new AltosReplayReader(set, file);
}
static boolean process_replay(File file) {
IdleThread idle_thread;
AltosVoice voice;
AltosFlightReader reader;
- AltosState old_state, state;
+ AltosState state;
+ int old_state = AltosLib.ao_flight_invalid;
+ boolean old_gps_ready = false;
AltosListenerState listener_state;
AltosFlightDisplay display;
}
public synchronized void notice(boolean spoken) {
- if (old_state != null && old_state.state() != state.state()) {
+ if (old_state != state.state()) {
report_time = now();
this.notify();
} else if (spoken)
synchronized boolean tell() {
boolean ret = false;
- if (old_state == null || old_state.state() != state.state()) {
+ if (old_state != state.state()) {
if (state.state() != AltosLib.ao_flight_stateless)
voice.speak(state.state_name());
- if ((old_state == null || old_state.state() <= AltosLib.ao_flight_boost) &&
+ if ((old_state == AltosLib.ao_flight_invalid || old_state <= AltosLib.ao_flight_boost) &&
state.state() > AltosLib.ao_flight_boost) {
if (state.max_speed() != AltosLib.MISSING)
voice.speak("max speed: %s.",
AltosConvert.speed.say_units(state.max_speed() + 0.5));
ret = true;
- } else if ((old_state == null || old_state.state() < AltosLib.ao_flight_drogue) &&
+ } else if ((old_state == AltosLib.ao_flight_invalid || old_state < AltosLib.ao_flight_drogue) &&
state.state() >= AltosLib.ao_flight_drogue) {
if (state.max_height() != AltosLib.MISSING)
voice.speak("max height: %s.",
ret = true;
}
}
- if (old_state == null || old_state.gps_ready != state.gps_ready) {
+ if (old_gps_ready != state.gps_ready) {
if (state.gps_ready) {
voice.speak("GPS ready");
ret = true;
}
- else if (old_state != null) {
+ else if (old_gps_ready) {
voice.speak("GPS lost");
ret = true;
}
}
- old_state = state;
+ old_state = state.state();
+ old_gps_ready = state.gps_ready;
return ret;
}
try {
for (;;) {
try {
- AltosState new_state = reader.read();
- if (new_state == null) {
- state = null;
+ state = reader.read();
+ if (state == null) {
listener_state.running = false;
break;
}
- reader.update(new_state);
- state = new_state;
show_safely();
told = tell();
idle_thread.notice(told);