1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.diyefi.openlogviewer.decoder;
24
25
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.BufferedInputStream;
29 import java.io.IOException;
30 import java.util.Arrays;
31 import java.util.ResourceBundle;
32
33 import org.diyefi.openlogviewer.OpenLogViewer;
34 import org.diyefi.openlogviewer.decoder.LogField.types;
35 import org.diyefi.openlogviewer.genericlog.GenericLog;
36
37
38
39
40
41
42 public class FreeEMSBin extends AbstractDecoder {
43 private static final int initialLength = 75000;
44 private static final int loadFactor = 2;
45
46 private static final int MINIMUM_PACKET_LENGTH = 3;
47 private static final int MAXIMUM_PACKET_LENGTH = 0x0820;
48
49
50
51 private static final short ESCAPE_BYTE = 0xBB;
52 private static final short START_BYTE = 0xAA;
53 private static final short STOP_BYTE = 0xCC;
54 private static final short ESCAPED_ESCAPE_BYTE = 0x44;
55 private static final short ESCAPED_START_BYTE = 0x55;
56 private static final short ESCAPED_STOP_BYTE = 0x33;
57
58 private boolean startFound;
59 private final ResourceBundle labels;
60 private final File logFile;
61 private final short[] packetBuffer;
62 private final GenericLog decodedLog;
63 private final Thread t;
64 private int packetLength;
65 private int firstPayloadIDFound = -1;
66
67 private final long startTime;
68
69
70 private int tempClockIndex;
71 private int lastTempClockValue;
72 private boolean firstTempClockStored = false;
73
74
75 private int clockInMilliSecondsIndex;
76 private int lastClockInMilliSecondsValue;
77 private boolean firstClockInMilliSecondsStored = false;
78 private long accurateClock;
79
80 private static String[] coreStatusAFlagNames = {
81 "CS-FuelPumpPrime",
82 "CS-unused1",
83 "CS-unused2",
84 "CS-unused3",
85 "CS-unused4",
86 "CS-unused5",
87 "CS-unused6",
88 "CS-unused7"
89 };
90
91 private static String[] decoderFlagsFlagNames = {
92 "DF-CombustionSync",
93 "DF-CrankSync",
94 "DF-CamSync",
95 "DF-LAST_TIMESTAMP_VALID",
96 "DF-LAST_PERIOD_VALID",
97 "DF-LAST_MATCH_VALID",
98 "DF-Spare-6",
99 "DF-Spare-7"
100 };
101
102 private static String[] ignitionLimiterFlagsNames = {
103 "LIG-RPM",
104 "LIG-OverBoost",
105 "LIG-Spare0",
106 "LIG-Spare1",
107 "LIG-Spare2",
108 "LIG-Spare3",
109 "LIG-Spare4",
110 "LIG-Spare5"
111 };
112
113 private static String[] injectionLimiterFlagsNames = {
114 "LIN-RPM",
115 "LIN-OverBoost",
116 "LIN-Spare0",
117 "LIN-Spare1",
118 "LIN-Spare2",
119 "LIN-Spare3",
120 "LIN-Spare4",
121 "LIN-Spare5"
122 };
123
124 private static String[] flaggableFlagsNames = {
125 "FF-callsToUISRs",
126 "FF-lowVoltageConditions",
127 "FF-decoderSyncLosses",
128 "FF-decoderSyncCorrections",
129 "FF-decoderSyncStateClears",
130 "FF-serialNoiseErrors",
131 "FF-serialFramingErrors",
132 "FF-serialParityErrors",
133 "FF-serialOverrunErrors",
134 "FF-serialEscapePairMismatches",
135 "FF-serialStartsInsideAPacket",
136 "FF-serialPacketsOverLength",
137 "FF-serialChecksumMismatches",
138 "FF-serialPacketsUnderLength",
139 "FF-commsDebugMessagesNotSent",
140 "FF-commsErrorMessagesNotSent"
141 };
142
143
144 private static LogField[] fields = {
145
146 new LogField("IAT", 100, -273.15),
147 new LogField("CHT", 100, -273.15),
148 new LogField("TPS", 640),
149 new LogField("EGO", 32768),
150 new LogField("MAP", 100),
151 new LogField("AAP", 100),
152 new LogField("BRV", 1000),
153 new LogField("MAT", 100, -273.15),
154 new LogField("EGO2", 32768),
155 new LogField("IAP", 100),
156 new LogField("MAF"),
157 new LogField("DMAP"),
158 new LogField("DTPS"),
159 new LogField("RPM", 2),
160 new LogField("DRPM"),
161 new LogField("DDRPM"),
162
163
164 new LogField("LoadMain", 512),
165 new LogField("VEMain", 512),
166 new LogField("Lambda", 32768),
167 new LogField("AirFlow"),
168 new LogField("densityAndFuel"),
169 new LogField("BasePW", 1250),
170 new LogField("ETE", (100.0 / 16384.0)),
171 new LogField("TFCTotal", 1250),
172 new LogField("EffectivePW", 1250),
173 new LogField("IDT", 1250),
174 new LogField("RefPW", 1250),
175 new LogField("Advance", 50),
176 new LogField("Dwell", 1250),
177
178
179 new LogField("tempClock", types.UINT8),
180 new LogField("spareChar", types.UINT8),
181 new LogField("coreStatusA", types.BITS8, coreStatusAFlagNames),
182 new LogField("decoderFlags", types.BITS8, decoderFlagsFlagNames),
183 new LogField("flaggableFlags", types.BITS16, flaggableFlagsNames),
184 new LogField("currentEvent", types.UINT8),
185 new LogField("syncLostWithThisID", types.UINT8),
186 new LogField("syncLostOnThisEvent", types.UINT8),
187 new LogField("syncCaughtOnThisEvent", types.UINT8),
188 new LogField("syncResetCalls", types.UINT8),
189 new LogField("primaryTeethSeen", types.UINT8),
190 new LogField("secondaryTeethSeen", types.UINT8),
191 new LogField("serialOverrunErrors", types.UINT8),
192 new LogField("serialHardwareErrors", types.UINT8),
193 new LogField("serialAndCommsCodeErrors", types.UINT8),
194 new LogField("inputEventTimeTolerance"),
195 new LogField("zsp10"),
196 new LogField("zsp9"),
197 new LogField("zsp8"),
198 new LogField("zsp7"),
199 new LogField("zsp6"),
200 new LogField("zsp5"),
201 new LogField("zsp4"),
202 new LogField("zsp3"),
203 new LogField("clockInMilliSeconds"),
204 new LogField("clock8thMSsInMillis", 8),
205 new LogField("ignitionLimiterFlags", types.BITS8, ignitionLimiterFlagsNames),
206 new LogField("injectionLimiterFlags", types.BITS8, injectionLimiterFlagsNames)
207 };
208
209
210
211
212
213
214
215
216 public FreeEMSBin(final String path, final ResourceBundle labels) {
217 this(new File(path), labels);
218 }
219
220
221
222
223
224 public FreeEMSBin(final File f, final ResourceBundle labels) {
225 this.labels = labels;
226 startTime = System.currentTimeMillis();
227 logFile = f;
228 startFound = false;
229 packetBuffer = new short[MAXIMUM_PACKET_LENGTH];
230 packetLength = 0;
231
232
233
234
235 final String[] headers = new String[fields.length * 32];
236 int headersPosition = 0;
237 for (int i = 0; i < fields.length; i++) {
238 if ((fields[i].getType() == types.BITS8) || (fields[i].getType() == types.BITS16) || (fields[i].getType() == types.BITS32)) {
239 for (int j = 0; j < fields[i].getBitFieldNames().length; j++) {
240 final String flagID = getFlagName(fields[i], j);
241 headers[headersPosition] = flagID;
242 headersPosition++;
243 }
244 } else {
245 final String fieldID = fields[i].getID();
246 headers[headersPosition] = fieldID;
247 if("clockInMilliSeconds".equals(fieldID)){
248 clockInMilliSecondsIndex = i;
249 }else if("tempClock".equals(fieldID)){
250 tempClockIndex = i;
251 }
252 headersPosition++;
253 }
254 }
255
256 decodedLog = new GenericLog(Arrays.copyOfRange(headers, 0, headersPosition), initialLength, loadFactor, labels);
257
258 t = new Thread(this, "FreeEMSBin Loading");
259 t.setPriority(Thread.MAX_PRIORITY);
260 t.start();
261 }
262
263
264
265
266
267
268 @Override
269 public final void run() {
270 FileInputStream fis = null;
271 BufferedInputStream bis = null;
272 try {
273
274 final byte[] readByte = new byte[1];
275 short uByte = 0;
276
277
278 int escapePairMismatches = 0;
279 int startsInsideAPacket = 0;
280 int packetsOverLength = 0;
281 int packetsUnderLength = 0;
282 int checksumMismatches = 0;
283 int packetsParsedFully = 0;
284 int packetLengthWrong = 0;
285 int strayBytesLost = 0;
286 int payloadIDWrong = 0;
287
288 startFound = false;
289 fis = new FileInputStream(logFile);
290 bis = new BufferedInputStream(fis);
291 decodedLog.setLogStatus(GenericLog.LogState.LOG_LOADING);
292 while (bis.read(readByte) != -1) {
293 uByte = unsignedValueOf(readByte[0]);
294 if (uByte == START_BYTE) {
295 if (!startFound) {
296 startFound = true;
297 } else {
298 startsInsideAPacket++;
299 }
300 packetLength = 0;
301 } else if (startFound) {
302 if (uByte == STOP_BYTE) {
303 if (packetLength < MINIMUM_PACKET_LENGTH) {
304 packetsUnderLength++;
305 } else if (packetLength > MAXIMUM_PACKET_LENGTH) {
306 packetsOverLength++;
307 } else {
308 decodedLog.incrementPosition();
309 final short[] justThePacket = Arrays.copyOfRange(packetBuffer, 0, packetLength);
310 if (checksum(justThePacket)) {
311 if (decodeBasicLogPacket(justThePacket)) {
312 packetsParsedFully++;
313 } else {
314 packetLengthWrong++;
315 payloadIDWrong++;
316 }
317 } else {
318 checksumMismatches++;
319 }
320 }
321 startFound = false;
322 } else if (uByte == ESCAPE_BYTE) {
323 if (bis.read(readByte) != -1) {
324 uByte = unEscape(unsignedValueOf(readByte[0]));
325 if (uByte != (short) -1) {
326 packetBuffer[packetLength] = uByte;
327 packetLength++;
328 } else {
329 startFound = false;
330 escapePairMismatches++;
331 }
332 }
333 } else {
334 if (packetLength < MAXIMUM_PACKET_LENGTH) {
335 packetBuffer[packetLength] = uByte;
336 packetLength++;
337 } else {
338 startFound = false;
339 packetsOverLength++;
340 }
341 }
342 } else {
343 strayBytesLost++;
344 }
345 }
346 decodedLog.setLogStatus(GenericLog.LogState.LOG_LOADED);
347
348 System.out.println(OpenLogViewer.NEWLINE + "Binary Parsing Statistics:" + OpenLogViewer.NEWLINE);
349
350 System.out.println("EscapePairMismatches: " + escapePairMismatches + " Incremented when an escape is found but not followed by an escapee");
351 System.out.println("StartsInsideAPacket: " + startsInsideAPacket + " Incremented when a start byte is found inside a packet");
352 System.out.println("PacketsOverLength: " + packetsOverLength + " Incremented when the buffer fills up before the end");
353 System.out.println("PacketsUnderLength: " + packetsUnderLength + " Incremented when a packet is found that is too short");
354 System.out.println("ChecksumMismatches: " + checksumMismatches + " Incremented when calculated checksum did not match the received one");
355 System.out.println("PacketsParsedFully: " + packetsParsedFully + " Number of packets that matched all requirements and got into the log");
356 System.out.println("PacketLengthWrong: " + packetLengthWrong + " The length should match the ID passed in, if not, fail.");
357 System.out.println("StrayBytesLost: " + strayBytesLost + " How many bytes were not in a packet! (should be low, ie, under one packet");
358 System.out.println("PayloadIDWrong: " + payloadIDWrong + " Requests to parse packet as A when packet was of type B");
359
360 System.out.println(OpenLogViewer.NEWLINE + "Thank you for choosing FreeEMS!");
361
362 } catch (Exception e) {
363 e.printStackTrace();
364 decodedLog.setLogStatusMessage(e.getMessage());
365 } finally {
366 OpenLogViewer.getInstance().getEntireGraphingPanel().setGraphSize(decodedLog.getRecordCount());
367 decodedLog.setLogStatus(GenericLog.LogState.LOG_LOADED);
368 System.out.println("Loaded " + (decodedLog.getRecordCount() + 1) + " records in " + (System.currentTimeMillis() - startTime) + " millis!");
369
370 try {
371 if (bis != null) {
372 bis.close();
373 }
374 } catch (IOException ioe) {
375 ioe.printStackTrace();
376 System.out.println("Failed To Close BIS Stream!");
377 }
378
379 try {
380 if (fis != null) {
381 fis.close();
382 }
383 } catch (IOException ioe) {
384 ioe.printStackTrace();
385 System.out.println("Failed To Close FIS Stream!");
386 }
387 }
388 }
389
390
391
392
393
394
395
396 private boolean decodeBasicLogPacket(final short[] packet) {
397 final int HEADER_HAS_LENGTH_INDEX = 0;
398
399 final int HEADER_HAS_SEQUENCE_INDEX = 2;
400
401
402
403
404
405
406
407
408 int position = 0;
409
410 final short flags = packet[position];
411 position++;
412
413 final short payloadIdUpper = packet[position];
414 position++;
415 final short payloadIdLower = packet[position];
416 position++;
417 final int payloadId = (payloadIdUpper * 256) + payloadIdLower;
418
419 if (firstPayloadIDFound < 0) {
420 firstPayloadIDFound = payloadId;
421 } else if (payloadId != firstPayloadIDFound) {
422 return false;
423 }
424
425
426
427 final int[] flagValues = processFlagBytes(flags, 8);
428
429 if (flagValues[HEADER_HAS_SEQUENCE_INDEX] == 1) {
430 position++;
431 }
432
433 if (flagValues[HEADER_HAS_LENGTH_INDEX] == 1) {
434 position += 2;
435 }
436
437 int lengthOfFields = 0;
438 for (int i = 0; i < fields.length; i++) {
439 lengthOfFields += fields[i].getType().getWidth();
440 }
441
442
443
444 final int payloadLength = ((packet.length - position) - 1);
445 if (payloadLength != lengthOfFields) {
446 System.out.print(" Fields length is: " + lengthOfFields);
447 System.out.print(" Packet length is: " + packet.length);
448 System.out.println(" Payload length is: " + payloadLength);
449 return false;
450 }
451
452 for (int i = 0; i < fields.length; i++) {
453 final LogField field = fields[i];
454
455 int rawValue = 0;
456 for (int j = 0; j < field.getType().getWidth(); j++) {
457 rawValue = (rawValue * 256) + packet[position];
458 position++;
459 }
460
461 if (i == clockInMilliSecondsIndex) {
462 if (firstClockInMilliSecondsStored) {
463 final int increase = rawValue - lastClockInMilliSecondsValue;
464
465 if(increase < 0) {
466 final int actualIncrease = rawValue + (65536 - lastClockInMilliSecondsValue);
467 accurateClock += actualIncrease;
468 } else {
469 accurateClock += increase;
470 }
471
472 final double currentClock = (double) accurateClock / 1000d;
473 decodedLog.addValue(GenericLog.elapsedTimeKey, currentClock);
474 } else {
475 firstClockInMilliSecondsStored = true;
476 }
477
478 lastClockInMilliSecondsValue = rawValue;
479 } else if (i == tempClockIndex) {
480 if (firstTempClockStored) {
481 if (rawValue == 0) {
482 if (lastTempClockValue != 255) {
483 decodedLog.addValue(GenericLog.tempResetKey, 1);
484 }
485 } else {
486 final int dropOutLength = (rawValue - lastTempClockValue);
487 if (dropOutLength != 1) {
488 final double resetValue = (double) (1000 + dropOutLength) / -1000d;
489 decodedLog.addValue(GenericLog.tempResetKey, resetValue);
490 }
491 }
492 } else {
493 firstTempClockStored = true;
494 }
495
496 lastTempClockValue = rawValue;
497 }
498
499 if ((field.getType() == types.UINT8) || (field.getType() == types.UINT16) || (field.getType() == types.UINT32)) {
500 final double scaledValue = (double) rawValue / field.getDivBy();
501 final double finalValue = scaledValue + field.getAddTo();
502 decodedLog.addValue(field.getID(), finalValue);
503 } else if ((field.getType() == types.BITS8) || (field.getType() == types.BITS16) || (field.getType() == types.BITS32)) {
504 final int[] processedFlags = processFlagBytes(rawValue, (8 * field.getType().getWidth()));
505 for (int j = 0; j < processedFlags.length; j++) {
506 final String flagID = getFlagName(field, j);
507 decodedLog.addValue(flagID, processedFlags[j]);
508 }
509 } else if (field.getType() == types.SINT8) {
510 decodedLog.addValue(field.getID(), rawValue);
511 } else if (field.getType() == types.SINT16) {
512 decodedLog.addValue(field.getID(), rawValue);
513 } else if (field.getType() == types.SINT32) {
514 decodedLog.addValue(field.getID(), rawValue);
515 }
516
517 }
518 return true;
519 }
520
521 private int[] processFlagBytes(final long valueOfFlags, final int numberOfFlags) {
522 if ((numberOfFlags != 8) && (numberOfFlags != 16) && (numberOfFlags != 32)) {
523 throw new IllegalArgumentException("Basic units of computer sciene apply, embedded flags are never " + numberOfFlags + " wide!");
524
525 }
526
527 final int[] flagValues = new int[numberOfFlags];
528 int comparison = 1;
529 long remainingValueOfFlags = valueOfFlags;
530 for (int i = 0; i < numberOfFlags; i++) {
531 if ((remainingValueOfFlags % (2 * comparison)) == comparison) {
532 flagValues[i] = 1;
533 remainingValueOfFlags -= comparison;
534 }
535 comparison *= 2;
536 }
537
538 return flagValues;
539 }
540
541
542
543
544
545
546
547 private boolean checksum(final short[] packet) {
548 if (packetLength > 0) {
549 final short includedSum = packet[packetLength - 1];
550 long veryBIGsum = 0;
551 for (int x = 0; x < packetLength - 1; x++) {
552 veryBIGsum += packet[x];
553 }
554 final short calculatedSum = (short) (veryBIGsum % 256);
555 return (calculatedSum == includedSum);
556 } else {
557 return false;
558 }
559 }
560
561
562
563
564
565
566
567 private short unEscape(final short uByte) {
568 if (uByte == ESCAPED_START_BYTE) {
569 return START_BYTE;
570 } else if (uByte == ESCAPED_STOP_BYTE) {
571 return STOP_BYTE;
572 } else if (uByte == ESCAPED_ESCAPE_BYTE) {
573 return ESCAPE_BYTE;
574 } else {
575 return (short) -1;
576 }
577 }
578
579
580
581
582
583
584
585 private short unsignedValueOf(final byte uInt8) {
586 return (short) (0xFF & uInt8);
587 }
588
589
590
591
592
593
594 @Override
595 public final String toString() {
596 return super.toString();
597 }
598
599 private String getFlagName(final LogField field, final int flagIndex) {
600 return field.getBitFieldNames()[flagIndex] + "-" + field.getID() + "-B" + flagIndex;
601 }
602 }