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"),     // Incremented once per log sent, to be moved to a char TODO
145 		new LogField("coreStatusA",  types.BITS8, coreStatusAFlagNames),    // Duplicated, migrate here, remove global var
146 		new LogField("decoderFlags", types.BITS8, decoderFlagsFlagNames),   // Various decoder state flags
147 		new LogField("flaggableFlags", types.BITS16, flaggableFlagsNames),  // Flags to go with our flaggables struct.
148 		new LogField("currentEvent",             types.UINT8), // Which input event was last to come in
149 		new LogField("syncLostWithThisID",       types.UINT8), // A unique identifier for the reason behind a loss of sync
150 		new LogField("syncLostOnThisEvent",      types.UINT8), // Where in the input pattern it all went very badly wrong
151 		new LogField("syncCaughtOnThisEvent",    types.UINT8), // Where in the input pattern that things started making sense
152 		new LogField("syncResetCalls",           types.UINT8), // Sum of losses, corrections and state clears
153 		new LogField("primaryTeethSeen",         types.UINT8), // Free running counters for number of input events, useful at lower RPM
154 		new LogField("secondaryTeethSeen",       types.UINT8), // Free running counters for number of input events, useful at lower RPM
155 		new LogField("serialOverrunErrors",      types.UINT8), // Incremented when an overrun occurs due to high ISR load, just a fact of life at high RPM
156 		new LogField("serialHardwareErrors",     types.UINT8), // Sum of noise, parity, and framing errors
157 		new LogField("serialAndCommsCodeErrors", types.UINT8), // Sum of checksum, escape mismatches, starts inside, and over/under length
158 		new LogField("inputEventTimeTolerance"),   // Required to tune noise rejection over RPM TODO add to LT1 and MissingTeeth
159 		new LogField("zsp10"), // Spare US variable
160 		new LogField("zsp9"),  // Spare US variable
161 		new LogField("zsp8"),  // Spare US variable
162 		new LogField("zsp7"),  // Spare US variable
163 		new LogField("zsp6"),  // Spare US variable
164 		new LogField("zsp5"),  // Spare US variable
165 		new LogField("zsp4"),  // Spare US variable
166 		new LogField("zsp3"),  // Spare US variable
167 		new LogField("clockInMilliSeconds"),       // Migrate to start of all large datalogs once analysed
168 		new LogField("clock8thMSsInMillis", 8) // Migrate to start of all large datalogs once analysed
169 	};
170 
171 	// NO default constructor, a file or path to a file MUST be given
172 	// Reason: File()'s constructors are ambiguous cannot give a null value
173 	/**
174 	 * FreeEmsBin Constructor: <code>String</code> path to your binary log
175 	 * @param path The file system path of the log file.
176 	 *
177 	 */
178 	public FreeEMSBin(final String path) {
179 		this(new File(path));
180 	}
181 
182 	/**
183 	 * FreeEmsBin Constructor: <code>File</code> object of your Binary log
184 	 * @param f The file reference to the log file.
185 	 */
186 	public FreeEMSBin(final File f) {
187 		startTime = System.currentTimeMillis(); // Let's profile this bitch! OS style :-)
188 		logFile = f;
189 		startFound = false;
190 		packetBuffer = new short[6000];
191 		packetLength = 0;
192 
193 		// TODO put matching name grabber here
194 
195 		// Eventually pull this in from files and config etc instead of default, if it makes sense to.
196 		final String[] headers = new String[fields.length * 32]; // Hack to make it plenty big, trim afterwards...
197 		int headersPosition = 0;
198 		for (int i = 0; i < fields.length; i++) {
199 			if ((fields[i].getType() == types.BITS8) || (fields[i].getType() == types.BITS16) || (fields[i].getType() == types.BITS32)) {
200 				for (int j = 0; j < fields[i].getBitFieldNames().length; j++) {
201 					final String flagID = getFlagName(fields[i], j);
202 					headers[headersPosition] = flagID;
203 					headersPosition++;
204 				}
205 			} else {
206 				headers[headersPosition] = fields[i].getID();
207 				headersPosition++;
208 			}
209 		}
210 
211 		decodedLog = new GenericLog(Arrays.copyOfRange(headers, 0, headersPosition), initialLength, loadFactor);
212 
213 		t = new Thread(this, "FreeEMSBin Loading");
214 		t.setPriority(Thread.MAX_PRIORITY);
215 		t.start();
216 	}
217 
218 
219 	/**
220 	 * DecodeLog will use the current <code>logFile</code> parse through it and when required pass the job <br>
221 	 * to the required method of this class such as decodePacket or checksum.
222 	 */
223 	@Override
224 	public final void run() {
225 		FileInputStream fis = null;
226 		BufferedInputStream bis = null;
227 		try {
228 			// file setup
229 			final byte[] readByte = new byte[1];
230 			short uByte = 0;
231 
232 			// These can be caused by noise, but if there is no noise, then it's a code issue with the firmware!
233 			int escapePairMismatches = 0; // Incremented when an escape is found but not followed by an escapee
234 			int startsInsideAPacket  = 0; // Incremented when a start byte is found inside a packet
235 			int packetsOverLength    = 0; // Incremented when the buffer fills up before the end
236 			int packetsUnderLength   = 0; // Incremented when a packet is found that is too short
237 			int checksumMismatches   = 0; // Incremented when calculated checksum did not match the received one
238 			int packetsParsedFully   = 0; // Number of packets that matched all requirements and got into the log
239 			int packetLengthWrong    = 0; // The length should match the ID passed in, if not, fail.
240 			int strayBytesLost       = 0; // How many bytes were not in a packet! (should be low, ie, under one packet)
241 			int payloadIDWrong       = 0; // Requests to parse packet as A when packet was of type B
242 
243 			startFound = false;
244 			fis = new FileInputStream(logFile);
245 			bis = new BufferedInputStream(fis);
246 			decodedLog.setLogStatus(GenericLog.LOG_LOADING);
247 			while (bis.read(readByte) != -1) {
248 				uByte = unsignedValueOf(readByte[0]);
249 				if (uByte == START_BYTE) {
250 					if (!startFound) {
251 						startFound = true;
252 					} else {
253 						startsInsideAPacket++;
254 					}
255 					packetLength = 0; // Reset array position, always (discard existing data recorded, if any)
256 				} else if (startFound) { // Skip stray bytes until a start is found
257 					if (uByte == STOP_BYTE) {
258 						if (packetLength < MINIMUM_PACKET_LENGTH) {
259 							packetsUnderLength++;
260 						} else if (packetLength > MAXIMUM_PACKET_LENGTH) {
261 							packetsOverLength++;
262 						} else {
263 							decodedLog.incrementPosition(); // Do this before attempting to load data
264 							final short[] justThePacket = Arrays.copyOfRange(packetBuffer, 0, packetLength);
265 							if (checksum(justThePacket)) {
266 								if (decodeBasicLogPacket(justThePacket)) {
267 									packetsParsedFully++;
268 								} else {
269 									packetLengthWrong++;
270 									payloadIDWrong++; // TODO maybe handle various possibilities post valid packet being parsed
271 								}
272 							} else {
273 								checksumMismatches++;
274 							}
275 						}
276 						startFound = false;
277 					} else if (uByte == ESCAPE_BYTE) {
278 						if (bis.read(readByte) != -1) { // Read in the byte to be un-escaped
279 							uByte = unEscape(unsignedValueOf(readByte[0])); // un-escape this byte
280 							if (uByte != (short) -1) {
281 								packetBuffer[packetLength] = uByte; // Store the un-escaped data for processing later
282 								packetLength++;
283 							} else {
284 								startFound = false; // The rest of the data should be ignored
285 								escapePairMismatches++;
286 							}
287 						}
288 					} else {
289 						packetBuffer[packetLength] = uByte; // Store the data as-is for processing later
290 						packetLength++;
291 					}
292 				} else {
293 					strayBytesLost++;
294 				}
295 			}
296 			decodedLog.setLogStatus(GenericLog.LOG_LOADED);
297 
298 			System.out.println(OpenLogViewer.NEWLINE + "Binary Parsing Statistics:" + OpenLogViewer.NEWLINE);
299 
300 			System.out.println("EscapePairMismatches: " + escapePairMismatches + " Incremented when an escape is found but not followed by an escapee");
301 			System.out.println("StartsInsideAPacket:  " + startsInsideAPacket  + " Incremented when a start byte is found inside a packet");
302 			System.out.println("PacketsOverLength:    " + packetsOverLength    + " Incremented when the buffer fills up before the end");
303 			System.out.println("PacketsUnderLength:   " + packetsUnderLength   + " Incremented when a packet is found that is too short");
304 			System.out.println("ChecksumMismatches:   " + checksumMismatches   + " Incremented when calculated checksum did not match the received one");
305 			System.out.println("PacketsParsedFully:   " + packetsParsedFully   + " Number of packets that matched all requirements and got into the log");
306 			System.out.println("PacketLengthWrong:    " + packetLengthWrong    + " The length should match the ID passed in, if not, fail.");
307 			System.out.println("StrayBytesLost:       " + strayBytesLost       + " How many bytes were not in a packet! (should be low, ie, under one packet");
308 			System.out.println("PayloadIDWrong:       " + payloadIDWrong       + " Requests to parse packet as A when packet was of type B");
309 
310 			System.out.println(OpenLogViewer.NEWLINE + "Thank you for choosing FreeEMS!");
311 
312 		} catch (Exception e) {
313 			e.printStackTrace();
314 			decodedLog.setLogStatusMessage(e.getMessage());
315 		} finally { // Setup the log to be displayed TODO in future it will just display as it goes
316 			OpenLogViewer.getInstance().getEntireGraphingPanel().setGraphSize(decodedLog.getRecordCount());
317 			decodedLog.setLogStatus(GenericLog.LOG_LOADED);
318 			System.out.println("Loaded " + (decodedLog.getRecordCount() + 1) + " records in " + (System.currentTimeMillis() - startTime) + " millis!");
319 
320 			try {
321 				if (bis != null) {
322 					bis.close();
323 				}
324 			} catch (IOException ioe) {
325 				ioe.printStackTrace();
326 				System.out.println("Failed To Close BIS Stream!");
327 			}
328 
329 			try {
330 				if (fis != null) {
331 					fis.close();
332 				}
333 			} catch (IOException ioe) {
334 				ioe.printStackTrace();
335 				System.out.println("Failed To Close FIS Stream!");
336 			}
337 		}
338 	}
339 
340 	/**
341 	 * This method decodes a packet by splitting up the data into larger data types to keep the unsigned info <br>
342 	 * This method could probably use a little work
343 	 * @param packet is a <code>short</code> array containing 1 full packet
344 	 * @param payloadIDToParse The ID of the payload type to expect and accept.
345 	 */
346 	private boolean decodeBasicLogPacket(final short[] packet) {
347 		final int HEADER_HAS_LENGTH_INDEX   = 0;
348 //		final int HEADER_IS_NACK_INDEX      = 1;
349 		final int HEADER_HAS_SEQUENCE_INDEX = 2;
350 //		final int HEADER_RESERVED_E_INDEX   = 3;
351 //		final int HEADER_RESERVED_D_INDEX   = 4;
352 //		final int HEADER_RESERVED_C_INDEX   = 5;
353 //		final int HEADER_RESERVED_B_INDEX   = 6;
354 //		final int HEADER_RESERVED_A_INDEX   = 7;
355 // TODO use a class to hold a packet in and provide getters for each of the above
356 
357 		// Increment post use
358 		int position = 0;
359 
360 		final short flags = packet[position];
361 		position++;
362 
363 		final short payloadIdUpper = packet[position];
364 		position++;
365 		final short payloadIdLower = packet[position];
366 		position++;
367 		final int payloadId = (payloadIdUpper * 256) + payloadIdLower;
368 
369 		if (firstPayloadIDFound < 0) {
370 			firstPayloadIDFound = payloadId;
371 		} else if (payloadId != firstPayloadIDFound) {
372 			return false; // TODO make this a code, or throw exception, but it's not exceptional at all for this to occur...
373 		}
374 		// record packet IDs that don't match desired in a Other Packets field, eventually put the packet
375 		// itself into meta data with an index number. how to invalidate these when looping? if looping...
376 
377 		final int[] flagValues = processFlagBytes(flags, 8);
378 
379 		if (flagValues[HEADER_HAS_SEQUENCE_INDEX] == 1) {
380 			position++; // Skip this!
381 		}
382 
383 		if (flagValues[HEADER_HAS_LENGTH_INDEX] == 1) {
384 			position += 2; // Ignore this for now, it's the payload length, check it vs actual length and check actual vs required.
385 		}
386 
387 		int lengthOfFields = 0;
388 		for (int i = 0; i < fields.length; i++) {
389 			lengthOfFields += fields[i].getType().getWidth();
390 		}
391 		// TODO warn if length in config is not equal, but allow both sides of wrong as it
392 		// is reasonable to not care about some and also to truncate shorter on the ECU side.
393 
394 		final int payloadLength = ((packet.length - position) - 1);
395 		if (payloadLength != lengthOfFields) { // First run through to find out what the lengths are.
396 			System.out.print(" Fields length is: " + lengthOfFields);
397 			System.out.print(" Packet length is: " + packet.length);
398 			System.out.println(" Payload length is: " + payloadLength);
399 			return false;
400 		}
401 
402 		for (int i = 0; i < fields.length; i++) {
403 			final LogField field = fields[i];
404 
405 			int rawValue = 0;
406 			for (int j = 0; j < field.getType().getWidth(); j++) {
407 				rawValue = (rawValue * 256) + packet[position];
408 				position++;
409 			}
410 
411 			if ((field.getType() == types.UINT8) || (field.getType() == types.UINT16) || (field.getType() == types.UINT32)) {
412 				final double scaledValue = (double) rawValue / field.getDivBy();
413 				final double finalValue = scaledValue + field.getAddTo();
414 				decodedLog.addValue(field.getID(), finalValue);
415 			} else if ((field.getType() == types.BITS8) || (field.getType() == types.BITS16) || (field.getType() == types.BITS32)) {
416 				final int[] processedFlags = processFlagBytes(rawValue, (8 * field.getType().getWidth()));
417 				for (int j = 0; j < processedFlags.length; j++) {
418 					final String flagID = getFlagName(field, j);
419 					decodedLog.addValue(flagID, processedFlags[j]);
420 				}
421 			} else if (field.getType() == types.SINT8) { // TODO handle signed ints...
422 				decodedLog.addValue(field.getID(), rawValue);
423 			} else if (field.getType() == types.SINT16) {
424 				decodedLog.addValue(field.getID(), rawValue);
425 			} else if (field.getType() == types.SINT32) {
426 				decodedLog.addValue(field.getID(), rawValue);
427 			}
428 			// TODO handle floats
429 		}
430 		return true; // TODO FIXME : Default to all things being good till I attack this!
431 	}
432 
433 	private final int[] processFlagBytes(final long valueOfFlags, final int numberOfFlags) {
434 		if ((numberOfFlags != 8) && (numberOfFlags != 16) && (numberOfFlags != 32)) {
435 			throw new IllegalArgumentException("Basic units of computer sciene apply, embedded flags are never " + numberOfFlags + " wide!");
436 			// Unless they are 64, but shhhh...
437 		}
438 
439 		final int[] flagValues = new int[numberOfFlags];
440 		int comparison = 1;
441 		long remainingValueOfFlags = valueOfFlags;
442 		for (int i = 0; i < numberOfFlags; i++) {
443 			if ((remainingValueOfFlags % (2 * comparison)) == comparison) {
444 				flagValues[i] = 1;
445 				remainingValueOfFlags -= comparison;
446 			}
447 			comparison *= 2;
448 		}
449 
450 		return flagValues;
451 	}
452 
453 	/**
454 	 * performs a check sum based on the packet data <br>
455 	 * the checksum needs to be improved however
456 	 * @param packet
457 	 * @return true or false based on if the checksum passes
458 	 */
459 	private boolean checksum(final short[] packet) {
460 		if (packetLength > 0) {
461 			final short includedSum = packet[packetLength - 1]; // sum is last byte
462 			long veryBIGsum = 0;
463 			for (int x = 0; x < packetLength - 1; x++) {
464 				veryBIGsum += packet[x];
465 			}
466 			final short calculatedSum = (short) (veryBIGsum % 256);
467 			return (calculatedSum == includedSum);
468 		} else {
469 			return false;
470 		}
471 	}
472 
473 	/**
474 	 * Transforms an escape-encoded byte back into what it was before transmission
475 	 *
476 	 * @param uByte - byte to be Un-escaped
477 	 * @return -1 if bad data or the proper value of the escaped byte
478 	 */
479 	private short unEscape(final short uByte) {
480 		if (uByte == ESCAPED_START_BYTE) {
481 			return START_BYTE;
482 		} else if (uByte == ESCAPED_STOP_BYTE) {
483 			return STOP_BYTE;
484 		} else if (uByte == ESCAPED_ESCAPE_BYTE) {
485 			return ESCAPE_BYTE;
486 		} else {
487 			return (short) -1;
488 		}
489 	}
490 
491 	/**
492 	 * Transforms an unsigned char into a format suitable for Java, a short.
493 	 *
494 	 * @param uInt8 the raw signed byte representation of our raw unsigned char
495 	 * @return the value of the unsigned byte stored in a short
496 	 */
497 	private final short unsignedValueOf(final byte uInt8) {
498 		return (short) (0xFF & uInt8);
499 	}
500 
501 	/**
502 	 *
503 	 * @return Misc data about this log
504 	 * <br> to be implemented in full later
505 	 */
506 	@Override
507 	public final String toString() {
508 		return super.toString();
509 	}
510 
511 	private String getFlagName(final LogField field, final int flagIndex) {
512 		return field.getBitFieldNames()[flagIndex] + "-" + field.getID() + "-B" + flagIndex;
513 	}
514 }