diff options
| -rw-r--r-- | ao-tools/altosui/AltosSerial.java | 152 | ||||
| -rw-r--r-- | ao-tools/altosui/AltosTelemetry.java | 52 | ||||
| -rw-r--r-- | ao-tools/altosui/AltosUI.java | 143 | ||||
| -rw-r--r-- | ao-tools/altosui/Makefile | 13 | 
4 files changed, 260 insertions, 100 deletions
| diff --git a/ao-tools/altosui/AltosSerial.java b/ao-tools/altosui/AltosSerial.java index 82663eab..9537f190 100644 --- a/ao-tools/altosui/AltosSerial.java +++ b/ao-tools/altosui/AltosSerial.java @@ -21,18 +21,12 @@  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); +	}  } diff --git a/ao-tools/altosui/AltosTelemetry.java b/ao-tools/altosui/AltosTelemetry.java index e072bb34..99e82bbf 100644 --- a/ao-tools/altosui/AltosTelemetry.java +++ b/ao-tools/altosui/AltosTelemetry.java @@ -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);  		}  	} diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index b731725c..89eaac15 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -17,29 +17,14 @@  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);  			}  		} diff --git a/ao-tools/altosui/Makefile b/ao-tools/altosui/Makefile index cb422df8..090911ef 100644 --- a/ao-tools/altosui/Makefile +++ b/ao-tools/altosui/Makefile @@ -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 | 
