altosui: Show "Missing" instead of huge numbers in descent/landed tabs
[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 javax.swing.*;
22 import org.altusmetrum.altoslib_4.*;
23 import org.altusmetrum.altosuilib_2.*;
24
25 public class AltosDescent extends JComponent implements AltosFlightDisplay {
26         GridBagLayout   layout;
27
28         public abstract class DescentStatus implements AltosFontListener, AltosUnitsListener {
29                 JLabel          label;
30                 JTextField      value;
31                 AltosLights     lights;
32
33                 abstract void show(AltosState state, AltosListenerState listener_state);
34
35                 void show() {
36                         label.setVisible(true);
37                         value.setVisible(true);
38                         lights.setVisible(true);
39                 }
40
41                 void show(String s) {
42                         show();
43                         value.setText(s);
44                 }
45
46                 void show(String format, double value) {
47                         show(String.format(format, value));
48                 }
49
50                 void hide() {
51                         label.setVisible(false);
52                         value.setVisible(false);
53                         lights.setVisible(false);
54                 }
55
56                 void reset() {
57                         value.setText("");
58                         lights.set(false);
59                 }
60
61                 public void font_size_changed(int font_size) {
62                         label.setFont(Altos.label_font);
63                         value.setFont(Altos.value_font);
64                 }
65
66                 public void units_changed(boolean imperial_units) {
67                 }
68
69                 public DescentStatus (GridBagLayout layout, int y, String text) {
70                         GridBagConstraints      c = new GridBagConstraints();
71                         c.weighty = 1;
72
73                         lights = new AltosLights();
74                         c.gridx = 0; c.gridy = y;
75                         c.anchor = GridBagConstraints.CENTER;
76                         c.fill = GridBagConstraints.VERTICAL;
77                         c.weightx = 0;
78                         layout.setConstraints(lights, c);
79                         add(lights);
80
81                         label = new JLabel(text);
82                         label.setFont(Altos.label_font);
83                         label.setHorizontalAlignment(SwingConstants.LEFT);
84                         c.gridx = 1; c.gridy = y;
85                         c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad);
86                         c.anchor = GridBagConstraints.WEST;
87                         c.fill = GridBagConstraints.VERTICAL;
88                         c.gridwidth = 3;
89                         c.weightx = 0;
90                         layout.setConstraints(label, c);
91                         add(label);
92
93                         value = new JTextField(Altos.text_width);
94                         value.setFont(Altos.value_font);
95                         value.setHorizontalAlignment(SwingConstants.RIGHT);
96                         c.gridx = 4; c.gridy = y;
97                         c.gridwidth = 1;
98                         c.anchor = GridBagConstraints.WEST;
99                         c.fill = GridBagConstraints.BOTH;
100                         c.weightx = 1;
101                         layout.setConstraints(value, c);
102                         add(value);
103
104                 }
105         }
106
107         public abstract class DescentValue implements AltosFontListener, AltosUnitsListener {
108                 JLabel          label;
109                 JTextField      value;
110                 AltosUnits      units;
111                 double          v;
112
113                 void reset() {
114                         value.setText("");
115                 }
116
117                 abstract void show(AltosState state, AltosListenerState listener_state);
118
119                 void show() {
120                         label.setVisible(true);
121                         value.setVisible(true);
122                 }
123
124                 void hide() {
125                         label.setVisible(false);
126                         value.setVisible(false);
127                 }
128
129                 void show(String v) {
130                         show();
131                         value.setText(v);
132                 }
133
134                 void show(double v) {
135                         this.v = v;
136                         if (v == AltosLib.MISSING)
137                                 show("Missing");
138                         else
139                                 show(units.show(8, v));
140                 }
141
142                 void show(String format, double v) {
143                         if (v == AltosLib.MISSING)
144                                 show("Missing");
145                         else
146                                 show(String.format(format, v));
147                 }
148
149                 public void font_size_changed(int font_size) {
150                         label.setFont(Altos.label_font);
151                         value.setFont(Altos.value_font);
152                 }
153
154                 public void units_changed(boolean imperial_units) {
155                         if (units != null)
156                                 show(v);
157                 }
158
159                 public DescentValue (GridBagLayout layout, int x, int y, AltosUnits units, String text) {
160                         this.units = units;
161                         GridBagConstraints      c = new GridBagConstraints();
162                         c.weighty = 1;
163
164                         label = new JLabel(text);
165                         label.setFont(Altos.label_font);
166                         label.setHorizontalAlignment(SwingConstants.LEFT);
167                         c.gridx = x + 1; c.gridy = y;
168                         c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad);
169                         c.anchor = GridBagConstraints.WEST;
170                         c.fill = GridBagConstraints.VERTICAL;
171                         c.weightx = 0;
172                         add(label, c);
173
174                         value = new JTextField(Altos.text_width);
175                         value.setFont(Altos.value_font);
176                         value.setHorizontalAlignment(SwingConstants.RIGHT);
177                         c.gridx = x + 2; c.gridy = y;
178                         c.gridwidth = 1;
179                         c.anchor = GridBagConstraints.WEST;
180                         c.fill = GridBagConstraints.BOTH;
181                         c.weightx = 1;
182                         add(value, c);
183                 }
184
185                 public DescentValue (GridBagLayout layout, int x, int y, String text) {
186                         this(layout, x, y, null, text);
187                 }
188         }
189
190         public abstract class DescentDualValue implements AltosFontListener, AltosUnitsListener {
191                 JLabel          label;
192                 JTextField      value1;
193                 JTextField      value2;
194
195                 void reset() {
196                         value1.setText("");
197                         value2.setText("");
198                 }
199
200                 void show() {
201                         label.setVisible(true);
202                         value1.setVisible(true);
203                         value2.setVisible(true);
204                 }
205
206                 void hide() {
207                         label.setVisible(false);
208                         value1.setVisible(false);
209                         value2.setVisible(false);
210                 }
211
212                 public void font_size_changed(int font_size) {
213                         label.setFont(Altos.label_font);
214                         value1.setFont(Altos.value_font);
215                         value2.setFont(Altos.value_font);
216                 }
217
218                 public void units_changed(boolean imperial_units) {
219                 }
220
221                 abstract void show(AltosState state, AltosListenerState listener_state);
222
223                 void show(String v1, String v2) {
224                         show();
225                         value1.setText(v1);
226                         value2.setText(v2);
227                 }
228                 void show(String f1, double v1, String f2, double v2) {
229                         show();
230                         value1.setText(String.format(f1, v1));
231                         value2.setText(String.format(f2, v2));
232                 }
233
234                 public DescentDualValue (GridBagLayout layout, int x, int y, String text) {
235                         GridBagConstraints      c = new GridBagConstraints();
236                         c.weighty = 1;
237
238                         label = new JLabel(text);
239                         label.setFont(Altos.label_font);
240                         label.setHorizontalAlignment(SwingConstants.LEFT);
241                         c.gridx = x + 1; c.gridy = y;
242                         c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad);
243                         c.anchor = GridBagConstraints.WEST;
244                         c.fill = GridBagConstraints.VERTICAL;
245                         c.weightx = 0;
246                         layout.setConstraints(label, c);
247                         add(label);
248
249                         value1 = new JTextField(Altos.text_width);
250                         value1.setFont(Altos.value_font);
251                         value1.setHorizontalAlignment(SwingConstants.RIGHT);
252                         c.gridx = x + 2; c.gridy = y;
253                         c.anchor = GridBagConstraints.WEST;
254                         c.fill = GridBagConstraints.BOTH;
255                         c.weightx = 1;
256                         layout.setConstraints(value1, c);
257                         add(value1);
258
259                         value2 = new JTextField(Altos.text_width);
260                         value2.setFont(Altos.value_font);
261                         value2.setHorizontalAlignment(SwingConstants.RIGHT);
262                         c.gridx = x + 4; c.gridy = y;
263                         c.anchor = GridBagConstraints.WEST;
264                         c.fill = GridBagConstraints.BOTH;
265                         c.weightx = 1;
266                         c.gridwidth = 1;
267                         layout.setConstraints(value2, c);
268                         add(value2);
269                 }
270         }
271
272         class Height extends DescentValue {
273                 void show (AltosState state, AltosListenerState listener_state) {
274                         show(state.height());
275                 }
276                 public Height (GridBagLayout layout, int x, int y) {
277                         super (layout, x, y, AltosConvert.height, "Height");
278                 }
279         }
280
281         Height  height;
282
283         class Speed extends DescentValue {
284                 void show (AltosState state, AltosListenerState listener_state) {
285                         show(state.speed());
286                 }
287                 public Speed (GridBagLayout layout, int x, int y) {
288                         super (layout, x, y, AltosConvert.speed, "Speed");
289                 }
290         }
291
292         Speed   speed;
293
294         String pos(double p, String pos, String neg) {
295                 String  h = pos;
296                 if (p < 0) {
297                         h = neg;
298                         p = -p;
299                 }
300                 int deg = (int) Math.floor(p);
301                 double min = (p - Math.floor(p)) * 60.0;
302                 return String.format("%s %d° %9.6f", h, deg, min);
303         }
304
305         class Lat extends DescentValue {
306                 void show (AltosState state, AltosListenerState listener_state) {
307                         if (state.gps != null && state.gps.connected && state.gps.lat != AltosLib.MISSING)
308                                 show(pos(state.gps.lat,"N", "S"));
309                         else
310                                 show("???");
311                 }
312                 public Lat (GridBagLayout layout, int x, int y) {
313                         super (layout, x, y, "Latitude");
314                 }
315         }
316
317         Lat lat;
318
319         class Lon extends DescentValue {
320                 void show (AltosState state, AltosListenerState listener_state) {
321                         if (state.gps != null && state.gps.connected && state.gps.lon != AltosLib.MISSING)
322                                 show(pos(state.gps.lon,"W", "E"));
323                         else
324                                 show("???");
325                 }
326                 public Lon (GridBagLayout layout, int x, int y) {
327                         super (layout, x, y, "Longitude");
328                 }
329         }
330
331         Lon lon;
332
333         class Distance extends DescentValue {
334                 void show(AltosState state, AltosListenerState listener_state) {
335                         if (state.from_pad != null)
336                                 show(state.from_pad.distance);
337                         else
338                                 show("???");
339                 }
340
341                 public Distance (GridBagLayout layout, int x, int y) {
342                         super(layout, x, y, AltosConvert.distance, "Ground Distance");
343                 }
344         }
345
346         Distance distance;
347
348
349         class Apogee extends DescentStatus {
350                 void show (AltosState state, AltosListenerState listener_state) {
351                         show("%4.2f V", state.apogee_voltage);
352                         lights.set(state.apogee_voltage >= AltosLib.ao_igniter_good);
353                 }
354                 public Apogee (GridBagLayout layout, int y) {
355                         super(layout, y, "Apogee Igniter Voltage");
356                 }
357         }
358
359         Apogee apogee;
360
361         class Main extends DescentStatus {
362                 void show (AltosState state, AltosListenerState listener_state) {
363                         show("%4.2f V", state.main_voltage);
364                         lights.set(state.main_voltage >= AltosLib.ao_igniter_good);
365                 }
366                 public Main (GridBagLayout layout, int y) {
367                         super(layout, y, "Main Igniter Voltage");
368                 }
369         }
370
371         Main main;
372
373         class Bearing extends DescentDualValue {
374                 void show (AltosState state, AltosListenerState listener_state) {
375                         if (state.from_pad != null) {
376                                 show( String.format("%3.0f°", state.from_pad.bearing),
377                                       state.from_pad.bearing_words(
378                                               AltosGreatCircle.BEARING_LONG));
379                         } else {
380                                 show("???", "???");
381                         }
382                 }
383                 public Bearing (GridBagLayout layout, int x, int y) {
384                         super (layout, x, y, "Bearing");
385                 }
386         }
387
388         Bearing bearing;
389
390         class Range extends DescentValue {
391                 void show (AltosState state, AltosListenerState listener_state) {
392                         show(state.range);
393                 }
394                 public Range (GridBagLayout layout, int x, int y) {
395                         super (layout, x, y, AltosConvert.distance, "Range");
396                 }
397         }
398
399         Range range;
400
401         class Elevation extends DescentValue {
402                 void show (AltosState state, AltosListenerState listener_state) {
403                         show("%3.0f°", state.elevation);
404                 }
405                 public Elevation (GridBagLayout layout, int x, int y) {
406                         super (layout, x, y, "Elevation");
407                 }
408         }
409
410         Elevation elevation;
411
412         public void reset() {
413                 lat.reset();
414                 lon.reset();
415                 height.reset();
416                 speed.reset();
417                 bearing.reset();
418                 range.reset();
419                 distance.reset();
420                 elevation.reset();
421                 main.reset();
422                 apogee.reset();
423         }
424
425         public void font_size_changed(int font_size) {
426                 lat.font_size_changed(font_size);
427                 lon.font_size_changed(font_size);
428                 height.font_size_changed(font_size);
429                 speed.font_size_changed(font_size);
430                 bearing.font_size_changed(font_size);
431                 range.font_size_changed(font_size);
432                 distance.font_size_changed(font_size);
433                 elevation.font_size_changed(font_size);
434                 main.font_size_changed(font_size);
435                 apogee.font_size_changed(font_size);
436         }
437
438         public void units_changed(boolean imperial_units) {
439                 lat.units_changed(imperial_units);
440                 lon.units_changed(imperial_units);
441                 height.units_changed(imperial_units);
442                 speed.units_changed(imperial_units);
443                 bearing.units_changed(imperial_units);
444                 range.units_changed(imperial_units);
445                 distance.units_changed(imperial_units);
446                 elevation.units_changed(imperial_units);
447                 main.units_changed(imperial_units);
448                 apogee.units_changed(imperial_units);
449         }
450
451         public void show(AltosState state, AltosListenerState listener_state) {
452                 height.show(state, listener_state);
453                 speed.show(state, listener_state);
454                 if (state.gps != null && state.gps.connected) {
455                         bearing.show(state, listener_state);
456                         range.show(state, listener_state);
457                         distance.show(state, listener_state);
458                         elevation.show(state, listener_state);
459                         lat.show(state, listener_state);
460                         lon.show(state, listener_state);
461                 } else {
462                         bearing.hide();
463                         range.hide();
464                         distance.hide();
465                         elevation.hide();
466                         lat.hide();
467                         lon.hide();
468                 }
469                 if (state.main_voltage != AltosLib.MISSING)
470                         main.show(state, listener_state);
471                 else
472                         main.hide();
473                 if (state.apogee_voltage != AltosLib.MISSING)
474                         apogee.show(state, listener_state);
475                 else
476                         apogee.hide();
477         }
478
479         public String getName() {
480                 return "Descent";
481         }
482
483         public AltosDescent() {
484                 layout = new GridBagLayout();
485
486                 setLayout(layout);
487
488                 /* Elements in descent display */
489                 speed = new Speed(layout, 0, 0);
490                 height = new Height(layout, 2, 0);
491                 elevation = new Elevation(layout, 0, 1);
492                 range = new Range(layout, 2, 1);
493                 bearing = new Bearing(layout, 0, 2);
494                 distance = new Distance(layout, 0, 3);
495                 lat = new Lat(layout, 0, 4);
496                 lon = new Lon(layout, 2, 4);
497
498                 apogee = new Apogee(layout, 5);
499                 main = new Main(layout, 6);
500         }
501 }