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  
32  import org.diyefi.openlogviewer.OpenLogViewer;
33  import org.diyefi.openlogviewer.decoder.LogField.types;
34  import org.diyefi.openlogviewer.genericlog.GenericLog;
35  
36  
37  /**
38   * This function takes a binary log file, plucks FreeEMS packets out of it,
39   * filters for standard packets and parses them into fields with appropriate scaling.
40   */
41  public class FreeEMSBin extends AbstractDecoder implements Runnable { // implements runnable to make this class theadable
42  	private static final int initialLength = 75000;
43  	private static final int loadFactor = 2;
44  
45  	private static final int MINIMUM_PACKET_LENGTH = 3; // Flag byte, payload id word, no payload - defined by protocol
46  	private static final int MAXIMUM_PACKET_LENGTH = 0x0820; // Buffer size on FreeEMS vanilla, take this from config file eventually
47  	// NOTE, standard supported log types require their own structure definition in the code,
48  	// override-able via files, matched looked for first, then local, then fall back to code
49  
50  	private static final short ESCAPE_BYTE = 0xBB;         // Used as an unsigned byte
51  	private static final short START_BYTE = 0xAA;          // Used as an unsigned byte
52  	private static final short STOP_BYTE = 0xCC;           // Used as an unsigned byte
53  	private static final short ESCAPED_ESCAPE_BYTE = 0x44; // Used as an unsigned byte
54  	private static final short ESCAPED_START_BYTE = 0x55;  // Used as an unsigned byte
55  	private static final short ESCAPED_STOP_BYTE = 0x33;   // Used as an unsigned byte
56  
57  	private boolean startFound;
58  	private File logFile;
59  	private short[] packetBuffer; // For use as an unsigned byte
60  	private GenericLog decodedLog;
61  	private Thread t;
62  	private int packetLength; // Track packet length
63  	private int firstPayloadIDFound = -1;
64  
65  	private long startTime;
66  
67  	private static String[] coreStatusAFlagNames = {
68  		"CS-FuelPumpPrime",
69  		"CS-unused1",
70  		"CS-unused2",
71  		"CS-unused3",
72  		"CS-unused4",
73  		"CS-unused5",
74  		"CS-unused6",
75  		"CS-unused7"
76  	};
77  
78  	private static String[] decoderFlagsFlagNames = {
79  		"DF-CombustionSync",
80  		"DF-CrankSync",
81  		"DF-CamSync",
82  		"DF-LAST_TIMESTAMP_VALID",
83  		"DF-LAST_PERIOD_VALID",
84  		"DF-LAST_MATCH_VALID",
85  		"DF-Spare-6",
86  		"DF-Spare-7"
87  	};
88  
89  	private static String[] flaggableFlagsNames = {
90  		"FF-callsToUISRs",               // to ensure we aren't accidentally triggering unused ISRs.
91  		"FF-lowVoltageConditions",       // low voltage conditions.
92  		"FF-decoderSyncLosses",          // Number of times cam, crank or combustion sync is lost.
93  		"FF-decoderSyncCorrections",     // Definite decoder syncs found while already synced in a different position.
94  		"FF-decoderSyncStateClears",     // Sync loss called when not synced yet, thus discarding data and preventing sync.
95  		"FF-serialNoiseErrors",          // Incremented when noise is detected
96  		"FF-serialFramingErrors",        // Incremented when a framing error occurs
97  		"FF-serialParityErrors",         // Incremented when a parity error occurs
98  		"FF-serialOverrunErrors",        // Incremented when overrun occurs (duplicated in KeyUserDebug below)
99  		"FF-serialEscapePairMismatches", // Incremented when an escape is found but not followed by an escapee
100 		"FF-serialStartsInsideAPacket",  // Incremented when a start byte is found inside a packet
101 		"FF-serialPacketsOverLength",    // Incremented when the buffer fills up before the end
102 		"FF-serialChecksumMismatches",   // Incremented when calculated checksum did not match the received one
103 		"FF-serialPacketsUnderLength",   // Incremented when a packet is found that is too short
104 		"FF-commsDebugMessagesNotSent",  // Incremented when a debug message can't be sent due to the TX buffer
105 		"FF-commsErrorMessagesNotSent"   // Incremented when an error message can't be sent due to the TX buffer
106 	};
107 
108 	// 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.
109 	private static LogField[] fields = {
110 		// CoreVars struct contents:
111 		new LogField("IAT",  100),   // Inlet Air Temperature           : 0.0 -   655.35       (0.01 Kelvin (/100))
112 		new LogField("CHT",  100),   // Coolant / Head Temperature      : 0.0 -   655.35       (0.01 Kelvin (/100))
113 		new LogField("TPS",  640),   // Throttle Position Sensor        : 0.0 -   102.398438   (0.0015625 % (/640))
114 		new LogField("EGO",  32768), // Exhaust Gas Oxygen              : 0.0 -     1.99996948 (0.0000305175781 lambda (/32768))
115 		new LogField("MAP",  100),   // Manifold Absolute Pressure      : 0.0 -   655.35       (0.01 kPa (/100))
116 		new LogField("AAP",  100),   // Atmospheric Absolute Pressure   : 0.0 -   655.35       (0.01 kPa (/100))
117 		new LogField("BRV",  1000),  // Battery Reference Voltage       : 0.0 -    65.535      (0.001 Volts (/1000))
118 		new LogField("MAT",  100),   // Manifold Air Temperature        : 0.0 -   655.35       (0.01 Kelvin (/100))
119 		new LogField("EGO2", 32768), // Exhaust Gas Oxygen              : 0.0 -     1.99996948 (0.0000305175781 lambda (/32768))
120 		new LogField("IAP",  100),   // Intercooler Absolute Pressure   : 0.0 -   655.35       (0.01 kPa (/100))
121 		new LogField("MAF"),         // Mass Air Flow                   : 0.0 - 65535.0        (raw units from lookup)
122 		new LogField("DMAP"),        // Delta MAP kPa/second or similar : 0.0 - 65535.0        (raw units from lookup)
123 		new LogField("DTPS"),        // Delta TPS %/second or similar   : 0.0 - 65535.0        (raw units from lookup)
124 		new LogField("RPM", 2),      // Revolutions Per Minute (Calced) : 0.0 - 32767.5        (0.5 RPM (/2))
125 		new LogField("DRPM"),        // Delta RPM (Calced)              : 0.0 - 65535.0        (raw units from lookup)
126 		new LogField("DDRPM"),       // Delta Delta RPM (Calced)        : 0.0 - 65535.0        (raw units from lookup)
127 
128 		// DerivedVars struct contents:
129 		new LogField("LoadMain", 512),          // Configurable unit of load, scale same as map by default
130 		new LogField("VEMain", 512),            // Volumetric Efficiency in 0 - 128%
131 		new LogField("Lambda", 32768),          // Integral Lambda 0 - 2.0
132 		new LogField("AirFlow"),                // raw intermediate, remove
133 		new LogField("densityAndFuel"),         // raw intermediate, remove
134 		new LogField("BasePW", 1250),           // Raw PW before corrections 0 - ~52ms
135 		new LogField("ETE", (100.0 / 32768.0)), // Engine Temperature Enrichment percentage correction 0 - 200%
136 		new LogField("TFCTotal", 1250),         // Transient fuel correction PW (+/-)  0 - ~52ms
137 		new LogField("EffectivePW", 1250),      // Actual PW of fuel delivery 0 - ~52ms
138 		new LogField("IDT", 1250),              // PW duration before fuel flow begins 0 - ~52ms
139 		new LogField("RefPW", 1250),            // Reference electrical PW 0 - ~52ms
140 		new LogField("Advance", 50),            // Ignition advance (scaled degrees / oneDegree(currently 50) = degrees) 0 - ~64*
141 		new LogField("Dwell", 1250),            // Dwell period, s 0 - ~52ms
142 
143 		// KeyUserDebugs struct contents
144 		new LogField("tempClock", types.UINT8),     // Incremented once per log sent, to be moved to a char TODO
145 		new LogField("spareChar", types.UINT8),     // Incremented once per log sent, to be moved to a char TODO
146 		new LogField("coreStatusA",  types.BITS8, coreStatusAFlagNames),    // Duplicated, migrate here, remove global var
147 		new LogField("decoderFlags", types.BITS8, decoderFlagsFlagNames),   // Various decoder state flags
148 		new LogField("flaggableFlags", types.BITS16, flaggableFlagsNames),  // Flags to go with our flaggables struct.
149 		new LogField("currentEvent",             types.UINT8), // Which input event was last to come in
150 		new LogField("syncLostWithThisID",       types.UINT8), // A unique identifier for the reason behind a loss of sync
151 		new LogField("syncLostOnThisEvent",      types.UINT8), // Where in the input pattern it all went very badly wrong
152 		new LogField("syncCaughtOnThisEvent",    types.UINT8), // Where in the input pattern that things started making sense
153 		new LogField("syncResetCalls",           types.UINT8), // Sum of losses, corrections and state clears
154 		new LogField("primaryTeethSeen",         types.UINT8), // Free running counters for number of input events, useful at lower RPM
155 		new LogField("secondaryTeethSeen",       types.UINT8), // Free running counters for number of input events, useful at lower RPM
156 		new LogField("serialOverrunErrors",      types.UINT8), // Incremented when an overrun occurs due to high ISR load, just a fact of life at high RPM
157 		new LogField("serialHardwareErrors",     types.UINT8), // Sum of noise, parity, and framing errors
158 		new LogField("serialAndCommsCodeErrors", types.UINT8), // Sum of checksum, escape mismatches, starts inside, and over/under length
159 		new LogField("inputEventTimeTolerance"),   // Required to tune noise rejection over RPM TODO add to LT1 and MissingTeeth
160 		new LogField("zsp10"), // Spare US variable
161 		new LogField("zsp9"),  // Spare US variable
162 		new LogField("zsp8"),  // Spare US variable
163 		new LogField("zsp7"),  // Spare US variable
164 		new LogField("zsp6"),  // Spare US variable
165 		new LogField("zsp5"),  // Spare US variable
166 		new LogField("zsp4"),  // Spare US variable
167 		new LogField("zsp3"),  // Spare US variable
168 		new LogField("clockInMilliSeconds"),       // Migrate to start of all large datalogs once analysed
169 		new LogField("clock8thMSsInMillis", 8) // Migrate to start of all large datalogs once analysed
170 	};
171 
172 	// NO default constructor, a file or path to a file MUST be given
173 	// Reason: File()'s constructors are ambiguous cannot give a null value
174 	/**
175 	 * FreeEmsBin Constructor: <code>String</code> path to your binary log
176 	 * @param path The file system path of the log file.
177 	 *
178 	 */
179 	public FreeEMSBin(final String path) {
180 		this(new File(path));
181 	}
182 
183 	/**
184 	 * FreeEmsBin Constructor: <code>File</code> object of your Binary log
185 	 * @param f The file reference to the log file.
186 	 */
187 	public FreeEMSBin(final File f) {
188 		startTime = System.currentTimeMillis(); // Let's profile this bitch! OS style :-)
189 		logFile = f;
190 		startFound = false;
191 		packetBuffer = new short[6000];
192 		packetLength = 0;
193 
194 		// TODO put matching name grabber here
195 
196 		// Eventually pull this in from files and config etc instead of default, if it makes sense to.
197 		final String[] headers = new String[fields.length * 32]; // Hack to make it plenty big, trim afterwards...
198 		int headersPosition = 0;
199 		for (int i = 0; i < fields.length; i++) {
200 			if ((fields[i].getType() == types.BITS8) || (fields[i].getType() == types.BITS16) || (fields[i].getType() == types.BITS32)) {
201 				for (int j = 0; j < fields[i].getBitFieldNames().length; j++) {
202 					final String flagID = getFlagName(fields[i], j);
203 					headers[headersPosition] = flagID;
204 					headersPosition++;
205 				}
206 			} else {
207 				headers[headersPosition] = fields[i].getID();
208 				headersPosition++;
209 			}
210 		}
211 
212 		decodedLog = new GenericLog(Arrays.copyOfRange(headers, 0, headersPosition), initialLength, loadFactor);
213 
214 		t = new Thread(this, "FreeEMSBin Loading");
215 		t.setPriority(Thread.MAX_PRIORITY);
216 		t.start();
217 	}
218 
219 
220 	/**
221 	 * DecodeLog will use the current <code>logFile</code> parse through it and when required pass the job <br>
222 	 * to the required method of this class such as decodePacket or checksum.
223 	 */
224 	@Override
225 	public final void run() {
226 		FileInputStream fis = null;
227 		BufferedInputStream bis = null;
228 		try {
229 			// file setup
230 			final byte[] readByte = new byte[1];
231 			short uByte = 0;
232 
233 			// These can be caused by noise, but if there is no noise, then it's a code issue with the firmware!
234 			int escapePairMismatches = 0; // Incremented when an escape is found but not followed by an escapee
235 			int startsInsideAPacket  = 0; // Incremented when a start byte is found inside a packet
236 			int packetsOverLength    = 0; // Incremented when the buffer fills up before the end
237 			int packetsUnderLength   = 0; // Incremented when a packet is found that is too short
238 			int checksumMismatches   = 0; // Incremented when calculated checksum did not match the received one
239 			int packetsParsedFully   = 0; // Number of packets that matched all requirements and got into the log
240 			int packetLengthWrong    = 0; // The length should match the ID passed in, if not, fail.
241 			int strayBytesLost       = 0; // How many bytes were not in a packet! (should be low, ie, under one packet)
242 			int payloadIDWrong       = 0; // Requests to parse packet as A when packet was of type B
243 
244 			startFound = false;
245 			fis = new FileInputStream(logFile);
246 			bis = new BufferedInputStream(fis);
247 			decodedLog.setLogStatus(GenericLog.LogState.LOG_LOADING);
248 			while (bis.read(readByte) != -1) {
249 				uByte = unsignedValueOf(readByte[0]);
250 				if (uByte == START_BYTE) {
251 					if (!startFound) {
252 						startFound = true;
253 					} else {
254 						startsInsideAPacket++;
255 					}
256 					packetLength = 0; // Reset array position, always (discard existing data recorded, if any)
257 				} else if (startFound) { // Skip stray bytes until a start is found
258 					if (uByte == STOP_BYTE) {
259 						if (packetLength < MINIMUM_PACKET_LENGTH) {
260 							packetsUnderLength++;
261 						} else if (packetLength > MAXIMUM_PACKET_LENGTH) {
262 							packetsOverLength++;
263 						} else {
264 							decodedLog.incrementPosition(); // Do this before attempting to load data
265 							final short[] justThePacket = Arrays.copyOfRange(packetBuffer, 0, packetLength);
266 							if (checksum(justThePacket)) {
267 								if (decodeBasicLogPacket(justThePacket)) {
268 									packetsParsedFully++;
269 								} else {
270 									packetLengthWrong++;
271 									payloadIDWrong++; // TODO maybe handle various possibilities post valid packet being parsed
272 								}
273 							} else {
274 								checksumMismatches++;
275 							}
276 						}
277 						startFound = false;
278 					} else if (uByte == ESCAPE_BYTE) {
279 						if (bis.read(readByte) != -1) { // Read in the byte to be un-escaped
280 							uByte = unEscape(unsignedValueOf(readByte[0])); // un-escape this byte
281 							if (uByte != (short) -1) {
282 								packetBuffer[packetLength] = uByte; // Store the un-escaped data for processing later
283 								packetLength++;
284 							} else {
285 								startFound = false; // The rest of the data should be ignored
286 								escapePairMismatches++;
287 							}
288 						}
289 					} else {
290 						packetBuffer[packetLength] = uByte; // Store the data as-is for processing later
291 						packetLength++;
292 					}
293 				} else {
294 					strayBytesLost++;
295 				}
296 			}
297 			decodedLog.setLogStatus(GenericLog.LogState.LOG_LOADED);
298 
299 			System.out.println(OpenLogViewer.NEWLINE + "Binary Parsing Statistics:" + OpenLogViewer.NEWLINE);
300 
301 			System.out.println("EscapePairMismatches: " + escapePairMismatches + " Incremented when an escape is found but not followed by an escapee");
302 			System.out.println("StartsInsideAPacket:  " + startsInsideAPacket  + " Incremented when a start byte is found inside a packet");
303 			System.out.println("PacketsOverLength:    " + packetsOverLength    + " Incremented when the buffer fills up before the end");
304 			System.out.println("PacketsUnderLength:   " + packetsUnderLength   + " Incremented when a packet is found that is too short");
305 			System.out.println("ChecksumMismatches:   " + checksumMismatches   + " Incremented when calculated checksum did not match the received one");
306 			System.out.println("PacketsParsedFully:   " + packetsParsedFully   + " Number of packets that matched all requirements and got into the log");
307 			System.out.println("PacketLengthWrong:    " + packetLengthWrong    + " The length should match the ID passed in, if not, fail.");
308 			System.out.println("StrayBytesLost:       " + strayBytesLost       + " How many bytes were not in a packet! (should be low, ie, under one packet");
309 			System.out.println("PayloadIDWrong:       " + payloadIDWrong       + " Requests to parse packet as A when packet was of type B");
310 
311 			System.out.println(OpenLogViewer.NEWLINE + "Thank you for choosing FreeEMS!");
312 
313 		} catch (Exception e) {
314 			e.printStackTrace();
315 			decodedLog.setLogStatusMessage(e.getMessage());
316 		} finally { // Setup the log to be displayed TODO in future it will just display as it goes
317 			OpenLogViewer.getInstance().getEntireGraphingPanel().setGraphSize(decodedLog.getRecordCount());
318 			decodedLog.setLogStatus(GenericLog.LogState.LOG_LOADED);
319 			System.out.println("Loaded " + (decodedLog.getRecordCount() + 1) + " records in " + (System.currentTimeMillis() - startTime) + " millis!");
320 
321 			try {
322 				if (bis != null) {
323 					bis.close();
324 				}
325 			} catch (IOException ioe) {
326 				ioe.printStackTrace();
327 				System.out.println("Failed To Close BIS Stream!");
328 			}
329 
330 			try {
331 				if (fis != null) {
332 					fis.close();
333 				}
334 			} catch (IOException ioe) {
335 				ioe.printStackTrace();
336 				System.out.println("Failed To Close FIS Stream!");
337 			}
338 		}
339 	}
340 
341 	/**
342 	 * This method decodes a packet by splitting up the data into larger data types to keep the unsigned info <br>
343 	 * This method could probably use a little work
344 	 * @param packet is a <code>short</code> array containing 1 full packet
345 	 * @param payloadIDToParse The ID of the payload type to expect and accept.
346 	 */
347 	private boolean decodeBasicLogPacket(final short[] packet) {
348 		final int HEADER_HAS_LENGTH_INDEX   = 0;
349 //		final int HEADER_IS_NACK_INDEX      = 1;
350 		final int HEADER_HAS_SEQUENCE_INDEX = 2;
351 //		final int HEADER_RESERVED_E_INDEX   = 3;
352 //		final int HEADER_RESERVED_D_INDEX   = 4;
353 //		final int HEADER_RESERVED_C_INDEX   = 5;
354 //		final int HEADER_RESERVED_B_INDEX   = 6;
355 //		final int HEADER_RESERVED_A_INDEX   = 7;
356 // TODO use a class to hold a packet in and provide getters for each of the above
357 
358 		// Increment post use
359 		int position = 0;
360 
361 		final short flags = packet[position];
362 		position++;
363 
364 		final short payloadIdUpper = packet[position];
365 		position++;
366 		final short payloadIdLower = packet[position];
367 		position++;
368 		final int payloadId = (payloadIdUpper * 256) + payloadIdLower;
369 
370 		if (firstPayloadIDFound < 0) {
371 			firstPayloadIDFound = payloadId;
372 		} else if (payloadId != firstPayloadIDFound) {
373 			return false; // TODO make this a code, or throw exception, but it's not exceptional at all for this to occur...
374 		}
375 		// record packet IDs that don't match desired in a Other Packets field, eventually put the packet
376 		// itself into meta data with an index number. how to invalidate these when looping? if looping...
377 
378 		final int[] flagValues = processFlagBytes(flags, 8);
379 
380 		if (flagValues[HEADER_HAS_SEQUENCE_INDEX] == 1) {
381 			position++; // Skip this!
382 		}
383 
384 		if (flagValues[HEADER_HAS_LENGTH_INDEX] == 1) {
385 			position += 2; // Ignore this for now, it's the payload length, check it vs actual length and check actual vs required.
386 		}
387 
388 		int lengthOfFields = 0;
389 		for (int i = 0; i < fields.length; i++) {
390 			lengthOfFields += fields[i].getType().getWidth();
391 		}
392 		// TODO warn if length in config is not equal, but allow both sides of wrong as it
393 		// is reasonable to not care about some and also to truncate shorter on the ECU side.
394 
395 		final int payloadLength = ((packet.length - position) - 1);
396 		if (payloadLength != lengthOfFields) { // First run through to find out what the lengths are.
397 			System.out.print(" Fields length is: " + lengthOfFields);
398 			System.out.print(" Packet length is: " + packet.length);
399 			System.out.println(" Payload length is: " + payloadLength);
400 			return false;
401 		}
402 
403 		for (int i = 0; i < fields.length; i++) {
404 			final LogField field = fields[i];
405 
406 			int rawValue = 0;
407 			for (int j = 0; j < field.getType().getWidth(); j++) {
408 				rawValue = (rawValue * 256) + packet[position];
409 				position++;
410 			}
411 
412 			if ((field.getType() == types.UINT8) || (field.getType() == types.UINT16) || (field.getType() == types.UINT32)) {
413 				final double scaledValue = (double) rawValue / field.getDivBy();
414 				final double finalValue = scaledValue + field.getAddTo();
415 				decodedLog.addValue(field.getID(), finalValue);
416 			} else if ((field.getType() == types.BITS8) || (field.getType() == types.BITS16) || (field.getType() == types.BITS32)) {
417 				final int[] processedFlags = processFlagBytes(rawValue, (8 * field.getType().getWidth()));
418 				for (int j = 0; j < processedFlags.length; j++) {
419 					final String flagID = getFlagName(field, j);
420 					decodedLog.addValue(flagID, processedFlags[j]);
421 				}
422 			} else if (field.getType() == types.SINT8) { // TODO handle signed ints...
423 				decodedLog.addValue(field.getID(), rawValue);
424 			} else if (field.getType() == types.SINT16) {
425 				decodedLog.addValue(field.getID(), rawValue);
426 			} else if (field.getType() == types.SINT32) {
427 				decodedLog.addValue(field.getID(), rawValue);
428 			}
429 			// TODO handle floats
430 		}
431 		return true; // TODO FIXME : Default to all things being good till I attack this!
432 	}
433 
434 	private int[] processFlagBytes(final long valueOfFlags, final int numberOfFlags) {
435 		if ((numberOfFlags != 8) && (numberOfFlags != 16) && (numberOfFlags != 32)) {
436 			throw new IllegalArgumentException("Basic units of computer sciene apply, embedded flags are never " + numberOfFlags + " wide!");
437 			// Unless they are 64, but shhhh...
438 		}
439 
440 		final int[] flagValues = new int[numberOfFlags];
441 		int comparison = 1;
442 		long remainingValueOfFlags = valueOfFlags;
443 		for (int i = 0; i < numberOfFlags; i++) {
444 			if ((remainingValueOfFlags % (2 * comparison)) == comparison) {
445 				flagValues[i] = 1;
446 				remainingValueOfFlags -= comparison;
447 			}
448 			comparison *= 2;
449 		}
450 
451 		return flagValues;
452 	}
453 
454 	/**
455 	 * performs a check sum based on the packet data <br>
456 	 * the checksum needs to be improved however
457 	 * @param packet
458 	 * @return true or false based on if the checksum passes
459 	 */
460 	private boolean checksum(final short[] packet) {
461 		if (packetLength > 0) {
462 			final short includedSum = packet[packetLength - 1]; // sum is last byte
463 			long veryBIGsum = 0;
464 			for (int x = 0; x < packetLength - 1; x++) {
465 				veryBIGsum += packet[x];
466 			}
467 			final short calculatedSum = (short) (veryBIGsum % 256);
468 			return (calculatedSum == includedSum);
469 		} else {
470 			return false;
471 		}
472 	}
473 
474 	/**
475 	 * Transforms an escape-encoded byte back into what it was before transmission
476 	 *
477 	 * @param uByte - byte to be Un-escaped
478 	 * @return -1 if bad data or the proper value of the escaped byte
479 	 */
480 	private short unEscape(final short uByte) {
481 		if (uByte == ESCAPED_START_BYTE) {
482 			return START_BYTE;
483 		} else if (uByte == ESCAPED_STOP_BYTE) {
484 			return STOP_BYTE;
485 		} else if (uByte == ESCAPED_ESCAPE_BYTE) {
486 			return ESCAPE_BYTE;
487 		} else {
488 			return (short) -1;
489 		}
490 	}
491 
492 	/**
493 	 * Transforms an unsigned char into a format suitable for Java, a short.
494 	 *
495 	 * @param uInt8 the raw signed byte representation of our raw unsigned char
496 	 * @return the value of the unsigned byte stored in a short
497 	 */
498 	private short unsignedValueOf(final byte uInt8) {
499 		return (short) (0xFF & uInt8);
500 	}
501 
502 	/**
503 	 *
504 	 * @return Misc data about this log
505 	 * <br> to be implemented in full later
506 	 */
507 	@Override
508 	public final String toString() {
509 		return super.toString();
510 	}
511 
512 	private String getFlagName(final LogField field, final int flagIndex) {
513 		return field.getBitFieldNames()[flagIndex] + "-" + field.getID() + "-B" + flagIndex;
514 	}
515 }