altosui: Support raw telemetry from TeleDongle
authorKeith Packard <keithp@keithp.com>
Tue, 28 Jun 2011 08:03:00 +0000 (01:03 -0700)
committerKeith Packard <keithp@keithp.com>
Tue, 28 Jun 2011 08:03:00 +0000 (01:03 -0700)
Use raw telemetry frames when TeleDongle supports them, this involves
parsing the hex dump of the packet instead of having teledongle take
the packet apart. Only the legacy format is working at this point; the
altos bits for the new split telemetry frames is not written yet.

Signed-off-by: Keith Packard <keithp@keithp.com>
20 files changed:
altosui/Altos.java
altosui/AltosEepromIterable.java
altosui/AltosFile.java
altosui/AltosFlightUI.java
altosui/AltosGPS.java
altosui/AltosGPSSat.java [new file with mode: 0644]
altosui/AltosLog.java
altosui/AltosPreferences.java
altosui/AltosRecord.java
altosui/AltosSerial.java
altosui/AltosTelemetry.java
altosui/AltosTelemetryIterable.java
altosui/AltosTelemetryReader.java
altosui/AltosTelemetryRecord.java [new file with mode: 0644]
altosui/AltosTelemetryRecordGeneral.java [new file with mode: 0644]
altosui/AltosTelemetryRecordLegacy.java [new file with mode: 0644]
altosui/AltosTelemetryRecordRaw.java [new file with mode: 0644]
altosui/AltosTelemetryRecordSensor.java [new file with mode: 0644]
altosui/Makefile.am
configure.ac

index 6a2d9b9b202ff2ce0dddff09526316da14ef3616..25d97bcf2b5c6fc35c145c201e12ee5c440caea1 100644 (file)
@@ -20,6 +20,7 @@ package altosui;
 import java.awt.*;
 import java.util.*;
 import java.text.*;
 import java.awt.*;
 import java.util.*;
 import java.text.*;
+import java.nio.charset.Charset;
 
 import libaltosJNI.*;
 
 
 import libaltosJNI.*;
 
@@ -69,8 +70,11 @@ public class Altos {
 
        /* Telemetry modes */
        static final int ao_telemetry_off = 0;
 
        /* Telemetry modes */
        static final int ao_telemetry_off = 0;
-       static final int ao_telemetry_full = 1;
-       static final int ao_telemetry_tiny = 2;
+       static final int ao_telemetry_legacy = 1;
+       static final int ao_telemetry_split = 2;
+
+       static final int ao_telemetry_split_len = 32;
+       static final int ao_telemetry_legacy_len = 95;
 
        static HashMap<String,Integer>  string_to_state = new HashMap<String,Integer>();
 
 
        static HashMap<String,Integer>  string_to_state = new HashMap<String,Integer>();
 
@@ -201,6 +205,66 @@ public class Altos {
                return -1;
        }
 
                return -1;
        }
 
+       static int int8(int[] bytes, int i) {
+               return (int) (byte) bytes[i];
+       }
+
+       static int uint8(int[] bytes, int i) {
+               return bytes[i];
+       }
+
+       static int int16(int[] bytes, int i) {
+               return (int) (short) (bytes[i] + (bytes[i+1] << 8));
+       }
+
+       static int uint16(int[] bytes, int i) {
+               return bytes[i] + (bytes[i+1] << 8);
+       }
+
+       static int uint32(int[] bytes, int i) {
+               return bytes[i] +
+                       (bytes[i+1] << 8) +
+                       (bytes[i+2] << 16) +
+                       (bytes[i+3] << 24);
+       }
+
+       static final Charset    unicode_set = Charset.forName("UTF-8");
+
+       static String string(int[] bytes, int s, int l) {
+               byte[]  b = new byte[bytes.length];
+               for (int i = 0; i < l; i++)
+                       b[i] = (byte) bytes[s+i];
+               return new String(b, unicode_set);
+       }
+
+       static int hexbyte(String s, int i) {
+               int c0, c1;
+
+               if (s.length() < i + 2)
+                       throw new NumberFormatException(String.format("invalid hex \"%s\"", s));
+               c0 = s.charAt(i);
+               if (!Altos.ishex(c0))
+                       throw new NumberFormatException(String.format("invalid hex \"%c\"", c0));
+               c1 = s.charAt(i+1);
+               if (!Altos.ishex(c1))
+                       throw new NumberFormatException(String.format("invalid hex \"%c\"", c1));
+               return Altos.fromhex(c0) * 16 + Altos.fromhex(c1);
+       }
+
+       static int[] hexbytes(String s) {
+               int     n;
+               int[]   r;
+               int     i;
+
+               if ((s.length() & 1) != 0)
+                       throw new NumberFormatException(String.format("invalid line \"%s\"", s));
+               n = s.length() / 2;
+               r = new int[n];
+               for (i = 0; i < n; i++)
+                       r[i] = Altos.hexbyte(s, i * 2);
+               return r;
+       }
+
        static int fromdec(String s) throws NumberFormatException {
                int c, v = 0;
                int sign = 1;
        static int fromdec(String s) throws NumberFormatException {
                int c, v = 0;
                int sign = 1;
index 16349b884ec8729e52b9ad18fe4c7373f6c8eee7..812e5fc616ff03a2ac863b9dd01114fc1a12602f 100644 (file)
@@ -230,6 +230,7 @@ public class AltosEepromIterable extends AltosRecordIterable {
                case Altos.AO_LOG_SOFTWARE_VERSION:
                        break;
                }
                case Altos.AO_LOG_SOFTWARE_VERSION:
                        break;
                }
+               state.seen |= eeprom.seen;
        }
 
        LinkedList<AltosRecord> make_list() {
        }
 
        LinkedList<AltosRecord> make_list() {
index 06360572f8e23c0a8d8731afebf0095cf6c2c028..2e33b271dded901ca6a453ded9efa9326262e400 100644 (file)
@@ -38,7 +38,7 @@ class AltosFile extends File {
                     extension);
        }
 
                     extension);
        }
 
-       public AltosFile(AltosTelemetry telem) {
+       public AltosFile(AltosRecord telem) {
                this(telem.serial, telem.flight, "telem");
        }
 }
                this(telem.serial, telem.flight, "telem");
        }
 }
