3 import gnu.io.CommPortIdentifier;
4 import gnu.io.PortInUseException;
5 import gnu.io.SerialPort;
6 import gnu.io.UnsupportedCommOperationException;
8 import java.io.FileNotFoundException;
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.io.OutputStream;
12 import java.io.PrintStream;
13 import java.nio.charset.Charset;
14 import java.text.DateFormat;
15 import java.util.ArrayList;
16 import java.util.Date;
17 import java.util.Enumeration;
18 import java.util.TimeZone;
21 * Class to interface the PerfectFlite Alt15K/WD altimeter.
23 * Also includes a main method that retrieves all flight profiles and saves them to files.
25 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
29 public static final int TIMEOUT = 500;
30 public static final int RWDELAY = 5;
32 private static final boolean DEBUG = false;
34 private static final Charset CHARSET = Charset.forName("ISO-8859-1");
36 private final CommPortIdentifier portID;
37 private SerialPort port = null;
38 private InputStream is = null;
39 private OutputStream os = null;
43 @SuppressWarnings("unchecked")
44 public static String[] getNames() {
45 ArrayList<String> list = new ArrayList<String>();;
47 Enumeration pids = CommPortIdentifier.getPortIdentifiers();
49 while (pids.hasMoreElements()) {
50 CommPortIdentifier pid = (CommPortIdentifier) pids.nextElement();
52 if (pid.getPortType() == CommPortIdentifier.PORT_SERIAL)
53 list.add(pid.getName());
55 return list.toArray(new String[0]);
60 @SuppressWarnings("unchecked")
61 public Alt15K(String name) throws IOException {
62 CommPortIdentifier pID = null;
64 Enumeration portIdentifiers = CommPortIdentifier.getPortIdentifiers();
65 while (portIdentifiers.hasMoreElements()) {
66 CommPortIdentifier pid = (CommPortIdentifier) portIdentifiers.nextElement();
68 if(pid.getPortType() == CommPortIdentifier.PORT_SERIAL &&
69 pid.getName().equals(name)) {
76 throw new IOException("Port '"+name+"' not found.");
83 * Get altimeter flight data. The flight profile is chosen by the parameter n,
84 * 0 = latest flight, 1 = second latest, etc.
86 * @param n Which flight profile to use (0=newest, 1=second newest, etc)
87 * @return The altimeter flight data
88 * @throws IOException in case of IOException
89 * @throws PortInUseException in case of PortInUseException
91 public AltData getData(int n) throws IOException, PortInUseException {
92 AltData alt = new AltData();
93 ArrayList<Integer> data = new ArrayList<Integer>();
95 byte[] buf2 = new byte[0];
96 boolean identical = false; // Whether identical lines have been read
99 System.out.println(" Retrieving altimeter data n="+n);
104 // Get version and position data
105 byte[] ver = getVersionData();
106 alt.setVersion(new byte[] { ver[0],ver[1] });
108 // Calculate the position requested
111 int position = ver[2] - n;
116 System.out.println(" Requesting data from position "+position);
120 write((byte)position);
125 // Read preliminary data
127 int msl_level = combine(buf[0],buf[1]);
128 int datacount = combine(buf[2],buf[3]);
131 System.out.println(" Preliminary data msl="+msl_level+" count="+datacount);
133 alt.setMslLevel(msl_level-6000);
134 alt.setDataSamples(datacount);
137 System.out.println(" Retrieving "+datacount+" samples");
139 long t = System.currentTimeMillis();
142 while (count < datacount) {
148 if (buf.length == 17) {
149 // Checksum = sum of all bytes + 1
150 // (signedness does not change the result)
152 for (int i=0; i<16; i++)
154 if (checksum != buf[16]) {
155 printBytes("ERROR: Checksum fail on data (computed="+checksum+
156 " orig="+buf[16]+")",buf);
157 System.out.println("Ignoring error");
160 System.err.println("ERROR: Only "+buf.length+" bytes read, should be 17");
163 for (int i=0; i<buf.length-1; i+=2) {
164 data.add(combine(buf[i],buf[i+1]));
169 * Check whether the data is identical to the previous data batch. If reading
170 * too fast, the data seems to become duplicated in the transfer. We need to check
171 * whether this has happened by attempting to read more data than is normally
174 int c, l=Math.min(buf.length, buf2.length);
175 for (c=0; c<l; c++) {
176 if (buf[c] != buf2[c])
179 if (c==l && buf.length == buf2.length)
185 System.out.println(" Retrieved "+data.size()+" samples in "+
186 (System.currentTimeMillis()-t)+" ms");
189 // In case of identical lines, check for more data. This would mean that the
190 // transfer was corrupted.
192 System.err.println("WARNING: Duplicate data detected, possible error");
195 // Test for more data
197 System.out.println(" Testing for more data");
202 if (buf.length > 0) {
203 System.err.println("ERROR: Data available after transfer! (length="+buf.length+")");
211 // Create an int[] array and set it
212 int[] d = new int[data.size()];
213 for (int i=0; i<d.length; i++)
217 // Catch all exceptions, close the port and re-throw the exception
218 } catch (PortInUseException e) {
221 } catch (IOException e) {
224 } catch (UnsupportedCommOperationException e) {
226 throw new RuntimeException("Required function of RxTx library not supported",e);
227 } catch (RuntimeException e) {
228 // Catch-all for all other types of exceptions
240 private byte[] getVersionData() throws PortInUseException, IOException,
241 UnsupportedCommOperationException {
242 byte[] ver = new byte[3];
246 System.out.println(" Retrieving altimeter version information");
248 // Signal to altimeter we are here
250 sleep(15); // Sleep for 15ms, data is incoming at 10 samples/sec
252 // Get altimeter version, skip zeros
255 buf = readSkipZero(2);
257 if (buf.length != 2) {
259 throw new IOException("Communication with altimeter failed.");
264 // Get position of newest data
268 if (buf.length != 1) {
270 throw new IOException("Communication with altimeter failed.");
275 System.out.println(" Received version info "+ver[0]+"."+ver[1]+", position "+ver[2]);
282 * Delay the communication by a small delay (RWDELAY ms).
284 private void sleep() {
289 * Sleep for the given amount of milliseconds.
291 private void sleep(int n) {
294 } catch (InterruptedException ignore) { }
299 throws PortInUseException, IOException, UnsupportedCommOperationException {
301 System.err.println("ERROR: open() called with port="+port);
307 System.out.println(" Opening port...");
310 port = (SerialPort)portID.open("OpenRocket",1000);
312 port.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
313 SerialPort.PARITY_NONE);
315 port.setInputBufferSize(1);
316 port.setOutputBufferSize(1);
318 port.enableReceiveTimeout(TIMEOUT);
320 is = port.getInputStream();
321 os = port.getOutputStream();
325 private byte[] readSkipZero(int n) throws IOException, UnsupportedCommOperationException {
326 long t = System.currentTimeMillis() + TIMEOUT*2;
329 System.out.println(" readSkipZero "+n+" bytes");
332 while (System.currentTimeMillis() < t) {
333 byte[] buf = read(n);
335 printBytes(" Received",buf);
337 if (buf.length == 0) // No data available
342 for (i=0; i<buf.length; i++)
346 if (i==0) // No zeros to skip
349 if (i < buf.length) {
351 int count = buf.length-i; // No. of data bytes
352 byte[] array = new byte[n];
353 System.arraycopy(buf, i, array, 0, count);
356 printBytes(" Received (partial)",buf);
357 System.arraycopy(buf, 0, array, count, buf.length);
360 printBytes(" Returning",array);
366 System.out.println(" No data read, returning empty");
367 return new byte[0]; // no data, only zeros
371 private byte[] read(int n) throws IOException, UnsupportedCommOperationException {
372 byte[] bytes = new byte[n];
374 port.enableReceiveThreshold(n);
376 long t = System.currentTimeMillis() + TIMEOUT;
380 System.out.println(" Reading "+n+" bytes");
382 while (count < n && System.currentTimeMillis() < t) {
383 byte[] buf = new byte[n-count];
384 int c = is.read(buf);
385 System.arraycopy(buf, 0, bytes, count, c);
389 byte[] array = new byte[count];
390 System.arraycopy(bytes, 0, array, 0, count);
393 printBytes(" Returning",array);
398 private void write(String s) throws IOException {
399 write(s.getBytes(CHARSET));
402 private void write(byte ... bytes) throws IOException {
404 printBytes(" Writing",bytes);
408 private void close() {
410 System.out.println(" Closing port");
424 public static void main(String[] arg) {
426 if (arg.length != 1) {
427 System.err.println("Usage: java Alt15K <basename>");
428 System.err.println("Files will be saved <basename>-old.log, -med and -new");
433 String device = null;
434 String[] devices = Alt15K.getNames();
435 for (int i=0; i<devices.length; i++) {
436 if (devices[i].matches(".*USB.*")) {
441 if (device == null) {
442 System.out.println("Device not found.");
447 System.out.println("Selected device "+device);
452 Alt15K p = new Alt15K(device);
454 System.out.println("Retrieving newest data...");
456 System.out.println("Apogee at "+alt.getApogee()+" feet");
458 file = arg[0]+"-new.log";
459 System.out.println("Saving data to "+file+"...");
463 System.out.println("Retrieving medium data...");
465 System.out.println("Apogee at "+alt.getApogee()+" feet");
467 file = arg[0]+"-med.log";
468 System.out.println("Saving data to "+file+"...");
472 System.out.println("Retrieving oldest data...");
474 System.out.println("Apogee at "+alt.getApogee()+" feet");
476 file = arg[0]+"-old.log";
477 System.out.println("Saving data to "+file+"...");
480 } catch (IOException e) {
482 } catch (PortInUseException e) {
486 // System.out.println(alt);
492 static private void savefile(String file, AltData data) throws FileNotFoundException {
494 PrintStream output = new PrintStream(file);
496 // WTF is this so difficult?!?
497 DateFormat fmt = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
498 TimeZone tz=TimeZone.getTimeZone("GMT+3");
501 output.println("# Alt15K data, file "+file);
502 output.println("# Data retrieved at: "+fmt.format(new Date()));
503 output.println("# Values are in feet above launch level");
504 output.println("# ");
505 output.println("# Apogee = "+data.getApogee());
506 output.println("# MSL level = "+data.getMslLevel());
507 output.println("# Data count = "+data.getDataSamples());
509 byte[] b = data.getVersion();
511 for (int i=0; i<b.length; i++) {
515 s = s+"."+((int)b[i]);
517 output.println("# Altimeter version = " + s);
519 int[] values = data.getData();
520 for (int i=0; i < values.length; i++) {
521 output.println(""+values[i]);
528 static private void printBytes(String str, byte[] b) {
529 printBytes(str, b,b.length);
532 static private void printBytes(String str, byte[] b, int n) {
534 s = str+" "+n+" bytes:";
535 for (int i=0; i<n; i++) {
536 s += " "+unsign(b[i]);
538 System.out.println(s);
541 static private int unsign(byte b) {
548 @SuppressWarnings("unused")
549 static private int combine(int a, int b) {
553 static private int combine(byte a, byte b) {
554 int val = 256*unsign(a)+unsign(b);