altosdroid: Remove a debug line in AltosVoice
[fw/altos] / altosdroid / src / org / altusmetrum / AltosDroid / AltosVoice.java
index f17cb82171e2f54e34eca4cd799677a95bdc1d2c..adf52dd9ab8f7907bf18fb95ee845ab31079a927 100644 (file)
@@ -20,183 +20,305 @@ package org.altusmetrum.AltosDroid;
 
 import android.speech.tts.TextToSpeech;
 import android.speech.tts.TextToSpeech.OnInitListener;
+import android.location.Location;
 
-import org.altusmetrum.altoslib_2.*;
+import org.altusmetrum.altoslib_7.*;
 
 public class AltosVoice {
 
        private TextToSpeech tts         = null;
        private boolean      tts_enabled = false;
 
-       private IdleThread   idle_thread = null;
+       static final int TELL_MODE_NONE = 0;
+       static final int TELL_MODE_PAD = 1;
+       static final int TELL_MODE_FLIGHT = 2;
+       static final int TELL_MODE_RECOVER = 3;
 
-       private AltosState   old_state   = null;
+       static final int TELL_FLIGHT_NONE = 0;
+       static final int TELL_FLIGHT_STATE = 1;
+       static final int TELL_FLIGHT_SPEED = 2;
+       static final int TELL_FLIGHT_HEIGHT = 3;
+       static final int TELL_FLIGHT_TRACK = 4;
 
-       public AltosVoice(AltosDroid a) {
+       private int             last_tell_mode;
+       private int             last_tell_serial = AltosLib.MISSING;
+       private int             last_state;
+       private AltosGPS        last_gps;
+       private double          last_height = AltosLib.MISSING;
+       private Location        last_receiver;
+       private long            last_speak_time;
+       private int             last_flight_tell = TELL_FLIGHT_NONE;
+
+       private long now() {
+               return System.currentTimeMillis();
+       }
+
+       private void reset_last() {
+               last_tell_mode = TELL_MODE_NONE;
+               last_speak_time = now() - 100 * 1000;
+               last_gps = null;
+               last_height = AltosLib.MISSING;
+               last_receiver = null;
+               last_state = AltosLib.ao_flight_invalid;
+               last_flight_tell = TELL_FLIGHT_NONE;
+       }
 
+       public AltosVoice(AltosDroid a) {
                tts = new TextToSpeech(a, new OnInitListener() {
                        public void onInit(int status) {
                                if (status == TextToSpeech.SUCCESS) tts_enabled = true;
-                               if (tts_enabled) {
-                                       idle_thread = new IdleThread();
-                               }
                        }
                });
+               reset_last();
+       }
 
+       public synchronized void set_enable(boolean enable) {
+               tts_enabled = enable;
        }
 
-       public void speak(String s) {
+       public synchronized void speak(String s) {
                if (!tts_enabled) return;
+               last_speak_time = now();
                tts.speak(s, TextToSpeech.QUEUE_ADD, null);
        }
 
+       public synchronized long time_since_speak() {
+               return now() - last_speak_time;
+       }
+
+       public synchronized void speak(String format, Object ... arguments) {
+               speak(String.format(format, arguments));
+       }
+
+       public synchronized boolean is_speaking() {
+               return tts.isSpeaking();
+       }
+
        public void stop() {
-               if (tts != null) tts.shutdown();
-               if (idle_thread != null) {
-                       idle_thread.interrupt();
-                       idle_thread = null;
+               if (tts != null) {
+                       tts.stop();
+                       tts.shutdown();
                }
        }
 
-       public void tell(AltosState state) {
-               if (!tts_enabled) return;
+       private boolean         last_apogee_good;
+       private boolean         last_main_good;
+       private boolean         last_gps_good;
 
-               boolean spoke = false;
-               if (old_state == null || old_state.state != state.state) {
-                       speak(state.state_name());
-                       if ((old_state == null || old_state.state <= AltosLib.ao_flight_boost) &&
-                           state.state > AltosLib.ao_flight_boost) {
-                               speak(String.format("max speed: %d meters per second.", (int) (state.max_speed() + 0.5)));
-                               spoke = true;
-                       } else if ((old_state == null || old_state.state < AltosLib.ao_flight_drogue) &&
-                                  state.state >= AltosLib.ao_flight_drogue) {
-                               speak(String.format("max height: %d meters.", (int) (state.max_height() + 0.5)));
-                               spoke = true;
-                       }
+       private boolean tell_gonogo(String name,
+                                 boolean current,
+                                 boolean previous,
+                                 boolean new_mode) {
+               if (current != previous || new_mode)
+                       speak("%s %s.", name, current ? "ready" : "not ready");
+               return current;
+       }
+
+       private boolean tell_pad(TelemetryState telem_state, AltosState state,
+                             AltosGreatCircle from_receiver, Location receiver) {
+
+               if (state == null)
+                       return false;
+
+               if (state.apogee_voltage != AltosLib.MISSING)
+                       last_apogee_good = tell_gonogo("apogee",
+                                                      state.apogee_voltage >= AltosLib.ao_igniter_good,
+                                                      last_apogee_good,
+                                                      last_tell_mode != TELL_MODE_PAD);
+
+               if (state.main_voltage != AltosLib.MISSING)
+                       last_main_good = tell_gonogo("main",
+                                                    state.main_voltage >= AltosLib.ao_igniter_good,
+                                                    last_main_good,
+                                                    last_tell_mode != TELL_MODE_PAD);
+
+               if (state.gps != null)
+                       last_gps_good = tell_gonogo("G P S",
+                                                   state.gps_ready,
+                                                   last_gps_good,
+                                                   last_tell_mode != TELL_MODE_PAD);
+               return true;
+       }
+
+
+       private boolean descending(int state) {
+               return AltosLib.ao_flight_drogue <= state && state <= AltosLib.ao_flight_landed;
+       }
+
+       private boolean target_moved(AltosState state) {
+               if (last_gps != null && state != null && state.gps != null) {
+                       AltosGreatCircle        moved = new AltosGreatCircle(last_gps.lat, last_gps.lon, last_gps.alt,
+                                                                            state.gps.lat, state.gps.lon, state.gps.alt);
+                       double                  height_change = 0;
+                       double                  height = state.height();
+
+                       if (height != AltosLib.MISSING && last_height != AltosLib.MISSING)
+                               height_change = Math.abs(last_height - height);
+
+                       if (moved.range < 10 && height_change < 10)
+                               return false;
                }
-               if (old_state == null || old_state.gps_ready != state.gps_ready) {
-                       if (state.gps_ready) {
-                               speak("GPS ready");
-                               spoke = true;
-                       } else if (old_state != null) {
-                               speak("GPS lost");
-                               spoke = true;
-                       }
+               return true;
+       }
+
+       private boolean receiver_moved(Location receiver) {
+               if (last_receiver != null && receiver != null) {
+                       AltosGreatCircle        moved = new AltosGreatCircle(last_receiver.getLatitude(),
+                                                                            last_receiver.getLongitude(),
+                                                                            last_receiver.getAltitude(),
+                                                                            receiver.getLatitude(),
+                                                                            receiver.getLongitude(),
+                                                                            receiver.getAltitude());
+                       if (moved.range < 10)
+                               return false;
                }
-               old_state = state;
-               idle_thread.notice(state, spoke);
+               return true;
        }
 
+       private boolean tell_flight(TelemetryState telem_state, AltosState state,
+                                   AltosGreatCircle from_receiver, Location receiver) {
 
-       class IdleThread extends Thread {
-               boolean            started;
-               private AltosState state;
-               int                reported_landing;
-               int                report_interval;
-               long               report_time;
+               boolean spoken = false;
 
-               public synchronized void report(boolean last) {
-                       if (state == null)
-                               return;
+               if (state == null)
+                       return false;
 
-                       /* reset the landing count once we hear about a new flight */
-                       if (state.state < AltosLib.ao_flight_drogue)
-                               reported_landing = 0;
+               if (last_tell_mode != TELL_MODE_FLIGHT)
+                       last_flight_tell = TELL_FLIGHT_NONE;
 
-                       /* Shut up once the rocket is on the ground */
-                       if (reported_landing > 2) {
-                               return;
+               if (state.state != last_state && AltosLib.ao_flight_boost <= state.state && state.state <= AltosLib.ao_flight_landed) {
+                       speak(state.state_name());
+                       if (descending(state.state) && !descending(last_state)) {
+                               if (state.max_height() != AltosLib.MISSING) {
+                                       speak("max height: %s.",
+                                             AltosConvert.height.say_units(state.max_height()));
+                               }
                        }
+                       last_flight_tell = TELL_FLIGHT_STATE;
+                       return true;
+               }
+
+               if (last_tell_mode == TELL_MODE_FLIGHT && last_flight_tell == TELL_FLIGHT_TRACK) {
+                       if (time_since_speak() < 10 * 1000)
+                               return false;
+                       if (!target_moved(state) && !receiver_moved(receiver))
+                               return false;
+               }
+
+               double  speed;
+               double  height;
+
+               if (last_flight_tell == TELL_FLIGHT_NONE || last_flight_tell == TELL_FLIGHT_STATE || last_flight_tell == TELL_FLIGHT_TRACK) {
+                       last_flight_tell = TELL_FLIGHT_SPEED;
 
-                       /* If the rocket isn't on the pad, then report height */
-                       if (AltosLib.ao_flight_drogue <= state.state &&
-                           state.state < AltosLib.ao_flight_landed &&
-                           state.range >= 0)
-                       {
-                               speak(String.format("Height %d, bearing %s %d, elevation %d, range %d.\n",
-                                                   (int) (state.height() + 0.5),
-                                       state.from_pad.bearing_words(
-                                             AltosGreatCircle.BEARING_VOICE),
-                                                   (int) (state.from_pad.bearing + 0.5),
-                                                   (int) (state.elevation + 0.5),
-                                                   (int) (state.range + 0.5)));
-                       } else if (state.state > AltosLib.ao_flight_pad) {
-                               speak(String.format("%d meters", (int) (state.height() + 0.5)));
+                       if (state.state <= AltosLib.ao_flight_coast) {
+                               speed = state.speed();
                        } else {
-                               reported_landing = 0;
+                               speed = state.gps_speed();
+                               if (speed == AltosLib.MISSING)
+                                       speed = state.speed();
                        }
 
-                       /* If the rocket is coming down, check to see if it has landed;
-                        * either we've got a landed report or we haven't heard from it in
-                        * a long time
-                        */
-                       if (state.state >= AltosLib.ao_flight_drogue &&
-                           (last ||
-                            System.currentTimeMillis() - state.received_time >= 15000 ||
-                            state.state == AltosLib.ao_flight_landed))
-                       {
-                               if (Math.abs(state.speed()) < 20 && state.height() < 100)
-                                       speak("rocket landed safely");
-                               else
-                                       speak("rocket may have crashed");
-                               if (state.from_pad != null)
-                                       speak(String.format("Bearing %d degrees, range %d meters.",
-                                                           (int) (state.from_pad.bearing + 0.5),
-                                                           (int) (state.from_pad.distance + 0.5)));
-                               ++reported_landing;
+                       if (speed != AltosLib.MISSING) {
+                               speak("speed: %s.", AltosConvert.speed.say_units(speed));
+                               return true;
                        }
                }
 
-               long now () {
-                       return System.currentTimeMillis();
-               }
+               if (last_flight_tell == TELL_FLIGHT_SPEED) {
+                       last_flight_tell = TELL_FLIGHT_HEIGHT;
+                       height = state.height();
 
-               void set_report_time() {
-                       report_time = now() + report_interval;
+                       if (height != AltosLib.MISSING) {
+                               speak("height: %s.", AltosConvert.height.say_units(height));
+                               return true;
+                       }
                }
 
-               public void run () {
-                       try {
-                               for (;;) {
-                                       set_report_time();
-                                       for (;;) {
-                                               synchronized (this) {
-                                                       long sleep_time = report_time - now();
-                                                       if (sleep_time <= 0)
-                                                               break;
-                                                       wait(sleep_time);
-                                               }
-                                       }
-                                       report(false);
-                               }
-                       } catch (InterruptedException ie) {
+               if (last_flight_tell == TELL_FLIGHT_HEIGHT) {
+                       last_flight_tell = TELL_FLIGHT_TRACK;
+                       if (from_receiver != null) {
+                               speak("bearing %s %d, elevation %d, range %s.",
+                                     from_receiver.bearing_words(
+                                             AltosGreatCircle.BEARING_VOICE),
+                                     (int) (from_receiver.bearing + 0.5),
+                                     (int) (from_receiver.elevation + 0.5),
+                                     AltosConvert.distance.say(from_receiver.range));
+                               return true;
                        }
                }
 
-               public synchronized void notice(AltosState new_state, boolean spoken) {
-                       AltosState old_state = state;
-                       state = new_state;
-                       if (!started && state.state > AltosLib.ao_flight_pad) {
-                               started = true;
-                               start();
-                       }
+               return spoken;
+       }
 
-                       if (state.state < AltosLib.ao_flight_drogue)
-                               report_interval = 10000;
-                       else
-                               report_interval = 20000;
-                       if (old_state != null && old_state.state != state.state) {
-                               report_time = now();
-                               this.notify();
-                       } else if (spoken)
-                               set_report_time();
-               }
+       private boolean tell_recover(TelemetryState telem_state, AltosState state,
+                                    AltosGreatCircle from_receiver, Location receiver) {
 
-               public IdleThread() {
-                       state = null;
-                       reported_landing = 0;
-                       report_interval = 10000;
+               if (from_receiver == null)
+                       return false;
+
+               if (last_tell_mode == TELL_MODE_RECOVER) {
+                       if (!target_moved(state) && !receiver_moved(receiver))
+                               return false;
+                       if (time_since_speak() <= 10 * 1000)
+                               return false;
                }
+
+               String direction = AltosDroid.direction(from_receiver, receiver);
+               if (direction == null)
+                       direction = String.format("Bearing %d", (int) (from_receiver.bearing + 0.5));
+
+               speak("%s, range %s.", direction,
+                     AltosConvert.distance.say_units(from_receiver.distance));
+
+               return true;
        }
 
+       public void tell(TelemetryState telem_state, AltosState state,
+                        AltosGreatCircle from_receiver, Location receiver,
+                        AltosDroidTab tab) {
+
+               boolean spoken = false;
+
+               if (!tts_enabled) return;
+
+               if (is_speaking()) return;
+
+               int     tell_serial = last_tell_serial;
+
+               if (state != null)
+                       tell_serial = state.serial;
+
+               if (tell_serial != last_tell_serial)
+                       reset_last();
+
+               int     tell_mode = TELL_MODE_NONE;
+
+               if (tab.tab_name().equals(AltosDroid.tab_pad_name))
+                       tell_mode = TELL_MODE_PAD;
+               else if (tab.tab_name().equals(AltosDroid.tab_flight_name))
+                       tell_mode = TELL_MODE_FLIGHT;
+               else
+                       tell_mode = TELL_MODE_RECOVER;
+
+               if (tell_mode == TELL_MODE_PAD)
+                       spoken = tell_pad(telem_state, state, from_receiver, receiver);
+               else if (tell_mode == TELL_MODE_FLIGHT)
+                       spoken = tell_flight(telem_state, state, from_receiver, receiver);
+               else
+                       spoken = tell_recover(telem_state, state, from_receiver, receiver);
+
+               if (spoken) {
+                       last_tell_mode = tell_mode;
+                       last_tell_serial = tell_serial;
+                       if (state != null) {
+                               last_state = state.state;
+                               last_height = state.height();
+                               if (state.gps != null)
+                                       last_gps = state.gps;
+                       }
+                       if (receiver != null)
+                               last_receiver = receiver;
+               }
+       }
 }