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
FileInputStream serial_in;
LinkedBlockingQueue<String> monitor_queue;
LinkedBlockingQueue<String> reply_queue;
+ Thread input_thread;
String line;
public void run () {
}
}
- 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 () {
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);
+ }
}
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]);
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;
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);
}
}
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);
}
}
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);
}
}
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]);
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);
}
}
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;
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() {
});
}
+ 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;
item = new JMenuItem("Connect to Device",KeyEvent.VK_C);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
+ ConnectToDevice();
}
});
menu.add(item);
item = new JMenuItem("Disconnect from Device",KeyEvent.VK_D);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
+ serialLine.close();
}
});
menu.add(item);
item = new JMenuItem("Save Flight Data",KeyEvent.VK_S);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
+ SaveFlightData();
}
});
menu.add(item);
item = new JMenuItem("Replay",KeyEvent.VK_R);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
+ Replay();
}
});
menu.add(item);
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);
}
}
.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