Add telem parsing code
authorKeith Packard <keithp@keithp.com>
Fri, 2 Apr 2010 20:37:52 +0000 (13:37 -0700)
committerKeith Packard <keithp@keithp.com>
Fri, 2 Apr 2010 20:37:52 +0000 (13:37 -0700)
ao-tools/altosui/AltosSerial.java
ao-tools/altosui/AltosTelemetry.java
ao-tools/altosui/AltosUI.java
ao-tools/altosui/Makefile

index 82663eab51d2a5d6711b352b354636538a5ea193..9537f19073074df88e03736285b03a9962c0134a 100644 (file)
 
 package altosui;
 
-import java.lang.String;
-import java.lang.System;
-import java.lang.Character;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.FileNotFoundException;
+import java.lang.*;
+import java.io.*;
 import java.util.concurrent.LinkedBlockingQueue;
-import java.lang.InterruptedException;
 import java.util.LinkedList;
-import altosui.AltosSerialMonitor;
 import java.util.Iterator;
+import altosui.AltosSerialMonitor;
 
 /*
  * This class reads from the serial port and places each received
@@ -43,6 +37,7 @@ class AltosSerialReader implements Runnable {
        FileInputStream serial_in;
        LinkedBlockingQueue<String> monitor_queue;
        LinkedBlockingQueue<String> reply_queue;
+       Thread input_thread;
        String line;
 
        public void run () {
@@ -71,20 +66,12 @@ class AltosSerialReader implements Runnable {
                }
        }
 
-       public String get_telem() {
-               try {
-                       return monitor_queue.take();
-               } catch (InterruptedException e) {
-                       return "";
-               }
+       public String get_telem() throws InterruptedException {
+               return monitor_queue.take();
        }
 
-       public String get_reply() {
-               try {
-                       return reply_queue.take();
-               } catch (InterruptedException e) {
-                       return "";
-               }
+       public String get_reply() throws InterruptedException {
+               return reply_queue.take();
        }
 
        public void flush () {
@@ -94,56 +81,131 @@ class AltosSerialReader implements Runnable {
                        reply_queue.clear();
                }
        }
-       public AltosSerialReader (FileInputStream in) {
-               serial_in = in;
+
+       public boolean opened() {
+               return serial_in != null;
+       }
+
+       public void close() {
+               if (serial_in != null) {
+                       try {
+                               serial_in.close();
+                       } catch (IOException e) {
+                       }
+                       serial_in = null;
+               }
+               if (input_thread != null) {
+                       try {
+                               input_thread.join();
+                       } catch (InterruptedException e) {
+                       }
+                       input_thread = null;
+               }
+       }
+
+       public void open(File name) throws FileNotFoundException {
+               close();
+               serial_in = new FileInputStream(name);
+               input_thread = new Thread(this);
+               input_thread.start();
+       }
+       public AltosSerialReader () {
+               serial_in = null;
+               input_thread = null;
+               line = "";
                monitor_queue = new LinkedBlockingQueue<String> ();
                reply_queue = new LinkedBlockingQueue<String> ();
-               line = "";
        }
 
 }
 
 public class AltosSerial implements Runnable {
-       FileInputStream serial_in = null;
        FileOutputStream serial_out = null;
-       AltosSerialReader reader;
+       Thread monitor_thread = null;
+       AltosSerialReader reader = null;
        LinkedList<AltosSerialMonitor> callbacks;
 
        public void run() {
-               for (;;) {
-                       String s = reader.get_reply();
-                       synchronized(callbacks) {
-                               Iterator<AltosSerialMonitor> i = callbacks.iterator();
-                               while (i.hasNext()) {
-                                       i.next().data(s);
+               try {
+                       for (;;) {
+                               String s = reader.get_telem();
+                               synchronized(callbacks) {
+                                       Iterator<AltosSerialMonitor> i = callbacks.iterator();
+                                       while (i.hasNext()) {
+                                               i.next().data(s);
+                                       }
                                }
                        }
+               } catch (InterruptedException e) {
                }
        }
 
-       public void start () {
-               try {
-                       serial_out.write('?');
-                       serial_out.write('\r');
-               } catch (IOException e) {
+       boolean need_monitor() {
+               return reader.opened() && !callbacks.isEmpty();
+       }
+
+       void maybe_stop_monitor() {
+               if (!need_monitor() && monitor_thread != null) {
+                       monitor_thread.interrupt();
+                       try {
+                               monitor_thread.join();
+                       } catch (InterruptedException e) {
+                       } finally {
+                               monitor_thread = null;
+                       }
+               }
+       }
+
+       void maybe_start_monitor() {
+               if (need_monitor() && monitor_thread == null) {
+                       monitor_thread = new Thread(this);
+                       monitor_thread.start();
                }
-               (new Thread(reader)).start();
-               (new Thread(this)).start();
        }
 
        public void monitor(AltosSerialMonitor monitor) {
                synchronized(callbacks) {
                        callbacks.add(monitor);
+                       maybe_start_monitor();
                }
        }
 
-       public AltosSerial(String serial_name) {
+
+       public void unmonitor(AltosSerialMonitor monitor) {
+               synchronized(callbacks) {
+                       callbacks.remove(monitor);
+                       maybe_stop_monitor();
+               }
+       }
+
+       public void close() {
+               synchronized(callbacks) {
+                       reader.close();
+                       maybe_stop_monitor();
+               }
+       }
+
+       public void open(File serial_name) throws FileNotFoundException {
+               reader.open(serial_name);
+               serial_out = new FileOutputStream(serial_name);
                try {
-                       serial_in = new FileInputStream(serial_name);
-                       serial_out = new FileOutputStream(serial_name);
-                       reader = new AltosSerialReader(serial_in);
-                       callbacks = new LinkedList<AltosSerialMonitor>();
-               } catch (FileNotFoundException e) {
+                       serial_out.write('?');
+                       serial_out.write('\r');
+               } catch (IOException e) {
                }
        }
+
+       void init() {
+               reader = new AltosSerialReader();
+               callbacks = new LinkedList<AltosSerialMonitor>();
+       }
+
+       public AltosSerial() {
+               init();
+       }
+
+       public AltosSerial(File serial_name) throws FileNotFoundException {
+               init();
+               open(serial_name);
+       }
 }
index e072bb347394186491bcd93d3b3e3136f7f88d62..99e82bbff1432a139560276f75681407247f23ae 100644 (file)
@@ -36,25 +36,21 @@ class AltosGPSTime {
                try {
                        return Integer.parseInt(v);
                } catch (NumberFormatException e) {
-                       throw new ParseException(v, 0);
+                       throw new ParseException("error parsing GPS value " + v, 0);
                }
        }
 
        public AltosGPSTime(String date, String time) throws ParseException {
                String[] ymd = date.split("-");
-               if (ymd.length != 3) {
-                       System.out.println("Error parsing GPS date " + date + " got " + ymd.length);
-                       throw new ParseException(date, 0);
-               }
+               if (ymd.length != 3)
+                       throw new ParseException("error parsing GPS date " + date + " got " + ymd.length, 0);
                year = parse_int(ymd[0]);
                month = parse_int(ymd[1]);
                day = parse_int(ymd[2]);
 
                String[] hms = time.split(":");
-               if (hms.length != 3) {
-                       System.out.println("Error parsing GPS time " + time + " got " + hms.length);
-                       throw new ParseException(time, 0);
-               }
+               if (hms.length != 3)
+                       throw new ParseException("Error parsing GPS time " + time + " got " + hms.length, 0);
                hour = parse_int(hms[0]);
                minute = parse_int(hms[1]);
                second = parse_int(hms[2]);
@@ -95,6 +91,29 @@ class AltosGPSTracking {
        AltosGPSSat[]           cc_gps_sat;
 }
 
+/*
+ * The telemetry data stream is a bit of a mess at present, with no consistent
+ * formatting. In particular, the GPS data is formatted for viewing instead of parsing.
+ * However, the key feature is that every telemetry line contains all of the information
+ * necessary to describe the current rocket state, including the calibration values
+ * for accelerometer and barometer.
+ *
+ * GPS unlocked:
+ *
+ * VERSION 2 CALL KB0G SERIAL  51 FLIGHT     2 RSSI  -68 STATUS ff STATE     pad  1001 \
+ *    a: 16032 p: 21232 t: 20284 v: 25160 d:   204 m:   204 fa: 16038 ga: 16032 fv:       0 \
+ *    fp: 21232 gp: 21230 a+: 16049 a-: 16304 GPS  0 sat unlocked SAT 1   15  30
+ *
+ * GPS locked:
+ *
+ * VERSION 2 CALL KB0G SERIAL  51 FLIGHT     2 RSSI  -71 STATUS ff STATE     pad  2504 \
+ *     a: 16028 p: 21220 t: 20360 v: 25004 d:   208 m:   200 fa: 16031 ga: 16032 fv:     330 \
+ *     fp: 21231 gp: 21230 a+: 16049 a-: 16304 \
+ *     GPS  9 sat 2010-02-13 17:16:51 35°20.0803'N 106°45.2235'W  1790m  \
+ *     0.00m/s(H) 0°     0.00m/s(V) 1.0(hdop)     0(herr)     0(verr) \
+ *     SAT 10   29  30  24  28   5  25  21  20  15  33   1  23  30  24  18  26  10  29   2  26
+ */
+
 public class AltosTelemetry {
        int     version;
        String  callsign;
@@ -124,8 +143,7 @@ public class AltosTelemetry {
                try {
                        return Integer.parseInt(v);
                } catch (NumberFormatException e) {
-                       System.out.println("error parsing int " + v);
-                       throw new ParseException(v, 0);
+                       throw new ParseException("error parsing int " + v, 0);
                }
        }
 
@@ -133,8 +151,7 @@ public class AltosTelemetry {
                try {
                        return Integer.parseInt(v, 16);
                } catch (NumberFormatException e) {
-                       System.out.println("error parsing hex " + v);
-                       throw new ParseException(v, 0);
+                       throw new ParseException("error parsing hex " + v, 0);
                }
        }
 
@@ -142,8 +159,7 @@ public class AltosTelemetry {
                try {
                        return Double.parseDouble(v);
                } catch (NumberFormatException e) {
-                       System.out.println("error parsing double " + v);
-                       throw new ParseException(v, 0);
+                       throw new ParseException("error parsing double " + v, 0);
                }
        }
 
@@ -151,8 +167,7 @@ public class AltosTelemetry {
                String[]        dsf = coord.split("\\D+");
 
                if (dsf.length != 3) {
-                       System.out.println("error parsing coord " + coord);
-                       throw new ParseException(coord, 0);
+                       throw new ParseException("error parsing coord " + coord, 0);
                }
                int deg = parse_int(dsf[0]);
                int min = parse_int(dsf[1]);
@@ -172,8 +187,7 @@ public class AltosTelemetry {
 
        void word(String v, String m) throws ParseException {
                if (!v.equals(m)) {
-                       System.out.println("error matching '" + v + "' '" + m + "'");
-                       throw new ParseException(v, 0);
+                       throw new ParseException("error matching '" + v + "' '" + m + "'", 0);
                }
        }
 
index b731725c6c3e1797d74ae37ccc6153949ecbc295..89eaac156b17d34f5c3b39d02191316536b7357e 100644 (file)
 
 package altosui;
 
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.Toolkit;
-import java.awt.Window;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import javax.swing.JFrame;
-import javax.swing.JMenu;
-import javax.swing.JMenuBar;
-import javax.swing.JMenuItem;
-import javax.swing.JRadioButtonMenuItem;
-import javax.swing.JSplitPane;
-import javax.swing.JTable;
-import javax.swing.KeyStroke;
-import javax.swing.table.TableCellEditor;
-import javax.swing.table.DefaultTableCellRenderer;
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import gnu.io.CommPortIdentifier;
+
 import altosui.AltosSerial;
 import altosui.AltosSerialMonitor;
 
@@ -71,13 +56,10 @@ public class AltosUI extends JFrame {
 
                createMenu();
 
-               serialLine = new AltosSerial("/dev/ttyACM0");
+               serialLine = new AltosSerial();
                serialLine.monitor(new AltosUIMonitor());
-               serialLine.start();
-               Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
-               size.width = size.width*9/10;
-               size.height = size.height*9/10;
-               this.setSize(size);
+               int dpi = Toolkit.getDefaultToolkit().getScreenResolution();
+               this.setSize(new Dimension (dpi * 5, dpi * 4));
                this.validate();
                setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
                addWindowListener(new WindowAdapter() {
@@ -88,6 +70,94 @@ public class AltosUI extends JFrame {
                });
        }
 
+       final JFileChooser deviceChooser = new JFileChooser();
+
+       private void PickSerialDevice() {
+               java.util.Enumeration<CommPortIdentifier> port_list = CommPortIdentifier.getPortIdentifiers();
+               while (port_list.hasMoreElements()) {
+                       CommPortIdentifier identifier = port_list.nextElement();
+                       System.out.println("Serial port " + identifier.getName());
+               }
+       }
+
+       private void ConnectToDevice() {
+               PickSerialDevice();
+               int returnVal = deviceChooser.showOpenDialog(AltosUI.this);
+
+               if (returnVal == JFileChooser.APPROVE_OPTION) {
+                       File file = deviceChooser.getSelectedFile();
+                       try {
+                               serialLine.open(file);
+                       } catch (FileNotFoundException ee) {
+                               JOptionPane.showMessageDialog(AltosUI.this,
+                                                             file.getName(),
+                                                             "Cannot open serial port",
+                                                             JOptionPane.ERROR_MESSAGE);
+                       }
+               }
+       }
+
+       String readline(FileInputStream s) throws IOException {
+               int c;
+               String  line = "";
+
+               while ((c = s.read()) != -1) {
+                       if (c == '\r')
+                               continue;
+                       if (c == '\n')
+                               return line;
+                       line = line + (char) c;
+               }
+               return null;
+       }
+
+       private void Replay() {
+//             int returnVal = deviceChooser.showOpenDialog(AltosUI.this);
+
+               /*              if (returnVal == JFileChooser.APPROVE_OPTION) */ {
+//                     File file = deviceChooser.getSelectedFile();
+//                     String  filename = file.getName();
+                       String  filename = "/home/keithp/src/cc1111/flights/2010-02-13-serial-051-flight-002.telem";
+                       try {
+//                             FileInputStream replay = new FileInputStream(file);
+                               FileInputStream replay = new FileInputStream(filename);
+                               String  line;
+
+                               try {
+                                       while ((line = readline(replay)) != null) {
+                                               try {
+                                                       AltosTelemetry  t = new AltosTelemetry(line);
+                                                       System.out.println ("Version " + t.version + t.callsign);
+                                               } catch (ParseException pp) {
+                                                       JOptionPane.showMessageDialog(AltosUI.this,
+                                                                                     line,
+                                                                                     "error parsing",
+                                                                                     JOptionPane.ERROR_MESSAGE);
+                                                       break;
+                                               }
+                                       }
+                               } catch (IOException ee) {
+                                       JOptionPane.showMessageDialog(AltosUI.this,
+                                                                     filename,
+                                                                     "error reading",
+                                                                     JOptionPane.ERROR_MESSAGE);
+                               } finally {
+                                       try {
+                                               replay.close();
+                                       } catch (IOException e) {}
+                               }
+                       } catch (FileNotFoundException ee) {
+                               JOptionPane.showMessageDialog(AltosUI.this,
+                                                             filename,
+                                                             "Cannot open serial port",
+                                                             JOptionPane.ERROR_MESSAGE);
+                       }
+               }
+       }
+
+       private void SaveFlightData() {
+       }
+
        private void createMenu() {
                JMenuBar menubar = new JMenuBar();
                JMenu menu;
@@ -120,6 +190,7 @@ public class AltosUI extends JFrame {
                        item = new JMenuItem("Connect to Device",KeyEvent.VK_C);
                        item.addActionListener(new ActionListener() {
                                        public void actionPerformed(ActionEvent e) {
+                                               ConnectToDevice();
                                        }
                                });
                        menu.add(item);
@@ -127,6 +198,7 @@ public class AltosUI extends JFrame {
                        item = new JMenuItem("Disconnect from Device",KeyEvent.VK_D);
                        item.addActionListener(new ActionListener() {
                                        public void actionPerformed(ActionEvent e) {
+                                               serialLine.close();
                                        }
                                });
                        menu.add(item);
@@ -136,6 +208,7 @@ public class AltosUI extends JFrame {
                        item = new JMenuItem("Save Flight Data",KeyEvent.VK_S);
                        item.addActionListener(new ActionListener() {
                                        public void actionPerformed(ActionEvent e) {
+                                               SaveFlightData();
                                        }
                                });
                        menu.add(item);
@@ -143,6 +216,7 @@ public class AltosUI extends JFrame {
                        item = new JMenuItem("Replay",KeyEvent.VK_R);
                        item.addActionListener(new ActionListener() {
                                        public void actionPerformed(ActionEvent e) {
+                                               Replay();
                                        }
                                });
                        menu.add(item);
@@ -186,16 +260,21 @@ public class AltosUI extends JFrame {
                        menu = new JMenu("Channel", true);
                        menu.setMnemonic(KeyEvent.VK_C);
                        menubar.add(menu);
+                       ButtonGroup group = new ButtonGroup();
 
                        for (int c = 0; c <= 9; c++) {
-                               radioitem = new JRadioButtonMenuItem("Channel " + c + " (" +
-                                                               (434.550 + c * .1) + ")",
-                                                               c == 0);
+                               radioitem = new JRadioButtonMenuItem(String.format("Channel %1d (%7.3fMHz)", c,
+                                                                                  434.550 + c * 0.1),
+                                                                    c == 0);
+                               radioitem.setActionCommand(String.format("%d", c));
                                radioitem.addActionListener(new ActionListener() {
                                                public void actionPerformed(ActionEvent e) {
+                                                       System.out.println("Command: " + e.getActionCommand() + " param: " +
+                                                                          e.paramString());
                                                }
                                        });
                                menu.add(radioitem);
+                               group.add(radioitem);
                        }
                }
 
index cb422df8c1955f046c13047bac28069b155c5261..090911ef06f26f35c445bb2b4e136aa065fde984 100644 (file)
@@ -1,13 +1,18 @@
 .SUFFIXES: .java .class
 
-CLASSPATH=..
-CLASSFILES=AltosSerialMonitor.class AltosSerial.class AltosUI.class
+CLASSPATH=..:/usr/share/java/*
+CLASSFILES=AltosSerialMonitor.class AltosSerial.class AltosTelemetry.class AltosUI.class
 JAVAFLAGS=-Xlint:unchecked
 
-all: $(CLASSFILES)
+all: $(CLASSFILES) altosui
 
 .java.class:
-       javac -cp $(CLASSPATH) $(JAVAFLAGS) $*.java
+       javac -cp "$(CLASSPATH)" $(JAVAFLAGS) $*.java
+
+altosui: Makefile
+       (echo '#!/bin/sh'; \
+       echo exec java -cp '"$(CLASSPATH)"' altosui/AltosUI) > $@
+       chmod +x $@
 
 clean:
        rm -f *.class
\ No newline at end of file