📄 nmeaparser.java
字号:
package org.j4me.bluetoothgps;
import org.j4me.logging.*;
/**
* Parses chunks of data from a GPS device.
*/
class NMEAParser {
private static final String DOLLAR_SIGN_GPGSA = "$GPGSA";
private static final String DOLLAR_SIGN_GPGGA = "$GPGGA";
private static final String DOLLAR_SIGN_GPRMC = "$GPRMC";
/**
* Size of the string buffer. This should be a little more than the size of
* the byte array plus 80 (the max size of an NMEA sentence).
*/
public static final short OUTPUT_BUFFER_MAX_SIZE = 2048;
/**
* The maximum size of a sentence according to the NMEA standards is 82. We
* will use 128 to be safe.
*/
private static final short MAX_SENTENCE_SIZE = 128;
/**
* Sentence characters
*/
private static final byte SENTENCE_START = '$';
private static final byte CHECKSUM_START = '*';
private static final byte SENTENCE_END = '\n';
private static final byte DELIMITER = ',';
/**
* There was not enough to process
*/
public static final short TYPE_NOTHING_TO_PROCESS = -1;
/**
* No type
*/
public static final short TYPE_NONE = 0;
/**
* Type GPRMC.
*/
public static final short TYPE_GPRMC = 1;
/**
* Type GPGGA.
*/
public static final short TYPE_GPGGA = 2;
/**
* Type GPGSA.
*/
public static final short TYPE_GPGSA = 4;
/**
* TYPE_GPRMC | TYPE_GPGGA | TYPE_GPGSA
*/
public static final short ALL_TYPES_MASK = 7;
/**
* The current data read from the GPS device
*/
private byte[] data = new byte[OUTPUT_BUFFER_MAX_SIZE];
/**
* The length of <code>data</code>
*/
private int dataLength = 0;
/**
* The record being built
*/
private GPSRecord record = null;
/**
* Holds a record to be processed
*/
private GPSRecord recordBuffer = null;
/**
* Cosntructor. Initialize the output buffer and record
*
*/
public NMEAParser() {
record = new GPSRecord();
}
/**
* Append the output to the output buffer. The size of the output must
* always be less than {@link #OUTPUT_BUFFER_MAX_SIZE}.
*
* @param output - the output in bytes
* @param size - the size of the output
*/
private void append(byte[] output, int size)
{
// Allocate a new buffer if we got so much data that it will
// contain all our information and the buffer from before can
// be discarded to save the overhead of processing it.
if ( dataLength + size >= data.length )
{
flush();
}
// Append output to data left over from a past append().
int start = 0;
int length = size;
if ( size > data.length )
{
start = size - data.length;
length = size - start;
}
System.arraycopy( output, start, data, dataLength, length );
dataLength += size;
}
/**
* Flush the buffer.
*/
public void flush()
{
dataLength = 0;
}
/**
* Append the output and parse it. The size of the output must always be
* less than {@link #OUTPUT_BUFFER_MAX_SIZE}.
*
* @param output - the output to parse in bytes
* @param size - the size of the output
* @return a integer to indicate which sentences were parsed
*/
public int parse(byte[] output, int size) {
append(output, size);
return doParse();
}
/**
* Parse the data from the Bluetooth GPS device into NMEA sentences.
*
* @return a integer to indicate which sentences were parsed
*/
private int doParse() {
int parsedSentenceTypes = TYPE_NONE;
// If there is hardly anything in the buffer, there won't be a
// NMEA sentence, so don't bother processing it.
if (dataLength < 40) {
return TYPE_NONE;
}
// Set the current index to be the
int currentIndex = dataLength - 1;
// True if the current sentence is the last sentence in the buffer
boolean isLastSentence = true;
// Index of the start of the last sentence
int lastSentenceStart = -1;
// While there are characters left to process
while (currentIndex > 0)
{
// Find the start of the last NMEA sentence.
int sentenceStart = lastIndexOf( data, SENTENCE_START, currentIndex );
// Did we find the start of a sentence?
if (sentenceStart != -1)
{
// We found the start of a sentence, look for the end.
int sentenceEnd = indexOf( data, SENTENCE_END, sentenceStart, dataLength );
// Did we find the sentence end?
if (sentenceEnd != -1)
{
// Look for the first delimitter to get the sentence type.
// (i.e. String.indexOf(DELIMITER, sentenceStart))
int sentenceTypeEnd = indexOf( data, DELIMITER, sentenceStart, sentenceEnd );
// If we found the type end and the sentence end is within
// this sentence, then process the sentence. By checking that the
// sentence end is less than the current index then we
// handle the the case that we have a buffer left of
// "$GPRMC,45667,V,4354.788"
// and the first chunch of the new chars does not end the
// same sentence
// but instead starts a new one.
if ((sentenceTypeEnd != -1) && (sentenceEnd <= currentIndex)) {
try {
String type = new String(data, sentenceStart, sentenceTypeEnd - sentenceStart);
if ((type.equals(DOLLAR_SIGN_GPRMC)) &&
((parsedSentenceTypes & TYPE_GPRMC) == 0)) {
parsedSentenceTypes = parsedSentenceTypes |
processSentence(data, sentenceStart, sentenceEnd, TYPE_GPRMC);
} else if ((type.equals(DOLLAR_SIGN_GPGGA)) &&
((parsedSentenceTypes & TYPE_GPGGA) == 0)) {
parsedSentenceTypes = parsedSentenceTypes |
processSentence(data, sentenceStart, sentenceEnd, TYPE_GPGGA);
} else if ((type.equals(DOLLAR_SIGN_GPGSA)) &&
((parsedSentenceTypes & TYPE_GPGSA) == 0)) {
parsedSentenceTypes = parsedSentenceTypes |
processSentence(data, sentenceStart, sentenceEnd, TYPE_GPGSA);
}
} catch (Throwable t) {
Log.warn("processSentence: dataLength=" + dataLength +
", Start=" + sentenceStart +
", End=" + sentenceEnd, t);
// We are kind of screwed at this point so just return
// what we have and flush the buffer.
flush();
return parsedSentenceTypes;
}
// move the current position
currentIndex = sentenceStart - 1;
// Check if we have a complete record. If so we do
// not need to keep working with this buffer
if (parsedSentenceTypes == ALL_TYPES_MASK) {
break;
}
} else {
// This sentence is bunk, so just skip it
currentIndex = sentenceStart - 1;
}
} else {
// If this is the last sentence in the buffer, then keep the
// index of the start so that we do not delete it.
if (isLastSentence) {
lastSentenceStart = sentenceStart;
}
currentIndex = sentenceStart - 1;
}
} else {
break;
}
// Once we have completed an iteration, set the last sentence flag
// to false
isLastSentence = false;
} // while
// Throw away everything that has already been parsed.
if ( lastSentenceStart < 0 )
{
// Processed everything. No partial sentence left at the end.
flush();
}
else
{
// Keep the partial last sentence.
dataLength -= lastSentenceStart;
System.arraycopy( data, lastSentenceStart, data, 0, dataLength );
}
// If we parsed any of the sentences that we care about, put the record
// in the buffer.
if (parsedSentenceTypes != 0) {
// Put the record in the record buffer
setRecordBuffer( record );
// Create a new record from the existing record
record = new GPSRecord(record);
}
return parsedSentenceTypes;
}
/**
* Looks for the first occurance of a byte <code>b</code> in <code>array</code> between
* [<code>fromIndex</code>, <code>stopIndex</code>).
*
* @param array is the data to scan.
* @param b is the byte to match.
* @param fromIndex is the first index into array to check.
* @param stopIndex is one past the last index into array to check.
* @return The first index where <code>b</code> was found; -1 if it was not
* found.
*/
protected static int indexOf (byte[] array, byte b, int fromIndex, int stopIndex)
{
for ( int position = fromIndex; position < stopIndex; position++ )
{
if ( array[position] == b )
{
return position;
}
}
// If we made it here, b was not found.
return -1;
}
/**
* Looks for the last occurance of a byte <code>b</code> in <code>array</code> going
* backwards from <code>fromIndex</code>.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -