/**
* SirfDecoder
*
* takes an InputStream and interpret layer 3 and layer 4. Than make
* callbacks to the receiver witch ahas to implement SirfMsgReceiver
*
* @autor Harald Mueller james22 at users dot sourceforge dot net
* Copyright (C) 2007 Harald Mueller
* Copyright (C) 2008 Kai Krueger
*/
package net.sharenav.gps.location;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.sharenav.util.Logger;
public class NmeaInput extends BtReceiverInput {
protected static final Logger logger = Logger.getInstance(NmeaInput.class,Logger.TRACE);
private NmeaMessage smsg;
private byte [] buf1 = new byte[512]; //Buffer used to read data from GPS-receiver
private byte [] buf2 = new byte[128]; //Buffer used to recombine data into NMEA sentences
public boolean init(LocationMsgReceiver receiver) {
//#debug
logger.debug("Starting NMEA");
smsg=new NmeaMessage(this.receiverList);
return super.init(receiver);
}
public boolean activate(LocationMsgReceiver receiver) {
// FIXME move activation code (code to enable continuos location feed) here
//#debug
logger.debug("Activating NMEA");
return super.activate(receiver);
}
public boolean deactivate(LocationMsgReceiver receiver) {
//#debug
logger.debug("Deactivating NMEA");
return super.deactivate(receiver);
}
public void process() throws IOException{
//position markers in buf1 and buf2, length of useful data in buf1
int p1 = 0, p2 = 0, len1 = 0;
char c;
//Start marker in buf1 from where to copy
int start1 = 0;
//Indicate if the start and or the end of a NMEA sentence has been read
boolean found_start = false, found_end = false;
while ((btGpsInputStream.available() > 0 || p1 > 0) && ! closed) {
if (p1 == 0) {
len1 = btGpsInputStream.read(buf1);
bytesReceived += len1;
if (rawDataLogger != null) {
rawDataLogger.write(buf1, 0, len1);
rawDataLogger.flush();
}
}
//if we have already seen the start of a sentence, copy buf1 from the start
if (found_start) start1 = 0;
//Scan through buf1 to check for the beginning and end of a sentence
while (p1 < len1 && !found_end) {
c = (char)buf1[p1];
switch (c){
case '\n':
if (found_start) found_end = true;
break;
case '$':
start1 = p1;
found_start = true;
break;
}
p1++;
}
//If we haven't seen the start of the sentence, this data is useless so discard it
if (!found_start) {
p1 = 0; p2 = 0; found_end = false;
continue;
}
//Check to make sure we don't overrun the buffer
if (p2 + p1 - start1 > 128) {
logger.info("Error: NMEA string was longer than 128 char, but max should be 82");
p1 = 0; p2 = 0; found_start = false; found_end = false;
continue;
}
System.arraycopy(buf1, start1, buf2, p2, p1 - start1);
p2 += p1 - start1;
if (p1 == len1) p1 = 0; //consumed all of buf1, begin at the start again
if (!found_end) continue;
//We have a complete NMEA sentence in buf2, so we can now decode it.
//First check the checksum and ignore incorrect data
if (isChecksumCorrect(buf2, p2)) {
//Throw away the first 3 characters ($GP) and the last 5 (checksum and \r\n)
String nmea_sentence = new String(buf2,3,p2-8);
smsg.decodeMessage(nmea_sentence, true, true);
} else {
logger.info("NMEA sentence has incorrect checksum, discarding: " + new String(buf2));
}
//Reset buf2 for the next sentence
p2 = 0; found_start = false; found_end = false;
}
}
/**
* Calculate the NMEA checksum. This is a byte wise XOR of the NMEA string between (excluding)
* the $ and the *
* @param buf the entire NMEA sentence including $ and \r\n
* @param len
* @return if the checksum is correctly computed
*/
private boolean isChecksumCorrect(byte[] buf, int len) {
if (len < 7)
return false;
byte checksum = buf[1]; //ignore the first character, that is the $ sign
for (int i = 2; i < len - 5; i++) { // ignore the * checksum and \r\n
checksum = (byte)(checksum ^ buf[i]);
}
byte targetChecksum;
try {
targetChecksum = (Integer.valueOf(new String(buf,len-4,2), 16).byteValue());
} catch (NumberFormatException nfe) {
logger.info("Target checksum not recognised: " + new String(buf,len-4,2));
return false;
}
return (targetChecksum == checksum);
}
}