altosui: Fix Landed tab units
[fw/altos] / altosui / AltosDescent.java
1 /*
2  * Copyright © 2010 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 package altosui;
19
20 import java.awt.*;
21 import java.awt.event.*;
22 import javax.swing.*;
23 import javax.swing.filechooser.FileNameExtensionFilter;
24 import javax.swing.table.*;
25 import java.io.*;
26 import java.util.*;
27 import java.text.*;
28 import java.util.prefs.*;
29 import java.util.concurrent.LinkedBlockingQueue;
30 import org.altusmetrum.AltosLib.*;
31
32 public class AltosDescent extends JComponent implements AltosFlightDisplay {
33         GridBagLayout   layout;
34
35         public abstract class DescentStatus {
36                 JLabel          label;
37                 JTextField      value;
38                 AltosLights     lights;
39
40                 abstract void show(AltosState state, int crc_errors);
41
42                 void show() {
43                         label.setVisible(true);
44                         value.setVisible(true);
45                         lights.setVisible(true);
46                 }
47
48                 void show(String s) {
49                         show();
50                         value.setText(s);
51                 }
52
53                 void show(String format, double value) {
54                         show(String.format(format, value));
55                 }
56
57                 void hide() {
58                         label.setVisible(false);
59                         value.setVisible(false);
60                         lights.setVisible(false);
61                 }
62
63                 void reset() {
64                         value.setText("");
65                         lights.set(false);
66                 }
67
68                 void set_font() {
69                         label.setFont(Altos.label_font);
70                         value.setFont(Altos.value_font);
71                 }
72
73                 public DescentStatus (GridBagLayout layout, int y, String text) {
74                         GridBagConstraints      c = new GridBagConstraints();
75                         c.weighty = 1;
76
77                         lights = new AltosLights();
78                         c.gridx = 0; c.gridy = y;
79                         c.anchor = GridBagConstraints.CENTER;
80                         c.fill = GridBagConstraints.VERTICAL;
81                         c.weightx = 0;
82                         layout.setConstraints(lights, c);
83                         add(lights);
84
85                         label = new JLabel(text);
86                         label.setFont(Altos.label_font);
87                         label.setHorizontalAlignment(SwingConstants.LEFT);
88                         c.gridx = 1; c.gridy = y;
89                         c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad);
90                         c.anchor = GridBagConstraints.WEST;
91                         c.fill = GridBagConstraints.VERTICAL;
92                         c.gridwidth = 3;
93                         c.weightx = 0;
94                         layout.setConstraints(label, c);
95                         add(label);
96
97                         value = new JTextField(Altos.text_width);
98                         value.setFont(Altos.value_font);
99                         value.setHorizontalAlignment(SwingConstants.RIGHT);
100                         c.gridx = 4; c.gridy = y;
101                         c.gridwidth = 1;
102                         c.anchor = GridBagConstraints.WEST;
103                         c.fill = GridBagConstraints.BOTH;
104                         c.weightx = 1;
105                         layout.setConstraints(value, c);
106                         add(value);
107
108                 }
109         }
110
111         public abstract class DescentValue {
112                 JLabel          label;
113                 JTextField      value;
114
115                 void reset() {
116                         value.setText("");
117                 }
118
119                 abstract void show(AltosState state, int crc_errors);
120
121                 void show() {
122                         label.setVisible(true);
123                         value.setVisible(true);
124                 }
125
126                 void hide() {
127                         label.setVisible(false);
128                         value.setVisible(false);
129                 }
130
131                 void show(String v) {
132                         show();
133                         value.setText(v);
134                 }
135
136                 void show(AltosUnits units, double v) {
137                         show(units.show(8, v));
138                 }
139
140                 void show(String format, double v) {
141                         show(String.format(format, v));
142                 }
143
144                 void set_font() {
145                         label.setFont(Altos.label_font);
146                         value.setFont(Altos.value_font);
147                 }
148
149                 public DescentValue (GridBagLayout layout, int x, int y, String text) {
150                         GridBagConstraints      c = new GridBagConstraints();
151                         c.weighty = 1;
152
153                         label = new JLabel(text);
154                         label.setFont(Altos.label_font);
155                         label.setHorizontalAlignment(SwingConstants.LEFT);
156                         c.gridx = x + 1; c.gridy = y;
157                         c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad);
158                         c.anchor = GridBagConstraints.WEST;
159                         c.fill = GridBagConstraints.VERTICAL;
160                         c.weightx = 0;
161                         add(label, c);
162
163                         value = new JTextField(Altos.text_width);
164                         value.setFont(Altos.value_font);
165                         value.setHorizontalAlignment(SwingConstants.RIGHT);
166                         c.gridx = x + 2; c.gridy = y;
167                         c.gridwidth = 1;
168                         c.anchor = GridBagConstraints.WEST;
169                         c.fill = GridBagConstraints.BOTH;
170                         c.weightx = 1;
171                         add(value, c);
172                 }
173         }
174
175         public abstract class DescentDualValue {
176                 JLabel          label;
177                 JTextField      value1;
178                 JTextField      value2;
179
180                 void reset() {
181                         value1.setText("");
182                         value2.setText("");
183                 }
184
185                 void show() {
186                         label.setVisible(true);
187                         value1.setVisible(true);
188                         value2.setVisible(true);
189                 }
190
191                 void hide() {
192                         label.setVisible(false);
193                         value1.setVisible(false);
194                         value2.setVisible(false);
195                 }
196
197                 void set_font() {
198                         label.setFont(Altos.label_font);
199                         value1.setFont(Altos.value_font);
200                         value2.setFont(Altos.value_font);
201                 }
202
203                 abstract void show(AltosState state, int crc_errors);
204
205                 void show(String v1, String v2) {
206                         show();
207                         value1.setText(v1);
208                         value2.setText(v2);
209                 }
210                 void show(String f1, double v1, String f2, double v2) {
211                         show();
212                         value1.setText(String.format(f1, v1));
213                         value2.setText(String.format(f2, v2));
214                 }
215
216                 public DescentDualValue (GridBagLayout layout, int x, int y, String text) {
217                         GridBagConstraints      c = new GridBagConstraints();
218                         c.weighty = 1;
219
220                         label = new JLabel(text);
221                         label.setFont(Altos.label_font);
222                         label.setHorizontalAlignment(SwingConstants.LEFT);
223                         c.gridx = x + 1; c.gridy = y;
224                         c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad);
225                         c.anchor = GridBagConstraints.WEST;
226                         c.fill = GridBagConstraints.VERTICAL;
227                         c.weightx = 0;
228                         layout.setConstraints(label, c);
229                         add(label);
230
231                         value1 = new JTextField(Altos.text_width);
232                         value1.setFont(Altos.value_font);
233                         value1.setHorizontalAlignment(SwingConstants.RIGHT);
234                         c.gridx = x + 2; c.gridy = y;
235                         c.anchor = GridBagConstraints.WEST;
236                         c.fill = GridBagConstraints.BOTH;
237                         c.weightx = 1;
238                         layout.setConstraints(value1, c);
239                         add(value1);
240
241                         value2 = new JTextField(Altos.text_width);
242                         value2.setFont(Altos.value_font);
243                         value2.setHorizontalAlignment(SwingConstants.RIGHT);
244                         c.gridx = x + 4; c.gridy = y;
245                         c.anchor = GridBagConstraints.WEST;
246                         c.fill = GridBagConstraints.BOTH;
247                         c.weightx = 1;
248                         c.gridwidth = 1;
249                         layout.setConstraints(value2, c);
250                         add(value2);
251                 }
252         }
253
254         class Height extends DescentValue {
255                 void show (AltosState state, int crc_errors) {
256                         show(AltosConvert.height, state.height);
257                 }
258                 public Height (GridBagLayout layout, int x, int y) {
259                         super (layout, x, y, "Height");
260                 }
261         }
262
263         Height  height;
264
265         class Speed extends DescentValue {
266                 void show (AltosState state, int crc_errors) {
267                         double speed = state.speed;
268                         if (!state.ascent)
269                                 speed = state.baro_speed;
270                         show(AltosConvert.speed, speed);
271                 }
272                 public Speed (GridBagLayout layout, int x, int y) {
273                         super (layout, x, y, "Speed");
274                 }
275         }
276
277         Speed   speed;
278
279         String pos(double p, String pos, String neg) {
280                 String  h = pos;
281                 if (p < 0) {
282                         h = neg;
283                         p = -p;
284                 }
285                 int deg = (int) Math.floor(p);
286                 double min = (p - Math.floor(p)) * 60.0;
287                 return String.format("%s %d° %9.6f", h, deg, min);
288         }
289
290         class Lat extends DescentValue {
291                 void show (AltosState state, int crc_errors) {
292                         if (state.gps != null && state.gps.connected)
293                                 show(pos(state.gps.lat,"N", "S"));
294                         else
295                                 show("???");
296                 }
297                 public Lat (GridBagLayout layout, int x, int y) {
298                         super (layout, x, y, "Latitude");
299                 }
300         }
301
302         Lat lat;
303
304         class Lon extends DescentValue {
305                 void show (AltosState state, int crc_errors) {
306                         if (state.gps != null && state.gps.connected)
307                                 show(pos(state.gps.lon,"W", "E"));
308                         else
309                                 show("???");
310                 }
311                 public Lon (GridBagLayout layout, int x, int y) {
312                         super (layout, x, y, "Longitude");
313                 }
314         }
315
316         Lon lon;
317
318         class Apogee extends DescentStatus {
319                 void show (AltosState state, int crc_errors) {
320                         show("%4.2f V", state.drogue_sense);
321                         lights.set(state.drogue_sense > 3.2);
322                 }
323                 public Apogee (GridBagLayout layout, int y) {
324                         super(layout, y, "Apogee Igniter Voltage");
325                 }
326         }
327
328         Apogee apogee;
329
330         class Main extends DescentStatus {
331                 void show (AltosState state, int crc_errors) {
332                         show("%4.2f V", state.main_sense);
333                         lights.set(state.main_sense > 3.2);
334                 }
335                 public Main (GridBagLayout layout, int y) {
336                         super(layout, y, "Main Igniter Voltage");
337                 }
338         }
339
340         Main main;
341
342         class Bearing extends DescentDualValue {
343                 void show (AltosState state, int crc_errors) {
344                         if (state.from_pad != null) {
345                                 show( String.format("%3.0f°", state.from_pad.bearing),
346                                       state.from_pad.bearing_words(
347                                               AltosGreatCircle.BEARING_LONG));
348                         } else {
349                                 show("???", "???");
350                         }
351                 }
352                 public Bearing (GridBagLayout layout, int x, int y) {
353                         super (layout, x, y, "Bearing");
354                 }
355         }
356
357         Bearing bearing;
358
359         class Range extends DescentValue {
360                 void show (AltosState state, int crc_errors) {
361                         show(AltosConvert.distance, state.range);
362                 }
363                 public Range (GridBagLayout layout, int x, int y) {
364                         super (layout, x, y, "Range");
365                 }
366         }
367
368         Range range;
369
370         class Elevation extends DescentValue {
371                 void show (AltosState state, int crc_errors) {
372                         show("%3.0f°", state.elevation);
373                 }
374                 public Elevation (GridBagLayout layout, int x, int y) {
375                         super (layout, x, y, "Elevation");
376                 }
377         }
378
379         Elevation elevation;
380
381         public void reset() {
382                 lat.reset();
383                 lon.reset();
384                 height.reset();
385                 speed.reset();
386                 bearing.reset();
387                 range.reset();
388                 elevation.reset();
389                 main.reset();
390                 apogee.reset();
391         }
392
393         public void set_font() {
394                 lat.set_font();
395                 lon.set_font();
396                 height.set_font();
397                 speed.set_font();
398                 bearing.set_font();
399                 range.set_font();
400                 elevation.set_font();
401                 main.set_font();
402                 apogee.set_font();
403         }
404
405         public void show(AltosState state, int crc_errors) {
406                 height.show(state, crc_errors);
407                 speed.show(state, crc_errors);
408                 if (state.gps != null && state.gps.connected) {
409                         bearing.show(state, crc_errors);
410                         range.show(state, crc_errors);
411                         elevation.show(state, crc_errors);
412                         lat.show(state, crc_errors);
413                         lon.show(state, crc_errors);
414                 } else {
415                         bearing.hide();
416                         range.hide();
417                         elevation.hide();
418                         lat.hide();
419                         lon.hide();
420                 }
421                 if (state.main_sense != AltosRecord.MISSING)
422                         main.show(state, crc_errors);
423                 else
424                         main.hide();
425                 if (state.drogue_sense != AltosRecord.MISSING)
426                         apogee.show(state, crc_errors);
427                 else
428                         apogee.hide();
429         }
430
431         public AltosDescent() {
432                 layout = new GridBagLayout();
433
434                 setLayout(layout);
435
436                 /* Elements in descent display */
437                 speed = new Speed(layout, 0, 0);
438                 height = new Height(layout, 2, 0);
439                 elevation = new Elevation(layout, 0, 1);
440                 range = new Range(layout, 2, 1);
441                 bearing = new Bearing(layout, 0, 2);
442                 lat = new Lat(layout, 0, 3);
443                 lon = new Lon(layout, 2, 3);
444
445                 apogee = new Apogee(layout, 4);
446                 main = new Main(layout, 5);
447         }
448 }