micropeak: Oops. Lost the call to actually start downloading data
[fw/altos] / altosdroid / src / org / altusmetrum / AltosDroid / AltosVoice.java
1 /*\r
2  * Copyright © 2011 Keith Packard <keithp@keithp.com>\r
3  * Copyright © 2012 Mike Beattie <mike@ethernal.org>\r
4  *\r
5  * This program is free software; you can redistribute it and/or modify\r
6  * it under the terms of the GNU General Public License as published by\r
7  * the Free Software Foundation; version 2 of the License.\r
8  *\r
9  * This program is distributed in the hope that it will be useful, but\r
10  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
12  * General Public License for more details.\r
13  *\r
14  * You should have received a copy of the GNU General Public License along\r
15  * with this program; if not, write to the Free Software Foundation, Inc.,\r
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.\r
17  */\r
18 \r
19 package org.altusmetrum.AltosDroid;\r
20 \r
21 import android.speech.tts.TextToSpeech;\r
22 import android.speech.tts.TextToSpeech.OnInitListener;\r
23 \r
24 import org.altusmetrum.altoslib_1.*;\r
25 \r
26 public class AltosVoice {\r
27 \r
28         private TextToSpeech tts         = null;\r
29         private boolean      tts_enabled = false;\r
30 \r
31         private IdleThread   idle_thread = null;\r
32 \r
33         private AltosState   old_state   = null;\r
34 \r
35         public AltosVoice(AltosDroid a) {\r
36 \r
37                 tts = new TextToSpeech(a, new OnInitListener() {\r
38                         public void onInit(int status) {\r
39                                 if (status == TextToSpeech.SUCCESS) tts_enabled = true;\r
40                                 if (tts_enabled) {\r
41                                         speak("AltosDroid ready");\r
42                                         idle_thread = new IdleThread();\r
43                                 }\r
44                         }\r
45                 });\r
46 \r
47         }\r
48 \r
49         public void speak(String s) {\r
50                 if (!tts_enabled) return;\r
51                 tts.speak(s, TextToSpeech.QUEUE_ADD, null);\r
52         }\r
53 \r
54         public void stop() {\r
55                 if (tts != null) tts.shutdown();\r
56                 if (idle_thread != null) {\r
57                         idle_thread.interrupt();\r
58                         idle_thread = null;\r
59                 }\r
60         }\r
61 \r
62         public void tell(AltosState state) {\r
63                 if (!tts_enabled) return;\r
64 \r
65                 boolean spoke = false;\r
66                 if (old_state == null || old_state.state != state.state) {\r
67                         speak(state.data.state());\r
68                         if ((old_state == null || old_state.state <= AltosLib.ao_flight_boost) &&\r
69                             state.state > AltosLib.ao_flight_boost) {\r
70                                 speak(String.format("max speed: %d meters per second.", (int) (state.max_speed() + 0.5)));\r
71                                 spoke = true;\r
72                         } else if ((old_state == null || old_state.state < AltosLib.ao_flight_drogue) &&\r
73                                    state.state >= AltosLib.ao_flight_drogue) {\r
74                                 speak(String.format("max height: %d meters.", (int) (state.max_height + 0.5)));\r
75                                 spoke = true;\r
76                         }\r
77                 }\r
78                 if (old_state == null || old_state.gps_ready != state.gps_ready) {\r
79                         if (state.gps_ready) {\r
80                                 speak("GPS ready");\r
81                                 spoke = true;\r
82                         } else if (old_state != null) {\r
83                                 speak("GPS lost");\r
84                                 spoke = true;\r
85                         }\r
86                 }\r
87                 old_state = state;\r
88                 idle_thread.notice(state, spoke);\r
89         }\r
90 \r
91 \r
92         class IdleThread extends Thread {\r
93                 boolean            started;\r
94                 private AltosState state;\r
95                 int                reported_landing;\r
96                 int                report_interval;\r
97                 long               report_time;\r
98 \r
99                 public synchronized void report(boolean last) {\r
100                         if (state == null)\r
101                                 return;\r
102 \r
103                         /* reset the landing count once we hear about a new flight */\r
104                         if (state.state < AltosLib.ao_flight_drogue)\r
105                                 reported_landing = 0;\r
106 \r
107                         /* Shut up once the rocket is on the ground */\r
108                         if (reported_landing > 2) {\r
109                                 return;\r
110                         }\r
111 \r
112                         /* If the rocket isn't on the pad, then report height */\r
113                         if (AltosLib.ao_flight_drogue <= state.state &&\r
114                             state.state < AltosLib.ao_flight_landed &&\r
115                             state.range >= 0)\r
116                         {\r
117                                 speak(String.format("Height %d, bearing %s %d, elevation %d, range %d.\n",\r
118                                                     (int) (state.height + 0.5),\r
119                                         state.from_pad.bearing_words(\r
120                                               AltosGreatCircle.BEARING_VOICE),\r
121                                                     (int) (state.from_pad.bearing + 0.5),\r
122                                                     (int) (state.elevation + 0.5),\r
123                                                     (int) (state.range + 0.5)));\r
124                         } else if (state.state > AltosLib.ao_flight_pad) {\r
125                                 speak(String.format("%d meters", (int) (state.height + 0.5)));\r
126                         } else {\r
127                                 reported_landing = 0;\r
128                         }\r
129 \r
130                         /* If the rocket is coming down, check to see if it has landed;\r
131                          * either we've got a landed report or we haven't heard from it in\r
132                          * a long time\r
133                          */\r
134                         if (state.state >= AltosLib.ao_flight_drogue &&\r
135                             (last ||\r
136                              System.currentTimeMillis() - state.report_time >= 15000 ||\r
137                              state.state == AltosLib.ao_flight_landed))\r
138                         {\r
139                                 if (Math.abs(state.baro_speed) < 20 && state.height < 100)\r
140                                         speak("rocket landed safely");\r
141                                 else\r
142                                         speak("rocket may have crashed");\r
143                                 if (state.from_pad != null)\r
144                                         speak(String.format("Bearing %d degrees, range %d meters.",\r
145                                                             (int) (state.from_pad.bearing + 0.5),\r
146                                                             (int) (state.from_pad.distance + 0.5)));\r
147                                 ++reported_landing;\r
148                         }\r
149                 }\r
150 \r
151                 long now () {\r
152                         return System.currentTimeMillis();\r
153                 }\r
154 \r
155                 void set_report_time() {\r
156                         report_time = now() + report_interval;\r
157                 }\r
158 \r
159                 public void run () {\r
160                         try {\r
161                                 for (;;) {\r
162                                         set_report_time();\r
163                                         for (;;) {\r
164                                                 synchronized (this) {\r
165                                                         long sleep_time = report_time - now();\r
166                                                         if (sleep_time <= 0)\r
167                                                                 break;\r
168                                                         wait(sleep_time);\r
169                                                 }\r
170                                         }\r
171                                         report(false);\r
172                                 }\r
173                         } catch (InterruptedException ie) {\r
174                         }\r
175                 }\r
176 \r
177                 public synchronized void notice(AltosState new_state, boolean spoken) {\r
178                         AltosState old_state = state;\r
179                         state = new_state;\r
180                         if (!started && state.state > AltosLib.ao_flight_pad) {\r
181                                 started = true;\r
182                                 start();\r
183                         }\r
184 \r
185                         if (state.state < AltosLib.ao_flight_drogue)\r
186                                 report_interval = 10000;\r
187                         else\r
188                                 report_interval = 20000;\r
189                         if (old_state != null && old_state.state != state.state) {\r
190                                 report_time = now();\r
191                                 this.notify();\r
192                         } else if (spoken)\r
193                                 set_report_time();\r
194                 }\r
195 \r
196                 public IdleThread() {\r
197                         state = null;\r
198                         reported_landing = 0;\r
199                         report_interval = 10000;\r
200                 }\r
201         }\r
202 \r
203 }\r