summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2017-03-12 04:05:42 -0400
committerLuke Shumaker <lukeshu@lukeshu.com>2017-03-12 04:05:42 -0400
commita6db0254bf9bc3f4ed1058c20cf294b66d455be4 (patch)
treedbdd79b3cf4b1fa9ab315b93a8f353d38bcfe92f
initial commit
-rw-r--r--Makefile6
-rw-r--r--dslog/DseventsReader.java56
-rw-r--r--dslog/DslogReader.java156
-rw-r--r--dslog/Main.java65
-rw-r--r--dslog/Read.java143
-rw-r--r--dslog/Util.java51
6 files changed, 477 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7813a81
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,6 @@
+classes = $(patsubst %.java,%,$(notdir $(wildcard dslog/*.java)))
+
+all: $(foreach c,$(classes),dslog/$c.class)
+
+%.class: %.java
+ javac $<
diff --git a/dslog/DseventsReader.java b/dslog/DseventsReader.java
new file mode 100644
index 0000000..c3c7dc5
--- /dev/null
+++ b/dslog/DseventsReader.java
@@ -0,0 +1,56 @@
+package dslog;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.UnsupportedOperationException;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.stream.IntStream;
+
+class DseventsReader {
+ private final InputStream reader;
+
+ public final int version;
+ public final Instant startTime;
+
+ public DseventsReader(InputStream reader) throws IOException {
+ this.reader = reader;
+ this.version = Read.i32(reader);
+ switch (this.version) {
+ case 0:
+ throw new UnsupportedOperationException("DSEVENTS version 0 not implemented yet");
+ case 1:
+ case 2:
+ case 3: // 2016-now
+ this.startTime = Read.LVTimestamp(reader);
+ break;
+ default:
+ throw new UnsupportedOperationException("DSEVENTS file version ("+this.version+") newer than log reader (3)");
+ }
+ }
+
+ public static class Event {
+ public Instant time;
+ public String message;
+ }
+
+ public Event readEvent() throws IOException{
+ switch (version) {
+ case 0:
+ throw new UnsupportedOperationException("DSEVENTS version 0 not implemented yet");
+ case 1:
+ case 2:
+ case 3:
+ return new Event() {{
+ time = Read.LVTimestamp(reader);
+ message = Read.LVString(reader);
+ }};
+ default:
+ throw new UnsupportedOperationException("DSEVENTS file version newer than log reader");
+ }
+ }
+
+ public void close() throws IOException {
+ reader.close();
+ }
+}
diff --git a/dslog/DslogReader.java b/dslog/DslogReader.java
new file mode 100644
index 0000000..3a3b211
--- /dev/null
+++ b/dslog/DslogReader.java
@@ -0,0 +1,156 @@
+package dslog;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.UnsupportedOperationException;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.stream.IntStream;
+
+class DslogReader {
+ private final InputStream reader;
+
+ public final int version;
+ public final Instant startTime;
+ private long i = 0;
+
+ public DslogReader(InputStream reader) throws IOException {
+ this.reader = reader;
+ this.version = Read.i32(reader);
+ switch (this.version) {
+ case 0: // ????-???? 2012
+ this.startTime = Read.LVTimestamp(reader);
+ break;
+ case 1: // ????-???? 2014
+ this.startTime = Read.LVTimestamp(reader);
+ break;
+ case 2: // ????-2015
+ throw new UnsupportedOperationException("DSLOG version 2 not implemented yet");
+ case 3: // 2016-now
+ this.startTime = Read.LVTimestamp(reader);
+ break;
+ default:
+ throw new UnsupportedOperationException("DSLOG file version ("+this.version+") newer than log reader (3)");
+ }
+ }
+
+ public static class Entry {
+ public Instant time;
+
+ public Duration tripTime;
+ public short lostPackets;
+ public double voltageVolts;
+ public double cpuPct;
+ public boolean
+ robotDisable,
+ robotAuto,
+ robotTele,
+ dsDisable,
+ dsAuto,
+ dsTele,
+ watchdog,
+ brownout;
+ public double canPct;
+ public double signalDB;
+ public double bandwidthMb;
+ public short pdpID;
+ public short pdpPad;
+ public double pdpValues[];
+ public short pdpResistance;
+ public short pdpVoltage;
+ public short pdpTemperature;
+ }
+
+ /**
+ * @see https://www.chiefdelphi.com/forums/showthread.php?t=104689
+ */
+ public Entry readEntry() throws IOException{
+ switch (version) {
+ case 0:
+ return new Entry() {{
+ time = startTime.plusMillis(20*i++);
+
+ /* 0 = 0 */
+ /* 0+4= 4 *///tripTime = Read.f32(reader);
+ /* 4+4= 8 */voltageVolts = Read.f32(reader);
+ /* 8+4=12 */cpuPct = Read.f32(reader);
+ /* 12+1=13 */short bits = Read.u8(reader);
+ robotDisable = (bits & (1<<2)) == 0;
+ robotAuto = (bits & (1<<3)) == 0;
+ robotTele = (bits & (1<<4)) == 0;
+ dsDisable = (bits & (1<<5)) == 0;
+ dsAuto = (bits & (1<<6)) == 0;
+ dsTele = (bits & (1<<7)) == 0;
+ /* 13+1=14 */lostPackets = Read.i8(reader);
+ }};
+ case 1:
+ return new Entry() {{
+ time = startTime.plusMillis(20*i++);
+
+ time = startTime.plusMillis(20*i++);
+
+ /* 0 = 0 */
+ /* 0+4= 4 *///tripTime = Read.f32(reader);
+ /* 4+4= 8 */voltageVolts = Read.f32(reader);
+ /* 8+4=12 */cpuPct = Read.f32(reader);
+ /* 12+1=13 */short bits = Read.u8(reader);
+ robotDisable = (bits & (1<<0)) == 0;
+ robotAuto = (bits & (1<<1)) == 0;
+ robotTele = (bits & (1<<2)) == 0;
+ dsDisable = (bits & (1<<3)) == 0;
+ dsAuto = (bits & (1<<4)) == 0;
+ dsTele = (bits & (1<<5)) == 0;
+ /* 13+1=14 */lostPackets = Read.i8(reader);
+ }};
+ case 2:
+ return new Entry();
+ case 3:
+ return new Entry() {{
+ time = startTime.plusMillis(20*i++);
+ // Read the 35-byte structure
+
+ // Read the 10 bytes of non-PDP data
+
+ /* 0 = 0 */
+ /* 0+1= 1 */tripTime = Duration.ofNanos((long)(Util.unpack(Read.u8(reader), 7,1)*1_000_000));
+ /* 1+1= 2 */lostPackets = Read.u8(reader);
+ /* 2+2= 4 */voltageVolts = Util.unpack(Read.u16(reader), 8,8);
+ /* 5+1= 5 */cpuPct = Util.unpack(Read.u8(reader), 7,1);
+ /* 5+1= 6 */short bits = Read.u8(reader);
+ robotDisable = (bits & (1<<0)) == 0;
+ robotAuto = (bits & (1<<1)) == 0;
+ robotTele = (bits & (1<<2)) == 0;
+ dsDisable = (bits & (1<<3)) == 0;
+ dsAuto = (bits & (1<<4)) == 0;
+ dsTele = (bits & (1<<5)) == 0;
+ watchdog = (bits & (1<<6)) == 0;
+ brownout = (bits & (1<<7)) == 0;
+ /* 6+1= 7 */canPct = Util.unpack(Read.u8(reader), 7,1);
+ /* 7+1= 8 */signalDB = Util.unpack(Read.u8(reader), 7,1);
+ /* 8+2=10 */bandwidthMb = Util.unpack(Read.u16(reader), 8,8);
+
+ // Read the 25 bytes of PDP data
+
+ /* 0 = 0 */
+ /* 0+1= 1 */pdpPad = Read.u8(reader);
+ /* 1+1= 2 */pdpID = Read.u8(reader);
+ pdpValues = IntStream.of(Util.reverse(Util.concat(
+ /* 2+5= 7 */ Read.u10x4(reader),
+ /* 7+5=12 */ Read.u10x4(reader),
+ /* 12+5=17 */ Read.u10x4(reader),
+ /* 17+5=22 */ Read.u10x4(reader))))
+ .mapToDouble(i -> Util.unpack((short)i, 7,3))
+ .toArray();
+ /* 22+1=23 */pdpResistance = Read.u8(reader);
+ /* 23+1=24 */pdpVoltage = Read.u8(reader);
+ /* 24+1=25 */pdpTemperature = Read.u8(reader);
+ }};
+ default:
+ throw new UnsupportedOperationException("DSLOG file version newer than log reader");
+ }
+ }
+
+ public void close() throws IOException {
+ reader.close();
+ }
+}
diff --git a/dslog/Main.java b/dslog/Main.java
new file mode 100644
index 0000000..c8c93fb
--- /dev/null
+++ b/dslog/Main.java
@@ -0,0 +1,65 @@
+package dslog;
+
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+class Main {
+ public static String line(DslogReader.Entry e) {
+ return
+ e.time+","+
+ e.tripTime+","+
+ e.lostPackets+","+
+ e.voltageVolts+","+
+ e.cpuPct+","+
+ e.robotDisable+","+
+ e.robotAuto+","+
+ e.robotTele+","+
+ e.dsDisable+","+
+ e.dsAuto+","+
+ e.dsTele+","+
+ e.watchdog+","+
+ e.brownout+","+
+ e.canPct+","+
+ e.signalDB+","+
+ e.bandwidthMb+","+
+ e.pdpID+","+
+ e.pdpPad+","+
+ e.pdpValues[0]+","+
+ e.pdpValues[1]+","+
+ e.pdpValues[2]+","+
+ e.pdpValues[3]+","+
+ e.pdpValues[4]+","+
+ e.pdpValues[5]+","+
+ e.pdpValues[6]+","+
+ e.pdpValues[7]+","+
+ e.pdpValues[8]+","+
+ e.pdpValues[9]+","+
+ e.pdpValues[10]+","+
+ e.pdpValues[11]+","+
+ e.pdpValues[12]+","+
+ e.pdpValues[13]+","+
+ e.pdpValues[14]+","+
+ e.pdpValues[15]+","+
+ e.pdpResistance+","+
+ e.pdpVoltage+","+
+ e.pdpTemperature;
+ }
+ public static void main(String[] args) throws IOException {
+ for (String filename : args) {
+ System.out.println("Filename: "+filename);
+ DslogReader file = new DslogReader(new FileInputStream(filename));
+ System.out.println("Format Version: "+file.version);
+ System.out.println("Start Time: "+file.startTime);
+ System.out.println("time,tripTime,lost (packets),voltage (V),cpu (%),robot disable,robot auto,robot tele,ds disable,ds auto,ds tele,watchdog,brownout,CAN (%),signal (dB),bandwidth (Mb),PDP ID,PDP pad,PDP-0,PDP-1,PDP-2,PDP-3,PDP-4,PDP-5,PDP-6,PDP-7,PDP-8,PDP-9,PDP-10,PDP-11,PDP-12,PDP-13,PDP-14,PDP-15,PDP Resistance,PDP Voltage,PDP Temperature");
+ for (;;) {
+ try {
+ System.out.println(line(file.readEntry()));
+ } catch (EOFException e) {
+ break;
+ }
+ }
+ file.close();
+ }
+ }
+}
diff --git a/dslog/Read.java b/dslog/Read.java
new file mode 100644
index 0000000..04537d5
--- /dev/null
+++ b/dslog/Read.java
@@ -0,0 +1,143 @@
+package dslog;
+
+import java.math.BigInteger;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.EOFException;
+import java.time.Instant;
+
+class Read {
+ public static void full(InputStream in, byte b[]) throws IOException {
+ int n = 0;
+ while (n < b.length) {
+ int count = in.read(b, n, b.length - n);
+ if (count < 0)
+ throw new EOFException();
+ n += count;
+ }
+ }
+
+ public static short u8(InputStream in) throws IOException {
+ int n = in.read();
+ if (n < 0)
+ throw new EOFException();
+ return (short)n;
+ }
+
+ public static byte i8(InputStream in) throws IOException {
+ return (byte)u8(in);
+ }
+
+
+ public static int u16(InputStream in) throws IOException {
+ byte bytes[] = new byte[2];
+ full(in, bytes);
+ return (((bytes[0] & 0xff) << 8) +
+ ((bytes[1] & 0xff) << 0));
+ }
+
+ public static short i16(InputStream in) throws IOException {
+ return (short)u16(in);
+ }
+
+ public static int i32(InputStream in) throws IOException {
+ byte bytes[] = new byte[4];
+ full(in, bytes);
+ return (((bytes[0] & 0xff) << 32) +
+ ((bytes[1] & 0xff) << 16) +
+ ((bytes[2] & 0xff) << 8) +
+ ((bytes[3] & 0xff) << 0));
+ }
+
+ public static long u32(InputStream in) throws IOException {
+ byte bytes[] = new byte[4];
+ full(in, bytes);
+ return (((long)(bytes[0] & 0xff) << 32) +
+ ((long)(bytes[1] & 0xff) << 16) +
+ ((long)(bytes[2] & 0xff) << 8) +
+ ((long)(bytes[3] & 0xff) << 0));
+ }
+
+ public static long i64(InputStream in) throws IOException {
+ byte bytes[] = new byte[8];
+ full(in, bytes);
+ return (((long)(bytes[0] & 0xff) << 56) +
+ ((long)(bytes[1] & 0xff) << 48) +
+ ((long)(bytes[2] & 0xff) << 40) +
+ ((long)(bytes[3] & 0xff) << 32) +
+ ((long)(bytes[4] & 0xff) << 24) +
+ ((long)(bytes[5] & 0xff) << 16) +
+ ((long)(bytes[6] & 0xff) << 8) +
+ ((long)(bytes[7] & 0xff) << 0));
+ }
+
+ public static BigInteger u64(InputStream in) throws IOException {
+ byte bytes[] = new byte[8];
+ full(in, bytes);
+ return BigInteger.valueOf(0)
+ .add(BigInteger.valueOf(bytes[0] & 0xff).shiftLeft(56))
+ .add(BigInteger.valueOf(bytes[1] & 0xff).shiftLeft(48))
+ .add(BigInteger.valueOf(bytes[2] & 0xff).shiftLeft(40))
+ .add(BigInteger.valueOf(bytes[3] & 0xff).shiftLeft(32))
+ .add(BigInteger.valueOf(bytes[4] & 0xff).shiftLeft(24))
+ .add(BigInteger.valueOf(bytes[5] & 0xff).shiftLeft(16))
+ .add(BigInteger.valueOf(bytes[6] & 0xff).shiftLeft( 8))
+ .add(BigInteger.valueOf(bytes[7] & 0xff).shiftLeft( 0));
+ }
+
+ public static float f32(InputStream in) throws IOException {
+ return Float.intBitsToFloat(i32(in));
+ }
+
+ public static double f64(InputStream in) throws IOException {
+ return Double.longBitsToDouble(i64(in));
+ }
+
+ public static int[] u10x4(InputStream in) throws IOException {
+ byte bytes[] = new byte[5];
+ full(in, bytes);
+ return new int[]{
+ ((bytes[0] & 0b1111_1111) << 2)+
+ ((bytes[1] & 0b1100_0000) >> 6),
+ ((bytes[1] & 0b0011_1111) << 4)+
+ ((bytes[2] & 0b1111_0000) >> 4),
+ ((bytes[2] & 0b0000_1111) << 6)+
+ ((bytes[3] & 0b1111_1100) >> 2),
+ ((bytes[3] & 0b0000_1111) << 8)+
+ ((bytes[4] & 0b1111_1111) >> 0),
+ };
+ }
+
+ public static final Instant LABVIEW_EPOCH = Instant.parse("1904-01-01T00:00:00.00Z");
+
+ /**
+ * @bug LabVIEW timestamps have greater precision
+ * (sub-attosecond) than Java time.Instant (nanosecond)
+ * or Java util.Date (millisecond), so converting to a
+ * native Java timestamp necessarily loses some
+ * precision.
+ *
+ * @see http://www.ni.com/tutorial/7900/en/
+ */
+ public static Instant LVTimestamp(InputStream in) throws IOException {
+ // seconds since the epoch 01/01/1904 00:00:00.00 UTC
+ // (using the Gregorian calendar and ignoring leap
+ // seconds),
+ long secs = i64(in);
+ // positive fractions of a second; 2¯⁶⁴ths of a second
+ BigInteger frac = u64(in);
+ // To get from (2¯⁶⁴s to ns, we need to divide by
+ // precisely 18446744073.709551616 (=10¯⁹/2¯⁶⁴)... but
+ // rounding up already gives us sub-nanosecond
+ // precision, so don't worry about the .709...
+ long nanos = frac.divide(BigInteger.valueOf(18446744074L)).longValue();
+ return LABVIEW_EPOCH.plusSeconds(secs).plusNanos(nanos);
+ }
+
+ public static String LVString(InputStream in) throws IOException {
+ int len = i32(in);
+ byte[] bytes = new byte[len];
+ full(in, bytes);
+ return new String(bytes);
+ }
+}
diff --git a/dslog/Util.java b/dslog/Util.java
new file mode 100644
index 0000000..b430af9
--- /dev/null
+++ b/dslog/Util.java
@@ -0,0 +1,51 @@
+package dslog;
+
+import java.io.InputStream;
+
+class Util {
+ public static int[] concat(int[]... lists) {
+ int len = 0;
+ for (int[] list : lists) {
+ len += list.length;
+ }
+ int[] ret = new int[len];
+ int n = 0;
+ for (int[] list : lists) {
+ System.arraycopy(list, 0, ret, n, list.length);
+ n += list.length;
+ }
+ return ret;
+ }
+
+ public static int[] reverse(int[] list) {
+ int mid = list.length / 2;
+ int tmp;
+ for (int i = 0; i < mid; i++) {
+ tmp = list[i];
+ list[i] = list[list.length - i - 1];
+ list[list.length - i - 1] = tmp;
+ }
+ return list;
+ }
+
+ public static double unpack(short val, int whole, int frac) {
+ switch (whole + frac) {
+ case 8:
+ case 10:
+ break;
+ default:
+ throw new IllegalArgumentException(whole+"+"+frac+" != 8|10");
+ }
+ return val / Math.pow(2, frac);
+ }
+
+ public static double unpack(int val, int whole, int frac) {
+ switch (whole + frac) {
+ case 16:
+ break;
+ default:
+ throw new IllegalArgumentException(whole+"+"+frac+" != 16");
+ }
+ return val / Math.pow(2, frac);
+ }
+}