View Javadoc

1   /* Open Log Viewer
2    *
3    * Copyright 2011 Fred Cooke
4    *
5    * This file is part of the OpenLogViewer project.
6    *
7    * OpenLogViewer software is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU General Public License as published by
9    * the Free Software Foundation, either version 3 of the License, or
10   * (at your option) any later version.
11   *
12   * OpenLogViewer software is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with any OpenLogViewer software.  If not, see http://www.gnu.org/licenses/
19   *
20   * I ask that if you make any changes to this file you fork the code on github.com!
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   * This function takes a binary log file, plucks FreeEMS packets out of it,
40   * filters for standard packets and parses them into fields with appropriate scaling.
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; // Flag byte, payload id word, no payload - defined by protocol
47  	private static final int MAXIMUM_PACKET_LENGTH = 0x0820; // Buffer size on FreeEMS vanilla, take this from config file eventually
48  	// NOTE, standard supported log types require their own structure definition in the code,
49  	// override-able via files, matched looked for first, then local, then fall back to code
50  
51  	private static final short ESCAPE_BYTE = 0xBB;         // Used as an unsigned byte
52  	private static final short START_BYTE = 0xAA;          // Used as an unsigned byte
53  	private static final short STOP_BYTE = 0xCC;           // Used as an unsigned byte
54  	private static final short ESCAPED_ESCAPE_BYTE = 0x44; // Used as an unsigned byte
55  	private static final short ESCAPED_START_BYTE = 0x55;  // Used as an unsigned byte
56  	private static final short ESCAPED_STOP_BYTE = 0x33;   // Used as an unsigned byte
57  
58  	private boolean startFound;
59  	private final ResourceBundle labels;
60  	private final File logFile;
61  	private final short[] packetBuffer; // For use as an unsigned byte
62  	private final GenericLog decodedLog;
63  	private final Thread t;
64  	private int packetLength; // Track packet length
65  	private int firstPayloadIDFound = -1;
66  
67  	private final long startTime; // Low tech profiling var
68  
69  	// Sequential number for reset detection
70  	private int tempClockIndex;
71  	private int lastTempClockValue;
72  	private boolean firstTempClockStored = false;
73  
74  	// Medium-high resolution time counter with good accuracy
75  	private int clockInMilliSecondsIndex;
76  	private int lastClockInMilliSecondsValue;
77  	private boolean firstClockInMilliSecondsStored = false;
78  	private long accurateClock; // Convert to floating point just before storing.
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",               // to ensure we aren't accidentally triggering unused ISRs.
126 		"FF-lowVoltageConditions",       // low voltage conditions.
127 		"FF-decoderSyncLosses",          // Number of times cam, crank or combustion sync is lost.
128 		"FF-decoderSyncCorrections",     // Definite decoder syncs found while already synced in a different position.
129 		"FF-decoderSyncStateClears",     // Sync loss called when not synced yet, thus discarding data and preventing sync.
130 		"FF-serialNoiseErrors",          // Incremented when noise is detected
131 		"FF-serialFramingErrors",        // Incremented when a framing error occurs
132 		"FF-serialParityErrors",         // Incremented when a parity error occurs
133 		"FF-serialOverrunErrors",        // Incremented when overrun occurs (duplicated in KeyUserDebug below)
134 		"FF-serialEscapePairMismatches", // Incremented when an escape is found but not followed by an escapee
135 		"FF-serialStartsInsideAPacket",  // Incremented when a start byte is found inside a packet
136 		"FF-serialPacketsOverLength",    // Incremented when the buffer fills up before the end
137 		"FF-serialChecksumMismatches",   // Incremented when calculated checksum did not match the received one
138 		"FF-serialPacketsUnderLength",   // Incremented when a packet is found that is too short
139 		"FF-commsDebugMessagesNotSent",  // Incremented when a debug message can't be sent due to the TX buffer
140 		"FF-commsErrorMessagesNotSent"   // Incremented when an error message can't be sent due to the TX buffer
141 	};
142 
143 	// This should be read from a file at some point, as it's going to be flexible. See FreeEMS/src/inc/structs.h for definitive answers.
144 	private static LogField[] fields = {
145 		// CoreVars struct contents:
146 		new LogField("IAT",  100, -273.15),   // Inlet Air Temperature           : 0.0 -   655.35       (0.01 Kelvin (/100))
147 		new LogField("CHT",  100, -273.15),   // Coolant / Head Temperature      : 0.0 -   655.35       (0.01 Kelvin (/100))
148 		new LogField("TPS",  640),   // Throttle Position Sensor        : 0.0 -   102.398438   (0.0015625 % (/640))
149 		new LogField("EGO",  32768), // Exhaust Gas Oxygen              : 0.0 -     1.99996948 (0.0000305175781 lambda (/32768))
150 		new LogField("MAP",  100),   // Manifold Absolute Pressure      : 0.0 -   655.35       (0.01 kPa (/100))
151 		new LogField("AAP",  100),   // Atmospheric Absolute Pressure   : 0.0 -   655.35       (0.01 kPa (/100))
152 		new LogField("BRV",  1000),  // Battery Reference Voltage       : 0.0 -    65.535      (0.001 Volts (/1000))
153 		new LogField("MAT",  100, -273.15),   // Manifold Air Temperature        : 0.0 -   655.35       (0.01 Kelvin (/100))
154 		new LogField("EGO2", 32768), // Exhaust Gas Oxygen              : 0.0 -     1.99996948 (0.0000305175781 lambda (/32768))
155 		new LogField("IAP",  100),   // Intercooler Absolute Pressure   : 0.0 -   655.35       (0.01 kPa (/100))
156 		new LogField("MAF"),         // Mass Air Flow                   : 0.0 - 65535.0        (raw units from lookup)
157 		new LogField("DMAP"),        // Delta MAP kPa/second or similar : 0.0 - 65535.0        (raw units from lookup)
158 		new LogField("DTPS"),        // Delta TPS %/second or similar   : 0.0 - 65535.0        (raw units from lookup)
159 		new LogField("RPM", 2),      // Revolutions Per Minute (Calced) : 0.0 - 32767.5        (0.5 RPM (/2))
160 		new LogField("DRPM"),        // Delta RPM (Calced)              : 0.0 - 65535.0        (raw units from lookup)
161 		new LogField("DDRPM"),       // Delta Delta RPM (Calced)        : 0.0 - 65535.0        (raw units from lookup)
162 
163 		// DerivedVars struct contents:
164 		new LogField("LoadMain", 512),          // Configurable unit of load, scale same as map by default
165 		new LogField("VEMain", 512),            // Volumetric Efficiency in 0 - 128%
166 		new LogField("Lambda", 32768),          // Integral Lambda 0 - 2.0
167 		new LogField("AirFlow"),                // raw intermediate, remove
168 		new LogField("densityAndFuel"),         // raw intermediate, remove
169 		new LogField("BasePW", 1250),           // Raw PW before corrections 0 - ~52ms
170 		new LogField("ETE", (100.0 / 16384.0)), // Engine Temperature Enrichment percentage correction 0 - 400%
171 		new LogField("TFCTotal", 1250),         // Transient fuel correction PW (+/-)  0 - ~52ms
172 		new LogField("EffectivePW", 1250),      // Actual PW of fuel delivery 0 - ~52ms
173 		new LogField("IDT", 1250),              // PW duration before fuel flow begins 0 - ~52ms
174 		new LogField("RefPW", 1250),            // Reference electrical PW 0 - ~52ms
175 		new LogField("Advance", 50),            // Ignition advance (scaled degrees / oneDegree(currently 50) = degrees) 0 - ~64*
176 		new LogField("Dwell", 1250),            // Dwell period, s 0 - ~52ms
177 
178 		// KeyUserDebugs struct contents
179 		new LogField("tempClock", types.UINT8),     // Incremented once per log sent, to be moved to a char TODO
180 		new LogField("spareChar", types.UINT8),     // Incremented once per log sent, to be moved to a char TODO
181 		new LogField("coreStatusA",  types.BITS8, coreStatusAFlagNames),    // Duplicated, migrate here, remove global var
182 		new LogField("decoderFlags", types.BITS8, decoderFlagsFlagNames),   // Various decoder state flags
183 		new LogField("flaggableFlags", types.BITS16, flaggableFlagsNames),  // Flags to go with our flaggables struct.
184 		new LogField("currentEvent",             types.UINT8), // Which input event was last to come in
185 		new LogField("syncLostWithThisID",       types.UINT8), // A unique identifier for the reason behind a loss of sync
186 		new LogField("syncLostOnThisEvent",      types.UINT8), // Where in the input pattern it all went very badly wrong
187 		new LogField("syncCaughtOnThisEvent",    types.UINT8), // Where in the input pattern that things started making sense
188 		new LogField("syncResetCalls",           types.UINT8), // Sum of losses, corrections and state clears
189 		new LogField("primaryTeethSeen",         types.UINT8), // Free running counters for number of input events, useful at lower RPM
190 		new LogField("secondaryTeethSeen",       types.UINT8), // Free running counters for number of input events, useful at lower RPM
191 		new LogField("serialOverrunErrors",      types.UINT8), // Incremented when an overrun occurs due to high ISR load, just a fact of life at high RPM
192 		new LogField("serialHardwareErrors",     types.UINT8), // Sum of noise, parity, and framing errors
193 		new LogField("serialAndCommsCodeErrors", types.UINT8), // Sum of checksum, escape mismatches, starts inside, and over/under length
194 		new LogField("inputEventTimeTolerance"),   // Required to tune noise rejection over RPM TODO add to LT1 and MissingTeeth
195 		new LogField("zsp10"), // Spare US variable
196 		new LogField("zsp9"),  // Spare US variable
197 		new LogField("zsp8"),  // Spare US variable
198 		new LogField("zsp7"),  // Spare US variable
199 		new LogField("zsp6"),  // Spare US variable
200 		new LogField("zsp5"),  // Spare US variable
201 		new LogField("zsp4"),  // Spare US variable
202 		new LogField("zsp3"),  // Spare US variable
203 		new LogField("clockInMilliSeconds"),       // Migrate to start of all large datalogs once analysed
204 		new LogField("clock8thMSsInMillis", 8), // Migrate to start of all large datalogs once analysed
205 		new LogField("ignitionLimiterFlags", types.BITS8, ignitionLimiterFlagsNames),
206 		new LogField("injectionLimiterFlags", types.BITS8, injectionLimiterFlagsNames)
207 	};
208 
209 	// NO default constructor, a file or path to a file MUST be given
210 	// Reason: File()'s constructors are ambiguous cannot give a null value
211 	/**
212 	 * FreeEmsBin Constructor: <code>String</code> path to your binary log
213 	 * @param path The file system path of the log file.
214 	 *
215 	 */
216 	public FreeEMSBin(final String path, final ResourceBundle labels) {
217 		this(new File(path), labels);
218 	}
219 
220 	/**
221 	 * FreeEmsBin Constructor: <code>File</code> object of your Binary log
222 	 * @param f The file reference to the log file.
223 	 */
224 	public FreeEMSBin(final File f, final ResourceBundle labels) {
225 		this.labels = labels;
226 		startTime = System.currentTimeMillis(); // Let's profile this bitch! OS style :-)
227 		logFile = f;
228 		startFound = false;
229 		packetBuffer = new short[MAXIMUM_PACKET_LENGTH];
230 		packetLength = 0;
231 
232 		// TODO put matching name grabber here
233 
234 		// Eventually pull this in from files and config etc instead of default, if it makes sense to.
235 		final String[] headers = new String[fields.length * 32]; // Hack to make it plenty big, trim afterwards...
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 	 * DecodeLog will use the current <code>logFile</code> parse through it and when required pass the job <br>
266 	 * to the required method of this class such as decodePacket or checksum.
267 	 */
268 	@Override
269 	public final void run() {
270 		FileInputStream fis = null;
271 		BufferedInputStream bis = null;
272 		try {
273 			// file setup
274 			final byte[] readByte = new byte[1];
275 			short uByte = 0;
276 
277 			// These can be caused by noise, but if there is no noise, then it's a code issue with the firmware!
278 			int escapePairMismatches = 0; // Incremented when an escape is found but not followed by an escapee
279 			int startsInsideAPacket  = 0; // Incremented when a start byte is found inside a packet
280 			int packetsOverLength    = 0; // Incremented when the buffer fills up before the end
281 			int packetsUnderLength   = 0; // Incremented when a packet is found that is too short
282 			int checksumMismatches   = 0; // Incremented when calculated checksum did not match the received one
283 			int packetsParsedFully   = 0; // Number of packets that matched all requirements and got into the log
284 			int packetLengthWrong    = 0; // The length should match the ID passed in, if not, fail.
285 			int strayBytesLost       = 0; // How many bytes were not in a packet! (should be low, ie, under one packet)
286 			int payloadIDWrong       = 0; // Requests to parse packet as A when packet was of type B
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; // Reset array position, always (discard existing data recorded, if any)
301 				} else if (startFound) { // Skip stray bytes until a start is found
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(); // Do this before attempting to load data
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++; // TODO maybe handle various possibilities post valid packet being parsed
316 								}
317 							} else {
318 								checksumMismatches++;
319 							}
320 						}
321 						startFound = false;
322 					} else if (uByte == ESCAPE_BYTE) {
323 						if (bis.read(readByte) != -1) { // Read in the byte to be un-escaped
324 							uByte = unEscape(unsignedValueOf(readByte[0])); // un-escape this byte
325 							if (uByte != (short) -1) {
326 								packetBuffer[packetLength] = uByte; // Store the un-escaped data for processing later
327 								packetLength++;
328 							} else {
329 								startFound = false; // The rest of the data should be ignored
330 								escapePairMismatches++;
331 							}
332 						}
333 					} else {
334 						if (packetLength < MAXIMUM_PACKET_LENGTH) {
335 							packetBuffer[packetLength] = uByte; // Store the data as-is for processing later
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 { // Setup the log to be displayed TODO in future it will just display as it goes
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 	 * This method decodes a packet by splitting up the data into larger data types to keep the unsigned info <br>
392 	 * This method could probably use a little work
393 	 * @param packet is a <code>short</code> array containing 1 full packet
394 	 * @param payloadIDToParse The ID of the payload type to expect and accept.
395 	 */
396 	private boolean decodeBasicLogPacket(final short[] packet) {
397 		final int HEADER_HAS_LENGTH_INDEX   = 0;
398 //		final int HEADER_IS_NACK_INDEX      = 1;
399 		final int HEADER_HAS_SEQUENCE_INDEX = 2;
400 //		final int HEADER_RESERVED_E_INDEX   = 3;
401 //		final int HEADER_RESERVED_D_INDEX   = 4;
402 //		final int HEADER_RESERVED_C_INDEX   = 5;
403 //		final int HEADER_RESERVED_B_INDEX   = 6;
404 //		final int HEADER_RESERVED_A_INDEX   = 7;
405 // TODO use a class to hold a packet in and provide getters for each of the above
406 
407 		// Increment post use
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; // TODO make this a code, or throw exception, but it's not exceptional at all for this to occur...
423 		}
424 		// record packet IDs that don't match desired in a Other Packets field, eventually put the packet
425 		// itself into meta data with an index number. how to invalidate these when looping? if looping...
426 
427 		final int[] flagValues = processFlagBytes(flags, 8);
428 
429 		if (flagValues[HEADER_HAS_SEQUENCE_INDEX] == 1) {
430 			position++; // Skip this!
431 		}
432 
433 		if (flagValues[HEADER_HAS_LENGTH_INDEX] == 1) {
434 			position += 2; // Ignore this for now, it's the payload length, check it vs actual length and check actual vs required.
435 		}
436 
437 		int lengthOfFields = 0;
438 		for (int i = 0; i < fields.length; i++) {
439 			lengthOfFields += fields[i].getType().getWidth();
440 		}
441 		// TODO warn if length in config is not equal, but allow both sides of wrong as it
442 		// is reasonable to not care about some and also to truncate shorter on the ECU side.
443 
444 		final int payloadLength = ((packet.length - position) - 1);
445 		if (payloadLength != lengthOfFields) { // First run through to find out what the lengths are.
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); // 1 = reset
484 						} // Else all is well.
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); // -1 to -1.254 = lost comms
490 						} // Else all is well.
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) { // TODO handle signed ints...
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 			// TODO handle floats
517 		}
518 		return true; // TODO FIXME : Default to all things being good till I attack this!
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 			// Unless they are 64, but shhhh...
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 	 * performs a check sum based on the packet data <br>
543 	 * the checksum needs to be improved however
544 	 * @param packet
545 	 * @return true or false based on if the checksum passes
546 	 */
547 	private boolean checksum(final short[] packet) {
548 		if (packetLength > 0) {
549 			final short includedSum = packet[packetLength - 1]; // sum is last byte
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 	 * Transforms an escape-encoded byte back into what it was before transmission
563 	 *
564 	 * @param uByte - byte to be Un-escaped
565 	 * @return -1 if bad data or the proper value of the escaped byte
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 	 * Transforms an unsigned char into a format suitable for Java, a short.
581 	 *
582 	 * @param uInt8 the raw signed byte representation of our raw unsigned char
583 	 * @return the value of the unsigned byte stored in a short
584 	 */
585 	private short unsignedValueOf(final byte uInt8) {
586 		return (short) (0xFF & uInt8);
587 	}
588 
589 	/**
590 	 *
591 	 * @return Misc data about this log
592 	 * <br> to be implemented in full later
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 }