altosui: Move AltosGreatCircle.java to altoslib
[fw/altos] / altosui / AltosIdleMonitorUI.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.*;
30 import org.altusmetrum.AltosLib.*;
31
32 class AltosADC {
33         int     tick;
34         int     accel;
35         int     pres;
36         int     temp;
37         int     batt;
38         int     drogue;
39         int     main;
40
41         public AltosADC(AltosSerial serial) throws InterruptedException, TimeoutException {
42                 serial.printf("a\n");
43                 for (;;) {
44                         String line = serial.get_reply_no_dialog(5000);
45                         if (line == null) {
46                                 throw new TimeoutException();
47                         }
48                         if (!line.startsWith("tick:"))
49                                 continue;
50                         String[] items = line.split("\\s+");
51                         for (int i = 0; i < items.length;) {
52                                 if (items[i].equals("tick:")) {
53                                         tick = Integer.parseInt(items[i+1]);
54                                         i += 2;
55                                         continue;
56                                 }
57                                 if (items[i].equals("accel:")) {
58                                         accel = Integer.parseInt(items[i+1]);
59                                         i += 2;
60                                         continue;
61                                 }
62                                 if (items[i].equals("pres:")) {
63                                         pres = Integer.parseInt(items[i+1]);
64                                         i += 2;
65                                         continue;
66                                 }
67                                 if (items[i].equals("temp:")) {
68                                         temp = Integer.parseInt(items[i+1]);
69                                         i += 2;
70                                         continue;
71                                 }
72                                 if (items[i].equals("batt:")) {
73                                         batt = Integer.parseInt(items[i+1]);
74                                         i += 2;
75                                         continue;
76                                 }
77                                 if (items[i].equals("drogue:")) {
78                                         drogue = Integer.parseInt(items[i+1]);
79                                         i += 2;
80                                         continue;
81                                 }
82                                 if (items[i].equals("main:")) {
83                                         main = Integer.parseInt(items[i+1]);
84                                         i += 2;
85                                         continue;
86                                 }
87                         }
88                         break;
89                 }
90         }
91 }
92
93 class AltosGPSQuery extends AltosGPS {
94         public AltosGPSQuery (AltosSerial serial) throws TimeoutException, InterruptedException {
95                 serial.printf("g\n");
96                 for (;;) {
97                         String line = serial.get_reply_no_dialog(5000);
98                         if (line == null)
99                                 throw new TimeoutException();
100                         String[] bits = line.split("\\s+");
101                         if (bits.length == 0)
102                                 continue;
103                         if (line.startsWith("Date:")) {
104                                 if (bits.length < 2)
105                                         continue;
106                                 String[] d = bits[1].split(":");
107                                 if (d.length < 3)
108                                         continue;
109                                 year = Integer.parseInt(d[0]) + 2000;
110                                 month = Integer.parseInt(d[1]);
111                                 day = Integer.parseInt(d[2]);
112                                 continue;
113                         }
114                         if (line.startsWith("Time:")) {
115                                 if (bits.length < 2)
116                                         continue;
117                                 String[] d = bits[1].split("/");
118                                 if (d.length < 3)
119                                         continue;
120                                 hour = Integer.parseInt(d[0]);
121                                 minute = Integer.parseInt(d[1]);
122                                 second = Integer.parseInt(d[2]);
123                                 continue;
124                         }
125                         if (line.startsWith("Lat/Lon:")) {
126                                 if (bits.length < 3)
127                                         continue;
128                                 lat = Integer.parseInt(bits[1]) * 1.0e-7;
129                                 lon = Integer.parseInt(bits[2]) * 1.0e-7;
130                                 continue;
131                         }
132                         if (line.startsWith("Alt:")) {
133                                 if (bits.length < 2)
134                                         continue;
135                                 alt = Integer.parseInt(bits[1]);
136                                 continue;
137                         }
138                         if (line.startsWith("Flags:")) {
139                                 if (bits.length < 2)
140                                         continue;
141                                 int status = Integer.decode(bits[1]);
142                                 connected = (status & Altos.AO_GPS_RUNNING) != 0;
143                                 locked = (status & Altos.AO_GPS_VALID) != 0;
144                                 continue;
145                         }
146                         if (line.startsWith("Sats:")) {
147                                 if (bits.length < 2)
148                                         continue;
149                                 nsat = Integer.parseInt(bits[1]);
150                                 cc_gps_sat = new AltosGPSSat[nsat];
151                                 for (int i = 0; i < nsat; i++) {
152                                         int     svid = Integer.parseInt(bits[2+i*2]);
153                                         int     cc_n0 = Integer.parseInt(bits[3+i*2]);
154                                         cc_gps_sat[i] = new AltosGPSSat(svid, cc_n0);
155                                 }
156                         }
157                         if (line.startsWith("done"))
158                                 break;
159                         if (line.startsWith("Syntax error"))
160                                 break;
161                 }
162         }
163 }
164
165 class AltosIdleMonitor extends Thread {
166         AltosDevice             device;
167         AltosSerial             serial;
168         AltosIdleMonitorUI      ui;
169         AltosState              state;
170         boolean                 remote;
171         double                  frequency;
172         AltosState              previous_state;
173         AltosConfigData         config_data;
174         AltosADC                adc;
175         AltosGPS                gps;
176
177         void update_state() throws InterruptedException, TimeoutException {
178                 AltosRecord     record = new AltosRecord();
179
180                 try {
181                         if (remote) {
182                                 serial.set_radio_frequency(frequency);
183                                 serial.start_remote();
184                         } else
185                                 serial.flush_input();
186                         config_data = new AltosConfigData(serial);
187                         adc = new AltosADC(serial);
188                         gps = new AltosGPSQuery(serial);
189                 } finally {
190                         if (remote)
191                                 serial.stop_remote();
192                 }
193
194                 record.version = 0;
195                 record.callsign = config_data.callsign;
196                 record.serial = config_data.serial;
197                 record.flight = config_data.log_available() > 0 ? 255 : 0;
198                 record.rssi = 0;
199                 record.status = 0;
200                 record.state = Altos.ao_flight_idle;
201
202                 record.tick = adc.tick;
203                 record.accel = adc.accel;
204                 record.pres = adc.pres;
205                 record.batt = adc.batt;
206                 record.temp = adc.temp;
207                 record.drogue = adc.drogue;
208                 record.main = adc.main;
209
210                 record.ground_accel = record.accel;
211                 record.ground_pres = record.pres;
212                 record.accel_plus_g = config_data.accel_cal_plus;
213                 record.accel_minus_g = config_data.accel_cal_minus;
214                 record.acceleration = 0;
215                 record.speed = 0;
216                 record.height = 0;
217                 record.gps = gps;
218                 state = new AltosState (record, state);
219         }
220
221         void set_frequency(double in_frequency) {
222                 frequency = in_frequency;
223         }
224
225         public void post_state() {
226                 Runnable r = new Runnable() {
227                                 public void run() {
228                                         ui.update(state);
229                                 }
230                         };
231                 SwingUtilities.invokeLater(r);
232         }
233
234         public void run() {
235                 try {
236                         for (;;) {
237                                 try {
238                                         update_state();
239                                         post_state();
240                                 } catch (TimeoutException te) {
241                                 }
242                                 Thread.sleep(1000);
243                         }
244                 } catch (InterruptedException ie) {
245                         serial.close();
246                 }
247         }
248
249         public AltosIdleMonitor(AltosIdleMonitorUI in_ui, AltosDevice in_device, boolean in_remote)
250                 throws FileNotFoundException, AltosSerialInUseException, InterruptedException, TimeoutException {
251                 device = in_device;
252                 ui = in_ui;
253                 serial = new AltosSerial(device);
254                 remote = in_remote;
255                 state = null;
256         }
257 }
258
259 public class AltosIdleMonitorUI extends AltosFrame implements AltosFlightDisplay, AltosFontListener {
260         AltosDevice             device;
261         JTabbedPane             pane;
262         AltosPad                pad;
263         AltosInfoTable          flightInfo;
264         AltosFlightStatus       flightStatus;
265         AltosIdleMonitor        thread;
266         int                     serial;
267         boolean                 remote;
268
269         void stop_display() {
270                 if (thread != null && thread.isAlive()) {
271                         thread.interrupt();
272                         try {
273                                 thread.join();
274                         } catch (InterruptedException ie) {}
275                 }
276                 thread = null;
277         }
278
279         void disconnect() {
280                 stop_display();
281         }
282
283         public void reset() {
284                 pad.reset();
285                 flightInfo.clear();
286         }
287
288         public void set_font() {
289                 pad.set_font();
290                 flightInfo.set_font();
291         }
292
293         public void font_size_changed(int font_size) {
294                 set_font();
295         }
296
297         public void show(AltosState state, int crc_errors) {
298                 try {
299                         pad.show(state, crc_errors);
300                         flightStatus.show(state, crc_errors);
301                         flightInfo.show(state, crc_errors);
302                 } catch (Exception e) {
303                         System.out.print("Show exception" + e);
304                 }
305         }
306
307         public void update(AltosState state) {
308                 show (state, 0);
309         }
310
311         Container       bag;
312         AltosFreqList   frequencies;
313
314         public AltosIdleMonitorUI(JFrame in_owner)
315                 throws FileNotFoundException, AltosSerialInUseException, TimeoutException, InterruptedException {
316
317                 device = AltosDeviceDialog.show(in_owner, Altos.product_any);
318                 remote = false;
319                 if (!device.matchProduct(Altos.product_telemetrum))
320                         remote = true;
321
322                 serial = device.getSerial();
323                 bag = getContentPane();
324                 bag.setLayout(new GridBagLayout());
325
326                 GridBagConstraints c = new GridBagConstraints();
327
328                 java.net.URL imgURL = AltosUI.class.getResource("/altus-metrum-16x16.jpg");
329                 if (imgURL != null)
330                         setIconImage(new ImageIcon(imgURL).getImage());
331
332                 setTitle(String.format("AltOS %s", device.toShortString()));
333
334                 /* Stick frequency selector at top of table for telemetry monitoring */
335                 if (remote && serial >= 0) {
336                         // Frequency menu
337                         frequencies = new AltosFreqList(AltosUIPreferences.frequency(serial));
338                         frequencies.addActionListener(new ActionListener() {
339                                         public void actionPerformed(ActionEvent e) {
340                                                 double frequency = frequencies.frequency();
341                                                 thread.set_frequency(frequency);
342                                                 AltosUIPreferences.set_frequency(device.getSerial(),
343                                                                                frequency);
344                                         }
345                         });
346                         c.gridx = 0;
347                         c.gridy = 0;
348                         c.insets = new Insets(3, 3, 3, 3);
349                         c.anchor = GridBagConstraints.WEST;
350                         bag.add (frequencies, c);
351                 }
352
353
354                 /* Flight status is always visible */
355                 flightStatus = new AltosFlightStatus();
356                 c.gridx = 0;
357                 c.gridy = 1;
358                 c.fill = GridBagConstraints.HORIZONTAL;
359                 c.weightx = 1;
360                 c.gridwidth = 2;
361                 bag.add(flightStatus, c);
362                 c.gridwidth = 1;
363
364                 /* The rest of the window uses a tabbed pane to
365                  * show one of the alternate data views
366                  */
367                 pane = new JTabbedPane();
368
369                 pad = new AltosPad();
370                 pane.add("Launch Pad", pad);
371
372                 flightInfo = new AltosInfoTable();
373                 pane.add("Table", new JScrollPane(flightInfo));
374
375                 /* Make the tabbed pane use the rest of the window space */
376                 c.gridx = 0;
377                 c.gridy = 2;
378                 c.fill = GridBagConstraints.BOTH;
379                 c.weightx = 1;
380                 c.weighty = 1;
381                 c.gridwidth = 2;
382                 bag.add(pane, c);
383
384                 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
385
386                 AltosUIPreferences.register_font_listener(this);
387
388                 addWindowListener(new WindowAdapter() {
389                                 @Override
390                                 public void windowClosing(WindowEvent e) {
391                                         disconnect();
392                                         setVisible(false);
393                                         dispose();
394                                         AltosUIPreferences.unregister_font_listener(AltosIdleMonitorUI.this);
395                                 }
396                         });
397
398                 pack();
399                 setVisible(true);
400
401                 thread = new AltosIdleMonitor(this, device, remote);
402
403                 thread.start();
404         }
405 }