index eb6c6d9d82d1edc5f3500dcc142f3769dfc6d39d..5f1fc6788e89b2fde8737364506d267948936283 100644 (file)
@@ -156,9 +156,13 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay {
 
                        // Telemetry format menu
                        telemetries = new JComboBox();
 
                        // Telemetry format menu
                        telemetries = new JComboBox();
-                       telemetries.addItem("TeleMetrum");
-                       telemetries.addItem("TeleMini/TeleNano");
-                       telemetries.setSelectedIndex(AltosPreferences.telemetry(serial) - 1);
+                       telemetries.addItem("Legacy TeleMetrum");
+                       telemetries.addItem("Split Telemetry");
+                       int telemetry = 1;
+                       telemetry = AltosPreferences.telemetry(serial);
+                       if (telemetry > Altos.ao_telemetry_split)
+                               telemetry = Altos.ao_telemetry_split;
+                       telemetries.setSelectedIndex(telemetry - 1);
                        telemetries.setMaximumRowCount(2);
                        telemetries.addActionListener(new ActionListener() {
                                        public void actionPerformed(ActionEvent e) {
                        telemetries.setMaximumRowCount(2);
                        telemetries.addActionListener(new ActionListener() {
                                        public void actionPerformed(ActionEvent e) {
index 0dbc8364bb71f1e06f6ab10050a2021c48037374..b5df7c9a61d4b981553971cfd5842b862a7f86a0 100644 (file)
@@ -21,10 +21,6 @@ import java.lang.*;
 import java.text.*;
 
 public class AltosGPS {
 import java.text.*;
 
 public class AltosGPS {
-       public class AltosGPSSat {
-               int     svid;
-               int     c_n0;
-       }
 
        final static int MISSING = AltosRecord.MISSING;
 
 
        final static int MISSING = AltosRecord.MISSING;
 
@@ -158,9 +154,9 @@ public class AltosGPS {
                        else
                                tracking_channels = AltosParse.parse_int(words[i]);
                        i++;
                        else
                                tracking_channels = AltosParse.parse_int(words[i]);
                        i++;
-                       cc_gps_sat = new AltosGPS.AltosGPSSat[tracking_channels];
+                       cc_gps_sat = new AltosGPSSat[tracking_channels];
                        for (int chan = 0; chan < tracking_channels; chan++) {
                        for (int chan = 0; chan < tracking_channels; chan++) {
-                               cc_gps_sat[chan] = new AltosGPS.AltosGPSSat();
+                               cc_gps_sat[chan] = new AltosGPSSat();
                                cc_gps_sat[chan].svid = AltosParse.parse_int(words[i++]);
                                /* Older versions included SiRF status bits */
                                if (version < 2)
                                cc_gps_sat[chan].svid = AltosParse.parse_int(words[i++]);
                                /* Older versions included SiRF status bits */
                                if (version < 2)
@@ -168,7 +164,7 @@ public class AltosGPS {
                                cc_gps_sat[chan].c_n0 = AltosParse.parse_int(words[i++]);
                        }
                } else
                                cc_gps_sat[chan].c_n0 = AltosParse.parse_int(words[i++]);
                        }
                } else
-                       cc_gps_sat = new AltosGPS.AltosGPSSat[0];
+                       cc_gps_sat = new AltosGPSSat[0];
        }
 
        public void set_latitude(int in_lat) {
        }
 
        public void set_latitude(int in_lat) {
@@ -201,14 +197,14 @@ public class AltosGPS {
 
        public void add_sat(int svid, int c_n0) {
                if (cc_gps_sat == null) {
 
        public void add_sat(int svid, int c_n0) {
                if (cc_gps_sat == null) {
-                       cc_gps_sat = new AltosGPS.AltosGPSSat[1];
+                       cc_gps_sat = new AltosGPSSat[1];
                } else {
                } else {
-                       AltosGPSSat[] new_gps_sat = new AltosGPS.AltosGPSSat[cc_gps_sat.length + 1];
+                       AltosGPSSat[] new_gps_sat = new AltosGPSSat[cc_gps_sat.length + 1];
                        for (int i = 0; i < cc_gps_sat.length; i++)
                                new_gps_sat[i] = cc_gps_sat[i];
                        cc_gps_sat = new_gps_sat;
                }
                        for (int i = 0; i < cc_gps_sat.length; i++)
                                new_gps_sat[i] = cc_gps_sat[i];
                        cc_gps_sat = new_gps_sat;
                }
-               AltosGPS.AltosGPSSat    sat = new AltosGPS.AltosGPSSat();
+               AltosGPSSat     sat = new AltosGPSSat();
                sat.svid = svid;
                sat.c_n0 = c_n0;
                cc_gps_sat[cc_gps_sat.length - 1] = sat;
                sat.svid = svid;
                sat.c_n0 = c_n0;
                cc_gps_sat[cc_gps_sat.length - 1] = sat;
diff --git a/altosui/AltosGPSSat.java b/altosui/AltosGPSSat.java
new file mode 100644 (file)
index 0000000..fb12565
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package altosui;
+
+public class AltosGPSSat {
+       int     svid;
+       int     c_n0;
+
+       public AltosGPSSat(int s, int c) {
+               svid = s;
+               c_n0= c;
+       }
+
+       public AltosGPSSat() {
+       }
+}
+
index dd147d21adb960818ff3d26423f7c5e48816ba36..64275f32e4a9868e4a336d6808babb3c11d94fe2 100644 (file)
@@ -54,9 +54,10 @@ class AltosLog implements Runnable {
                }
        }
 
                }
        }
 
-       boolean open (AltosTelemetry telem) throws IOException {
+       boolean open (AltosRecord telem) throws IOException {
                AltosFile       a = new AltosFile(telem);
 
                AltosFile       a = new AltosFile(telem);
 
+               System.out.printf("open %s\n", a.toString());
                log_file = new FileWriter(a, true);
                if (log_file != null) {
                        while (!pending_queue.isEmpty()) {
                log_file = new FileWriter(a, true);
                if (log_file != null) {
                        while (!pending_queue.isEmpty()) {
@@ -74,18 +75,21 @@ class AltosLog implements Runnable {
 
        public void run () {
                try {
 
        public void run () {
                try {
+                       AltosRecord     previous = null;
                        for (;;) {
                                AltosLine       line = input_queue.take();
                                if (line.line == null)
                                        continue;
                                try {
                        for (;;) {
                                AltosLine       line = input_queue.take();
                                if (line.line == null)
                                        continue;
                                try {
-                                       AltosTelemetry  telem = new AltosTelemetry(line.line);
+                                       AltosRecord     telem = AltosTelemetry.parse(line.line, previous);
                                        if (telem.serial != serial || telem.flight != flight || log_file == null) {
                                                close_log_file();
                                                serial = telem.serial;
                                                flight = telem.flight;
                                        if (telem.serial != serial || telem.flight != flight || log_file == null) {
                                                close_log_file();
                                                serial = telem.serial;
                                                flight = telem.flight;
+                                               System.out.printf("Opening telem %d %d\n", serial, flight);
                                                open(telem);
                                        }
                                                open(telem);
                                        }
+                                       previous = telem;
                                } catch (ParseException pe) {
                                } catch (AltosCRCException ce) {
                                }
                                } catch (ParseException pe) {
                                } catch (AltosCRCException ce) {
                                }
index b1192be110e1f3573e2626939f73e238c68a3fda..5029aff631aa0f8a35a5746621a573237284dcf4 100644 (file)
@@ -216,7 +216,7 @@ class AltosPreferences {
                if (telemetries.containsKey(serial))
                        return telemetries.get(serial);
                int telemetry = preferences.getInt(String.format(telemetryPreferenceFormat, serial),
                if (telemetries.containsKey(serial))
                        return telemetries.get(serial);
                int telemetry = preferences.getInt(String.format(telemetryPreferenceFormat, serial),
-                                                  Altos.ao_telemetry_full);
+                                                  Altos.ao_telemetry_split);
                telemetries.put(serial, telemetry);
                return telemetry;
        }
                telemetries.put(serial, telemetry);
                return telemetry;
        }
index 200fffe5db993e1a7336fe2add88ff412dcb4482..8976edf05b90cdb007a6a3e3517beeed58967145 100644 (file)
@@ -25,6 +25,15 @@ import java.io.*;
 public class AltosRecord {
        final static int        MISSING = 0x7fffffff;
 
 public class AltosRecord {
        final static int        MISSING = 0x7fffffff;
 
+       static final int        seen_flight = 1;
+       static final int        seen_sensor = 2;
+       static final int        seen_temp_volt = 4;
+       static final int        seen_deploy = 8;
+       static final int        seen_gps_time = 16;
+       static final int        seen_gps_lat = 32;
+       static final int        seen_gps_lon = 64;
+       int                     seen;
+
        int     version;
        String  callsign;
        int     serial;
        int     version;
        String  callsign;
        int     serial;
@@ -226,6 +235,7 @@ public class AltosRecord {
 
        public AltosRecord(AltosRecord old) {
                version = old.version;
 
        public AltosRecord(AltosRecord old) {
                version = old.version;
+               seen = old.seen;
                callsign = old.callsign;
                serial = old.serial;
                flight = old.flight;
                callsign = old.callsign;
                serial = old.serial;
                flight = old.flight;
@@ -254,6 +264,7 @@ public class AltosRecord {
 
        public AltosRecord() {
                version = 0;
 
        public AltosRecord() {
                version = 0;
+               seen = 0;
                callsign = "N0CALL";
                serial = 0;
                flight = 0;
                callsign = "N0CALL";
                serial = 0;
                flight = 0;
index 6c80b66fbd46ec6d7401ac52162a2332bcb9a7a5..3666cb4142e65b9d3de42c344271b5d9faa2ea29 100644 (file)
@@ -95,7 +95,7 @@ public class AltosSerial implements Runnable {
                                                        }
                                                        if (debug)
                                                                System.out.printf("\t\t\t\t\t%s\n", line);
                                                        }
                                                        if (debug)
                                                                System.out.printf("\t\t\t\t\t%s\n", line);
-                                                       if (line.startsWith("VERSION") || line.startsWith("CRC")) {
+                                                       if (line.startsWith("TELEM") || line.startsWith("VERSION") || line.startsWith("CRC")) {
                                                                for (int e = 0; e < monitors.size(); e++) {
                                                                        LinkedBlockingQueue<AltosLine> q = monitors.get(e);
                                                                        q.put(new AltosLine (line));
                                                                for (int e = 0; e < monitors.size(); e++) {
                                                                        LinkedBlockingQueue<AltosLine> q = monitors.get(e);
                                                                        q.put(new AltosLine (line));
@@ -325,11 +325,22 @@ public class AltosSerial implements Runnable {
                set_callsign(AltosPreferences.callsign());
        }
 
                set_callsign(AltosPreferences.callsign());
        }
 
+       private int telemetry_len() {
+               switch (telemetry) {
+               case 1:
+               default:
+                       return Altos.ao_telemetry_legacy_len;
+               case 2:
+                       return Altos.ao_telemetry_split_len;
+               }
+       }
+
        public void set_channel(int in_channel) {
                channel = in_channel;
                if (altos != null) {
                        if (monitor_mode)
        public void set_channel(int in_channel) {
                channel = in_channel;
                if (altos != null) {
                        if (monitor_mode)
-                               printf("m 0\nc r %d\nm %d\n", channel, telemetry);
+                               printf("m 0\nc r %d\nm %x\n",
+                                      channel, telemetry_len());
                        else
                                printf("c r %d\n", channel);
                        flush_output();
                        else
                                printf("c r %d\n", channel);
                        flush_output();
@@ -340,7 +351,7 @@ public class AltosSerial implements Runnable {
                telemetry = in_telemetry;
                if (altos != null) {
                        if (monitor_mode)
                telemetry = in_telemetry;
                if (altos != null) {
                        if (monitor_mode)
-                               printf("m 0\nm %d\n", telemetry);
+                               printf("m 0\nm %x\n", telemetry_len());
                        flush_output();
                }
        }
                        flush_output();
                }
        }
@@ -349,7 +360,7 @@ public class AltosSerial implements Runnable {
                monitor_mode = monitor;
                if (altos != null) {
                        if (monitor)
                monitor_mode = monitor;
                if (altos != null) {
                        if (monitor)
-                               printf("m %d\n", telemetry);
+                               printf("m %x\n", telemetry_len());
                        else
                                printf("m 0\n");
                        flush_output();
                        else
                                printf("m 0\n");
                        flush_output();
@@ -393,7 +404,7 @@ public class AltosSerial implements Runnable {
                line = "";
                monitor_mode = false;
                frame = null;
                line = "";
                monitor_mode = false;
                frame = null;
-               telemetry = Altos.ao_telemetry_full;
+               telemetry = Altos.ao_telemetry_split;
                monitors = new LinkedList<LinkedBlockingQueue<AltosLine>> ();
                reply_queue = new LinkedBlockingQueue<AltosLine> ();
                open();
                monitors = new LinkedList<LinkedBlockingQueue<AltosLine>> ();
                reply_queue = new LinkedBlockingQueue<AltosLine> ();
                open();
index 7d68b5b5394898c62698ee90bdcf05e519ed43e2..a05269f4d305c7f5d3c9ad62587b5ed5cd0ea453 100644 (file)
@@ -27,6 +27,10 @@ import java.util.HashMap;
 
 
 /*
 
 
 /*
+ * The packet format is a simple hex dump of the raw telemetry frame.
+ * It starts with 'TELEM', then contains hex digits with a checksum as the last
+ * byte on the line.
+ *
  * Version 4 is a replacement with consistent syntax. Each telemetry line
  * contains a sequence of space-separated names and values, the values are
  * either integers or strings. The names are all unique. All values are
  * Version 4 is a replacement with consistent syntax. Each telemetry line
  * contains a sequence of space-separated names and values, the values are
  * either integers or strings. The names are all unique. All values are
@@ -81,6 +85,7 @@ import java.util.HashMap;
  */
 
 public class AltosTelemetry extends AltosRecord {
  */
 
 public class AltosTelemetry extends AltosRecord {
+
        /*
         * General header fields
         *
        /*
         * General header fields
         *
@@ -228,151 +233,9 @@ public class AltosTelemetry extends AltosRecord {
        final static String AO_TELEM_SAT_SVID   = "s_v";
        final static String AO_TELEM_SAT_C_N_0  = "s_c";
 
        final static String AO_TELEM_SAT_SVID   = "s_v";
        final static String AO_TELEM_SAT_C_N_0  = "s_c";
 
-       private void parse_v4(String[] words, int i) throws ParseException {
-               AltosTelemetryMap       map = new AltosTelemetryMap(words, i);
-
-               callsign = map.get_string(AO_TELEM_CALL, "N0CALL");
-               serial = map.get_int(AO_TELEM_SERIAL, MISSING);
-               flight = map.get_int(AO_TELEM_FLIGHT, MISSING);
-               rssi = map.get_int(AO_TELEM_RSSI, MISSING);
-               state = Altos.state(map.get_string(AO_TELEM_STATE, "invalid"));
-               tick = map.get_int(AO_TELEM_TICK, 0);
-
-               /* raw sensor values */
-               accel = map.get_int(AO_TELEM_RAW_ACCEL, MISSING);
-               pres = map.get_int(AO_TELEM_RAW_BARO, MISSING);
-               temp = map.get_int(AO_TELEM_RAW_THERMO, MISSING);
-               batt = map.get_int(AO_TELEM_RAW_BATT, MISSING);
-               drogue = map.get_int(AO_TELEM_RAW_DROGUE, MISSING);
-               main = map.get_int(AO_TELEM_RAW_MAIN, MISSING);
-
-               /* sensor calibration information */
-               ground_accel = map.get_int(AO_TELEM_CAL_ACCEL_GROUND, MISSING);
-               ground_pres = map.get_int(AO_TELEM_CAL_BARO_GROUND, MISSING);
-               accel_plus_g = map.get_int(AO_TELEM_CAL_ACCEL_PLUS, MISSING);
-               accel_minus_g = map.get_int(AO_TELEM_CAL_ACCEL_MINUS, MISSING);
-
-               /* flight computer values */
-               acceleration = map.get_double(AO_TELEM_KALMAN_ACCEL, MISSING, 1/16.0);
-               speed = map.get_double(AO_TELEM_KALMAN_SPEED, MISSING, 1/16.0);
-               height = map.get_int(AO_TELEM_KALMAN_HEIGHT, MISSING);
-
-               flight_accel = map.get_int(AO_TELEM_ADHOC_ACCEL, MISSING);
-               flight_vel = map.get_int(AO_TELEM_ADHOC_SPEED, MISSING);
-               flight_pres = map.get_int(AO_TELEM_ADHOC_BARO, MISSING);
-
-               if (map.has(AO_TELEM_GPS_STATE))
-                       gps = new AltosGPS(map);
-               else
-                       gps = null;
-       }
-
-       private void parse_legacy(String[] words, int i) throws ParseException {
-
-               AltosParse.word (words[i++], "CALL");
-               callsign = words[i++];
-
-               AltosParse.word (words[i++], "SERIAL");
-               serial = AltosParse.parse_int(words[i++]);
-
-               if (version >= 2) {
-                       AltosParse.word (words[i++], "FLIGHT");
-                       flight = AltosParse.parse_int(words[i++]);
-               } else
-                       flight = 0;
-
-               AltosParse.word(words[i++], "RSSI");
-               rssi = AltosParse.parse_int(words[i++]);
-
-               /* Older telemetry data had mis-computed RSSI value */
-               if (version <= 2)
-                       rssi = (rssi + 74) / 2 - 74;
-
-               AltosParse.word(words[i++], "STATUS");
-               status = AltosParse.parse_hex(words[i++]);
-
-               AltosParse.word(words[i++], "STATE");
-               state = Altos.state(words[i++]);
-
-               tick = AltosParse.parse_int(words[i++]);
-
-               AltosParse.word(words[i++], "a:");
-               accel = AltosParse.parse_int(words[i++]);
-
-               AltosParse.word(words[i++], "p:");
-               pres = AltosParse.parse_int(words[i++]);
-
-               AltosParse.word(words[i++], "t:");
-               temp = AltosParse.parse_int(words[i++]);
-
-               AltosParse.word(words[i++], "v:");
-               batt = AltosParse.parse_int(words[i++]);
-
-               AltosParse.word(words[i++], "d:");
-               drogue = AltosParse.parse_int(words[i++]);
-
-               AltosParse.word(words[i++], "m:");
-               main = AltosParse.parse_int(words[i++]);
-
-               AltosParse.word(words[i++], "fa:");
-               flight_accel = AltosParse.parse_int(words[i++]);
-
-               AltosParse.word(words[i++], "ga:");
-               ground_accel = AltosParse.parse_int(words[i++]);
-
-               AltosParse.word(words[i++], "fv:");
-               flight_vel = AltosParse.parse_int(words[i++]);
-
-               AltosParse.word(words[i++], "fp:");
-               flight_pres = AltosParse.parse_int(words[i++]);
-
-               /* Old TeleDongle code with kalman-reporting TeleMetrum code */
-               if ((flight_vel & 0xffff0000) == 0x80000000) {
-                       speed = ((short) flight_vel) / 16.0;
-                       acceleration = flight_accel / 16.0;
-                       height = flight_pres;
-                       flight_vel = MISSING;
-                       flight_pres = MISSING;
-                       flight_accel = MISSING;
-               }
-
-               AltosParse.word(words[i++], "gp:");
-               ground_pres = AltosParse.parse_int(words[i++]);
-
-               if (version >= 1) {
-                       AltosParse.word(words[i++], "a+:");
-                       accel_plus_g = AltosParse.parse_int(words[i++]);
-
-                       AltosParse.word(words[i++], "a-:");
-                       accel_minus_g = AltosParse.parse_int(words[i++]);
-               } else {
-                       accel_plus_g = ground_accel;
-                       accel_minus_g = ground_accel + 530;
-               }
-
-               gps = new AltosGPS(words, i, version);
-       }
-
-       public AltosTelemetry(String line) throws ParseException, AltosCRCException {
-               String[] words = line.split("\\s+");
-               int     i = 0;
-
-               if (words[i].equals("CRC") && words[i+1].equals("INVALID")) {
-                       i += 2;
-                       AltosParse.word(words[i++], "RSSI");
-                       rssi = AltosParse.parse_int(words[i++]);
-                       throw new AltosCRCException(rssi);
-               }
-               if (words[i].equals("CALL")) {
-                       version = 0;
-               } else {
-                       AltosParse.word (words[i++], "VERSION");
-                       version = AltosParse.parse_int(words[i++]);
-               }
+       static public AltosRecord parse(String line, AltosRecord previous) throws ParseException, AltosCRCException {
+               AltosTelemetryRecord    r = AltosTelemetryRecordGeneral.parse(line);
 
 
-               if (version < 4)
-                       parse_legacy(words, i);
-               else
-                       parse_v4(words, i);
+               return r.update_state(previous);
        }
 }
        }
 }
index 44e5ad8f8d9c2fe9977cd0550abf8ab67c8d9dc0..90a08485c8301611ebd7e66d8d751f20d423d66e 100644 (file)
@@ -40,6 +40,7 @@ public class AltosTelemetryIterable extends AltosRecordIterable {
                int     current_tick = 0;
                int     boost_tick = 0;
 
                int     current_tick = 0;
                int     boost_tick = 0;
 
+               AltosRecord     previous = null;
                records = new LinkedList<AltosRecord> ();
 
                try {
                records = new LinkedList<AltosRecord> ();
 
                try {
@@ -49,9 +50,10 @@ public class AltosTelemetryIterable extends AltosRecordIterable {
                                        break;
                                }
                                try {
                                        break;
                                }
                                try {
-                                       AltosTelemetry record = new AltosTelemetry(line);
+                                       AltosRecord record = AltosTelemetry.parse(line, previous);
                                        if (record == null)
                                                break;
                                        if (record == null)
                                                break;
+                                       previous = record;
                                        if (records.isEmpty()) {
                                                current_tick = record.tick;
                                        } else {
                                        if (records.isEmpty()) {
                                                current_tick = record.tick;
                                        } else {
index 980391b437476add2ca092b27ad0d00e6c89a2a2..18f17841acf76ac4373aeb58569ad46c0e732784 100644 (file)
@@ -26,6 +26,7 @@ class AltosTelemetryReader extends AltosFlightReader {
        AltosDevice     device;
        AltosSerial     serial;
        AltosLog        log;
        AltosDevice     device;
        AltosSerial     serial;
        AltosLog        log;
+       AltosRecord     previous;
 
        LinkedBlockingQueue<AltosLine> telem;
 
 
        LinkedBlockingQueue<AltosLine> telem;
 
@@ -33,7 +34,9 @@ class AltosTelemetryReader extends AltosFlightReader {
                AltosLine l = telem.take();
                if (l.line == null)
                        throw new IOException("IO error");
                AltosLine l = telem.take();
                if (l.line == null)
                        throw new IOException("IO error");
-               return new AltosTelemetry(l.line);
+               AltosRecord     next = AltosTelemetry.parse(l.line, previous);
+               previous = next;
+               return next;
        }
 
        void close(boolean interrupted) {
        }
 
        void close(boolean interrupted) {
@@ -58,6 +61,7 @@ class AltosTelemetryReader extends AltosFlightReader {
                serial = new AltosSerial(device);
                log = new AltosLog(serial);
                name = device.toShortString();
                serial = new AltosSerial(device);
                log = new AltosLog(serial);
                name = device.toShortString();
+               previous = null;
 
                telem = new LinkedBlockingQueue<AltosLine>();
                serial.set_radio();
 
                telem = new LinkedBlockingQueue<AltosLine>();
                serial.set_radio();
diff --git a/altosui/AltosTelemetryRecord.java b/altosui/AltosTelemetryRecord.java
new file mode 100644 (file)
index 0000000..cfac309
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package altosui;
+
+public interface AltosTelemetryRecord {
+
+       public AltosRecord update_state(AltosRecord previous);
+}
diff --git a/altosui/AltosTelemetryRecordGeneral.java b/altosui/AltosTelemetryRecordGeneral.java
new file mode 100644 (file)
index 0000000..55f6c49
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package altosui;
+
+import java.lang.*;
+import java.text.*;
+import java.util.HashMap;
+
+public class AltosTelemetryRecordGeneral {
+
+       static AltosTelemetryRecord parse(String line) throws ParseException, AltosCRCException {
+               AltosTelemetryRecord    r;
+
+               String[] word = line.split("\\s+");
+               int i =0;
+               if (word[i].equals("CRC") && word[i+1].equals("INVALID")) {
+                       i += 2;
+                       AltosParse.word(word[i++], "RSSI");
+                       throw new AltosCRCException(AltosParse.parse_int(word[i++]));
+               }
+
+               System.out.printf("First word \"%s\"\n", word[i]);
+               if (word[i].equals("TELEM"))
+                       r = AltosTelemetryRecordRaw.parse(word[i+1]);
+               else
+                       r = new AltosTelemetryRecordLegacy(line);
+               return r;
+       }
+}
diff --git a/altosui/AltosTelemetryRecordLegacy.java b/altosui/AltosTelemetryRecordLegacy.java
new file mode 100644 (file)
index 0000000..e3751ee
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package altosui;
+
+import java.lang.*;
+import java.text.*;
+import java.util.HashMap;
+
+/*
+ * Telemetry data contents
+ */
+
+
+/*
+ * The packet format is a simple hex dump of the raw telemetry frame.
+ * It starts with 'TELEM', then contains hex digits with a checksum as the last
+ * byte on the line.
+ *
+ * Version 4 is a replacement with consistent syntax. Each telemetry line
+ * contains a sequence of space-separated names and values, the values are
+ * either integers or strings. The names are all unique. All values are
+ * optional
+ *
+ * VERSION 4 c KD7SQG n 236 f 18 r -25 s pad t 513 r_a 15756 r_b 26444 r_t 20944
+ *   r_v 26640 r_d 512 r_m 208 c_a 15775 c_b 26439 c_p 15749 c_m 16281 a_a 15764
+ *   a_s 0 a_b 26439 g_s u g_n 0 s_n 0
+ *
+ * VERSION 4 c KD7SQG n 19 f 0 r -23 s pad t 513 r_b 26372 r_t 21292 r_v 26788
+ *   r_d 136 r_m 140 c_b 26370 k_h 0 k_s 0 k_a 0
+ *
+ * General header fields
+ *
+ *     Name            Value
+ *
+ *     VERSION         Telemetry version number (4 or more). Must be first.
+ *     c               Callsign (string, no spaces allowed)
+ *     n               Flight unit serial number (integer)
+ *     f               Flight number (integer)
+ *     r               Packet RSSI value (integer)
+ *     s               Flight computer state (string, no spaces allowed)
+ *     t               Flight computer clock (integer in centiseconds)
+ *
+ * Version 3 is Version 2 with fixed RSSI numbers -- the radio reports
+ * in 1/2dB increments while this protocol provides only integers. So,
+ * the syntax didn't change just the interpretation of the RSSI
+ * values.
+ *
+ * Version 2 of the telemetry data stream is a bit of a mess, 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 AltosTelemetryRecordLegacy extends AltosRecord implements AltosTelemetryRecord {
+       /*
+        * General header fields
+        *
+        *      Name            Value
+        *
+        *      VERSION         Telemetry version number (4 or more). Must be first.
+        *      c               Callsign (string, no spaces allowed)
+        *      n               Flight unit serial number (integer)
+        *      f               Flight number (integer)
+        *      r               Packet RSSI value (integer)
+        *      s               Flight computer state (string, no spaces allowed)
+        *      t               Flight computer clock (integer in centiseconds)
+        */
+
+       final static String AO_TELEM_VERSION    = "VERSION";
+       final static String AO_TELEM_CALL       = "c";
+       final static String AO_TELEM_SERIAL     = "n";
+       final static String AO_TELEM_FLIGHT     = "f";
+       final static String AO_TELEM_RSSI       = "r";
+       final static String AO_TELEM_STATE      = "s";
+       final static String AO_TELEM_TICK       = "t";
+
+       /*
+        * Raw sensor values
+        *
+        *      Name            Value
+        *      r_a             Accelerometer reading (integer)
+        *      r_b             Barometer reading (integer)
+        *      r_t             Thermometer reading (integer)
+        *      r_v             Battery reading (integer)
+        *      r_d             Drogue continuity (integer)
+        *      r_m             Main continuity (integer)
+        */
+
+       final static String AO_TELEM_RAW_ACCEL  = "r_a";
+       final static String AO_TELEM_RAW_BARO   = "r_b";
+       final static String AO_TELEM_RAW_THERMO = "r_t";
+       final static String AO_TELEM_RAW_BATT   = "r_v";
+       final static String AO_TELEM_RAW_DROGUE = "r_d";
+       final static String AO_TELEM_RAW_MAIN   = "r_m";
+
+       /*
+        * Sensor calibration values
+        *
+        *      Name            Value
+        *      c_a             Ground accelerometer reading (integer)
+        *      c_b             Ground barometer reading (integer)
+        *      c_p             Accelerometer reading for +1g
+        *      c_m             Accelerometer reading for -1g
+        */
+
+       final static String AO_TELEM_CAL_ACCEL_GROUND   = "c_a";
+       final static String AO_TELEM_CAL_BARO_GROUND    = "c_b";
+       final static String AO_TELEM_CAL_ACCEL_PLUS     = "c_p";
+       final static String AO_TELEM_CAL_ACCEL_MINUS    = "c_m";
+
+       /*
+        * Kalman state values
+        *
+        *      Name            Value
+        *      k_h             Height above pad (integer, meters)
+        *      k_s             Vertical speeed (integer, m/s * 16)
+        *      k_a             Vertical acceleration (integer, m/s² * 16)
+        */
+
+       final static String AO_TELEM_KALMAN_HEIGHT      = "k_h";
+       final static String AO_TELEM_KALMAN_SPEED       = "k_s";
+       final static String AO_TELEM_KALMAN_ACCEL       = "k_a";
+
+       /*
+        * Ad-hoc flight values
+        *
+        *      Name            Value
+        *      a_a             Acceleration (integer, sensor units)
+        *      a_s             Speed (integer, integrated acceleration value)
+        *      a_b             Barometer reading (integer, sensor units)
+        */
+
+       final static String AO_TELEM_ADHOC_ACCEL        = "a_a";
+       final static String AO_TELEM_ADHOC_SPEED        = "a_s";
+       final static String AO_TELEM_ADHOC_BARO         = "a_b";
+
+       /*
+        * GPS values
+        *
+        *      Name            Value
+        *      g_s             GPS state (string):
+        *                              l       locked
+        *                              u       unlocked
+        *                              e       error (missing or broken)
+        *      g_n             Number of sats used in solution
+        *      g_ns            Latitude (degrees * 10e7)
+        *      g_ew            Longitude (degrees * 10e7)
+        *      g_a             Altitude (integer meters)
+        *      g_Y             GPS year (integer)
+        *      g_M             GPS month (integer - 1-12)
+        *      g_D             GPS day (integer - 1-31)
+        *      g_h             GPS hour (integer - 0-23)
+        *      g_m             GPS minute (integer - 0-59)
+        *      g_s             GPS second (integer - 0-59)
+        *      g_v             GPS vertical speed (integer, cm/sec)
+        *      g_s             GPS horizontal speed (integer, cm/sec)
+        *      g_c             GPS course (integer, 0-359)
+        *      g_hd            GPS hdop (integer * 10)
+        *      g_vd            GPS vdop (integer * 10)
+        *      g_he            GPS h error (integer)
+        *      g_ve            GPS v error (integer)
+        */
+
+       final static String AO_TELEM_GPS_STATE                  = "g";
+       final static String AO_TELEM_GPS_STATE_LOCKED           = "l";
+       final static String AO_TELEM_GPS_STATE_UNLOCKED         = "u";
+       final static String AO_TELEM_GPS_STATE_ERROR            = "e";
+       final static String AO_TELEM_GPS_NUM_SAT                = "g_n";
+       final static String AO_TELEM_GPS_LATITUDE               = "g_ns";
+       final static String AO_TELEM_GPS_LONGITUDE              = "g_ew";
+       final static String AO_TELEM_GPS_ALTITUDE               = "g_a";
+       final static String AO_TELEM_GPS_YEAR                   = "g_Y";
+       final static String AO_TELEM_GPS_MONTH                  = "g_M";
+       final static String AO_TELEM_GPS_DAY                    = "g_D";
+       final static String AO_TELEM_GPS_HOUR                   = "g_h";
+       final static String AO_TELEM_GPS_MINUTE                 = "g_m";
+       final static String AO_TELEM_GPS_SECOND                 = "g_s";
+       final static String AO_TELEM_GPS_VERTICAL_SPEED         = "g_v";
+       final static String AO_TELEM_GPS_HORIZONTAL_SPEED       = "g_g";
+       final static String AO_TELEM_GPS_COURSE                 = "g_c";
+       final static String AO_TELEM_GPS_HDOP                   = "g_hd";
+       final static String AO_TELEM_GPS_VDOP                   = "g_vd";
+       final static String AO_TELEM_GPS_HERROR                 = "g_he";
+       final static String AO_TELEM_GPS_VERROR                 = "g_ve";
+
+       /*
+        * GPS satellite values
+        *
+        *      Name            Value
+        *      s_n             Number of satellites reported (integer)
+        *      s_v0            Space vehicle ID (integer) for report 0
+        *      s_c0            C/N0 number (integer) for report 0
+        *      s_v1            Space vehicle ID (integer) for report 1
+        *      s_c1            C/N0 number (integer) for report 1
+        *      ...
+        */
+
+       final static String AO_TELEM_SAT_NUM    = "s_n";
+       final static String AO_TELEM_SAT_SVID   = "s_v";
+       final static String AO_TELEM_SAT_C_N_0  = "s_c";
+
+       private void parse_v4(String[] words, int i) throws ParseException {
+               AltosTelemetryMap       map = new AltosTelemetryMap(words, i);
+
+               callsign = map.get_string(AO_TELEM_CALL, "N0CALL");
+               serial = map.get_int(AO_TELEM_SERIAL, MISSING);
+               flight = map.get_int(AO_TELEM_FLIGHT, MISSING);
+               rssi = map.get_int(AO_TELEM_RSSI, MISSING);
+               state = Altos.state(map.get_string(AO_TELEM_STATE, "invalid"));
+               tick = map.get_int(AO_TELEM_TICK, 0);
+
+               /* raw sensor values */
+               accel = map.get_int(AO_TELEM_RAW_ACCEL, MISSING);
+               pres = map.get_int(AO_TELEM_RAW_BARO, MISSING);
+               temp = map.get_int(AO_TELEM_RAW_THERMO, MISSING);
+               batt = map.get_int(AO_TELEM_RAW_BATT, MISSING);
+               drogue = map.get_int(AO_TELEM_RAW_DROGUE, MISSING);
+               main = map.get_int(AO_TELEM_RAW_MAIN, MISSING);
+
+               /* sensor calibration information */
+               ground_accel = map.get_int(AO_TELEM_CAL_ACCEL_GROUND, MISSING);
+               ground_pres = map.get_int(AO_TELEM_CAL_BARO_GROUND, MISSING);
+               accel_plus_g = map.get_int(AO_TELEM_CAL_ACCEL_PLUS, MISSING);
+               accel_minus_g = map.get_int(AO_TELEM_CAL_ACCEL_MINUS, MISSING);
+
+               /* flight computer values */
+               acceleration = map.get_double(AO_TELEM_KALMAN_ACCEL, MISSING, 1/16.0);
+               speed = map.get_double(AO_TELEM_KALMAN_SPEED, MISSING, 1/16.0);
+               height = map.get_int(AO_TELEM_KALMAN_HEIGHT, MISSING);
+
+               flight_accel = map.get_int(AO_TELEM_ADHOC_ACCEL, MISSING);
+               flight_vel = map.get_int(AO_TELEM_ADHOC_SPEED, MISSING);
+               flight_pres = map.get_int(AO_TELEM_ADHOC_BARO, MISSING);
+
+               if (map.has(AO_TELEM_GPS_STATE))
+                       gps = new AltosGPS(map);
+               else
+                       gps = null;
+       }
+
+       private void parse_legacy(String[] words, int i) throws ParseException {
+
+               AltosParse.word (words[i++], "CALL");
+               callsign = words[i++];
+
+               AltosParse.word (words[i++], "SERIAL");
+               serial = AltosParse.parse_int(words[i++]);
+
+               if (version >= 2) {
+                       AltosParse.word (words[i++], "FLIGHT");
+                       flight = AltosParse.parse_int(words[i++]);
+               } else
+                       flight = 0;
+
+               AltosParse.word(words[i++], "RSSI");
+               rssi = AltosParse.parse_int(words[i++]);
+
+               /* Older telemetry data had mis-computed RSSI value */
+               if (version <= 2)
+                       rssi = (rssi + 74) / 2 - 74;
+
+               AltosParse.word(words[i++], "STATUS");
+               status = AltosParse.parse_hex(words[i++]);
+
+               AltosParse.word(words[i++], "STATE");
+               state = Altos.state(words[i++]);
+
+               tick = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "a:");
+               accel = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "p:");
+               pres = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "t:");
+               temp = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "v:");
+               batt = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "d:");
+               drogue = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "m:");
+               main = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "fa:");
+               flight_accel = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "ga:");
+               ground_accel = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "fv:");
+               flight_vel = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "fp:");
+               flight_pres = AltosParse.parse_int(words[i++]);
+
+               /* Old TeleDongle code with kalman-reporting TeleMetrum code */
+               if ((flight_vel & 0xffff0000) == 0x80000000) {
+                       speed = ((short) flight_vel) / 16.0;
+                       acceleration = flight_accel / 16.0;
+                       height = flight_pres;
+                       flight_vel = MISSING;
+                       flight_pres = MISSING;
+                       flight_accel = MISSING;
+               }
+
+               AltosParse.word(words[i++], "gp:");
+               ground_pres = AltosParse.parse_int(words[i++]);
+
+               if (version >= 1) {
+                       AltosParse.word(words[i++], "a+:");
+                       accel_plus_g = AltosParse.parse_int(words[i++]);
+
+                       AltosParse.word(words[i++], "a-:");
+                       accel_minus_g = AltosParse.parse_int(words[i++]);
+               } else {
+                       accel_plus_g = ground_accel;
+                       accel_minus_g = ground_accel + 530;
+               }
+
+               gps = new AltosGPS(words, i, version);
+       }
+
+       public AltosTelemetryRecordLegacy(String line) throws ParseException, AltosCRCException {
+               String[] words = line.split("\\s+");
+               int     i = 0;
+
+               if (words[i].equals("CRC") && words[i+1].equals("INVALID")) {
+                       i += 2;
+                       AltosParse.word(words[i++], "RSSI");
+                       rssi = AltosParse.parse_int(words[i++]);
+                       throw new AltosCRCException(rssi);
+               }
+               if (words[i].equals("CALL")) {
+                       version = 0;
+               } else {
+                       AltosParse.word (words[i++], "VERSION");
+                       version = AltosParse.parse_int(words[i++]);
+               }
+
+               if (version < 4)
+                       parse_legacy(words, i);
+               else
+                       parse_v4(words, i);
+       }
+
+       /*
+        * Given a hex dump of a legacy telemetry line, construct an AltosRecord from that
+        */
+
+       int[]   bytes;
+
+       private int int8(int i) {
+               return Altos.int8(bytes, i + 1);
+       }
+       private int uint8(int i) {
+               return Altos.uint8(bytes, i + 1);
+       }
+       private int int16(int i) {
+               return Altos.int16(bytes, i + 1);
+       }
+       private int uint16(int i) {
+               return Altos.uint16(bytes, i + 1);
+       }
+       private int uint32(int i) {
+               return Altos.uint32(bytes, i + 1);
+       }
+       private String string(int i, int l) {
+               return Altos.string(bytes, i + 1, l);
+       }
+
+       static final int AO_GPS_NUM_SAT_MASK    = (0xf << 0);
+       static final int AO_GPS_NUM_SAT_SHIFT   = (0);
+
+       static final int AO_GPS_VALID           = (1 << 4);
+       static final int AO_GPS_RUNNING         = (1 << 5);
+       static final int AO_GPS_DATE_VALID      = (1 << 6);
+       static final int AO_GPS_COURSE_VALID    = (1 << 7);
+
+       static class theLock extends Object {
+       }
+       static public theLock lockObject = new theLock();
+       public AltosTelemetryRecordLegacy(int[] in_bytes, int in_rssi, int in_status) {
+               bytes = in_bytes;
+               synchronized(lockObject) {
+               for (int i = 0; i < in_bytes.length - 2; i++) {
+                       if ((i % 10) == 0)
+                               System.out.printf("%3d:", i);
+                       System.out.printf(" %02x", uint8(i));
+                       if ((i % 10) == 9 || i == in_bytes.length - 3)
+                               System.out.printf("\n");
+               }
+               }
+               version = 4;
+               callsign = string(62, 8);
+               serial = uint16(0);
+               flight = uint16(2);
+               rssi = in_rssi;
+               status = in_status;
+               state = uint8(4);
+               tick = uint16(21);
+               accel = int16(23);
+               pres = int16(25);
+               temp = int16(27);
+               batt = int16(29);
+               drogue = int16(31);
+               main = int16(33);
+               
+               ground_accel = int16(7);
+               ground_pres = int16(15);
+               accel_plus_g = int16(17);
+               accel_minus_g = int16(19);
+
+               if (uint16(11) == 0x8000) {
+                       acceleration = int16(5);
+                       speed = int16(9);
+                       height = int16(13);
+                       flight_accel = MISSING;
+                       flight_vel = MISSING;
+                       flight_pres = MISSING;
+               } else {
+                       flight_accel = int16(5);
+                       flight_vel = uint32(9);
+                       flight_pres = int16(13);
+                       acceleration = MISSING;
+                       speed = MISSING;
+                       height = MISSING;
+               }
+
+               gps = null;
+
+               int gps_flags = uint8(41);
+
+               if ((gps_flags & (AO_GPS_VALID|AO_GPS_RUNNING)) != 0) {
+                       gps = new AltosGPS();
+
+                       gps.nsat = (gps_flags & AO_GPS_NUM_SAT_MASK);
+                       gps.locked = (gps_flags & AO_GPS_VALID) != 0;
+                       gps.connected = true;
+                       gps.lat = uint32(42) / 1.0e7;
+                       gps.lon = uint32(46) / 1.0e7;
+                       gps.alt = int16(50);
+                       gps.ground_speed = uint16(52) / 100.0;
+                       gps.course = uint8(54) * 2;
+                       gps.hdop = uint8(55) / 5.0;
+                       gps.h_error = uint16(58);
+                       gps.v_error = uint16(60);
+
+                       int     n_tracking_reported = uint8(70);
+                       if (n_tracking_reported > 12)
+                               n_tracking_reported = 12;
+                       int     n_tracking_actual = 0;
+                       for (int i = 0; i < n_tracking_reported; i++) {
+                               if (uint8(71 + i*2) != 0)
+                                       n_tracking_actual++;
+                       }
+                       if (n_tracking_actual > 0) {
+                               gps.cc_gps_sat = new AltosGPSSat[n_tracking_actual];
+
+                               n_tracking_actual = 0;
+                               for (int i = 0; i < n_tracking_reported; i++) {
+                                       int     svid = uint8(71 + i*2);
+                                       int     c_n0 = uint8(72 + i*2);
+                                       if (svid != 0)
+                                               gps.cc_gps_sat[n_tracking_actual++] = new AltosGPSSat(svid, c_n0);
+                               }
+                       }
+               }
+
+               time = 0.0;
+       }
+
+       public AltosRecord update_state(AltosRecord previous) {
+               return this;
+       }
+}
diff --git a/altosui/AltosTelemetryRecordRaw.java b/altosui/AltosTelemetryRecordRaw.java
new file mode 100644 (file)
index 0000000..35796a2
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package altosui;
+
+import java.lang.*;
+import java.text.*;
+import java.util.HashMap;
+
+public class AltosTelemetryRecordRaw implements AltosTelemetryRecord {
+       int[]   bytes;
+       int     serial;
+       int     tick;
+       int     type;
+
+       final static int packet_type_TM_sensor = 0x01;
+       final static int packet_type_Tm_sensor = 0x02;
+       final static int packet_type_Tn_sensor = 0x03;
+       final static int packet_type_config = 0x04;
+       final static int packet_type_GPS_location = 0x05;
+       final static int packet_type_GPS_satellites = 0x06;
+       
+       final static int PKT_APPEND_STATUS_1_CRC_OK             = (1 << 7);
+       final static int PKT_APPEND_STATUS_1_LQI_MASK           = (0x7f);
+       final static int PKT_APPEND_STATUS_1_LQI_SHIFT          = 0;
+
+       static boolean cksum(int[] bytes) {
+               int     sum = 0x5a;
+               for (int i = 1; i < bytes.length - 1; i++)
+                       sum += bytes[i];
+               sum &= 0xff;
+               System.out.printf("%d bytes sum 0x%x last byte 0x%x\n",
+                                 bytes.length, sum, bytes[bytes.length - 1]);
+               return sum == bytes[bytes.length - 1];
+       }
+
+       public static AltosTelemetryRecord parse (String hex) throws ParseException, AltosCRCException {
+               AltosTelemetryRecord    r;
+
+               int[] bytes;
+               try {
+                       bytes = Altos.hexbytes(hex);
+               } catch (NumberFormatException ne) {
+                       throw new ParseException(ne.getMessage(), 0);
+               }
+
+               /* one for length, one for checksum */
+               if (bytes[0] != bytes.length - 2)
+                       throw new ParseException(String.format("invalid length %d != %d\n",
+                                                              bytes[0],
+                                                              bytes.length - 2), 0);
+               if (!cksum(bytes))
+                       throw new ParseException(String.format("invalid line \"%s\"", hex), 0);
+
+               int     rssi = Altos.int8(bytes, bytes.length - 3) / 2 - 74;
+               int     status = Altos.uint8(bytes, bytes.length - 2);
+
+               System.out.printf ("rssi 0x%x = %d status 0x%x\n",
+                                  Altos.uint8(bytes, bytes.length - 3),
+                                  rssi, status);
+
+               if ((status & PKT_APPEND_STATUS_1_CRC_OK) == 0)
+                       throw new AltosCRCException(rssi);
+
+               /* length, data ..., rssi, status, checksum -- 4 bytes extra */
+               switch (bytes.length) {
+               case Altos.ao_telemetry_split_len + 4:
+                       int     type = Altos.uint8(bytes, 4 + 1);
+                       switch (type) {
+                       case packet_type_TM_sensor:
+                       case packet_type_Tm_sensor:
+                       case packet_type_Tn_sensor:
+                               r = new AltosTelemetryRecordSensor(bytes);
+                               break;
+                       default:
+                               r = new AltosTelemetryRecordRaw(bytes);
+                               break;
+                       }
+               case Altos.ao_telemetry_legacy_len + 4:
+                       r = new AltosTelemetryRecordLegacy(bytes, rssi, status);
+                       break;
+               default:
+                       throw new ParseException(String.format("Invalid packet length %d", bytes.length), 0);
+               }
+               return r;
+       }
+
+       public int int8(int off) {
+               return Altos.int8(bytes, off + 1);
+       }
+
+       public int uint8(int off) {
+               return Altos.uint8(bytes, off + 1);
+       }
+
+       public int int16(int off) {
+               return Altos.int16(bytes, off + 1);
+       }
+
+       public int uint16(int off) {
+               return Altos.uint16(bytes, off + 1);
+       }
+
+       public int uint32(int off) {
+               return Altos.uint32(bytes, off + 1);
+       }
+
+       public AltosTelemetryRecordRaw(int[] in_bytes) {
+               bytes = in_bytes;
+               serial = uint16(0);
+               tick   = uint16(2);
+               type   = uint8(4);
+       }
+
+       public AltosRecord update_state(AltosRecord previous) {
+               if (previous != null)
+                       return new AltosRecord(previous);
+               else
+                       return new AltosRecord();
+       }
+}
diff --git a/altosui/AltosTelemetryRecordSensor.java b/altosui/AltosTelemetryRecordSensor.java
new file mode 100644 (file)
index 0000000..5ae9f89
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package altosui;
+
+
+public class AltosTelemetryRecordSensor extends AltosTelemetryRecordRaw {
+       int     state;
+       int     accel;
+       int     pres;
+       int     temp;
+       int     v_batt;
+       int     sense_d;
+       int     sense_m;
+
+       int     acceleration;
+       int     speed;
+       int     height;
+
+       int     ground_accel;
+       int     ground_pres;
+       int     accel_plus_g;
+       int     accel_minus_g;
+
+       public AltosTelemetryRecordSensor(int[] in_bytes) {
+               super(in_bytes);
+               state         = uint8(5);
+
+               accel         = int16(6);
+               pres          = int16(8);
+               temp          = int16(10);
+               v_batt        = int16(12);
+               sense_d       = int16(14);
+               sense_m       = int16(16);
+
+               acceleration  = int16(18);
+               speed         = int16(20);
+               height        = int16(22);
+
+               ground_accel  = int16(24);
+               ground_pres   = int16(26);
+               accel_plus_g  = int16(28);
+               accel_minus_g = int16(30);
+       }
+
+       public AltosRecord update_state(AltosRecord previous) {
+               AltosRecord     next = super.update_state(previous);
+               return next;
+       }
+}
index f4d84ad29648ee93fa98b030614106049ffa51aa..6a4d9d17503a9945d3f09a424369722e1d712eb6 100644 (file)
@@ -51,6 +51,7 @@ altosui_JAVA = \
        AltosFlightStatus.java \
        AltosFlightUI.java \
        AltosGPS.java \
        AltosFlightStatus.java \
        AltosFlightUI.java \
        AltosGPS.java \
+       AltosGPSSat.java \
        AltosGreatCircle.java \
        AltosHexfile.java \
        Altos.java \
        AltosGreatCircle.java \
        AltosHexfile.java \
        Altos.java \
@@ -70,6 +71,11 @@ altosui_JAVA = \
        AltosRecord.java \
        AltosRecordIterable.java \
        AltosTelemetryReader.java \
        AltosRecord.java \
        AltosRecordIterable.java \
        AltosTelemetryReader.java \
+       AltosTelemetryRecord.java \
+       AltosTelemetryRecordGeneral.java \
+       AltosTelemetryRecordRaw.java \
+       AltosTelemetryRecordSensor.java \
+       AltosTelemetryRecordLegacy.java \
        AltosTelemetryMap.java \
        AltosReplayReader.java \
        AltosRomconfig.java \
        AltosTelemetryMap.java \
        AltosReplayReader.java \
        AltosRomconfig.java \
index 32ef7d74fd2cb79d78679253ac30019e77f566a4..07d30717575188f4a0e1415e8bd1d9d2280a39a8 100644 (file)
@@ -18,7 +18,7 @@ dnl
 dnl Process this file with autoconf to create configure.
 
 AC_PREREQ(2.57)
 dnl Process this file with autoconf to create configure.
 
 AC_PREREQ(2.57)
-AC_INIT([altos], 0.9.4)
+AC_INIT([altos], 0.9.4.99)
 AC_CONFIG_SRCDIR([src/ao.h])
 AM_INIT_AUTOMAKE([foreign dist-bzip2])
 AM_MAINTAINER_MODE
 AC_CONFIG_SRCDIR([src/ao.h])
 AM_INIT_AUTOMAKE([foreign dist-bzip2])
 AM_MAINTAINER_MODE