View Javadoc

1   /*
2    * Trip Tracker, a real-time position tracking system for the Internet.
3    * Copyright (C) 2006  Team Trip Tracker
4    *
5    * This program is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License as published by the
7    * Free Software Foundation; either version 2 of the License, or (at your
8    * option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful, but
11   * WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13   * General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License along
16   * with this program; if not, write to the Free Software Foundation, Inc.,
17   * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18   */
19  
20  package triptracker.client.gps.core;
21  
22  /***
23   * Parser for the GPS NMEA-0183 (National Marine Electronics Association) data
24   * protocol. NMEA-0183 data is sent at 4800 baud and the max. length of a
25   * sentence is 80 ASCII characters including the leading '$' and the trailing
26   * CR LF control characters.
27   */
28  public class NMEASentence {
29  	
30  //	static {
31  //	}
32  	
33  	public static final char DELIMITER = ',';
34  	public static final char START = '$';
35  	public static final char CHECKSUM = '*';
36  	public static final int MAXLENGTH = 82; 
37  	public static final String CRLF = "\r\n";
38  	
39  	private String checksum;
40  	private String sentence;
41  	private String[] fields;
42  	private NMEATalkerID talkerID;
43  	private NMEASentenceID sentenceID;
44  	private NMEAManufacturerID manufacturerID;
45  	private boolean ignoreChecksum = false;
46  	private boolean proprietary = false;
47  	
48  
49  //	Test output from our Garmin GPS:
50  //	$GPRMC,105648,A,5856.3026,N,00541.6098,E,0.0,0.0,170106,1.3,W,A*0A
51  //	$GPRMB,A,,,,,,,,,,,,A,A*0B
52  //	$GPGGA,105648,5856.3026,N,00541.6098,E,1,05,3.8,28.2,M,44.0,M,,*74
53  //	$GPGSA,A,3,03,,,13,15,,,21,23,,,,6.5,3.8,4.7*3E
54  //	$GPGSV,3,1,11,03,13,262,39,06,46,090,00,10,24,046,00,13,13,325,37*7E
55  //	$GPGLL,5856.3026,N,00541.6098,E,105648,A,A*47
56  //	Latit, Long
57  //	$GPBOD,,T,,M,,*47
58  //	$GPVTG,0.0,T,1.3,M,0.0,N,0.0,K*4C
59  //	$PGRME,26.2,M,45.0,M,52.0,M*1E
60  //	$PGRMZ,93,f,3*21
61  //	$PGRMM,WGS 84*06
62  //	$GPRTE,1,1,c,*37
63  
64  	
65  	public NMEASentence() {	}
66  
67  	public NMEASentence(String sentence) {
68  		parseSentence(sentence);
69  	}
70  	
71  	/***
72  	 * Parses the NMEA-0183 sentence. Fields can then be read from
73  	 * {@link #getField(int)}.
74  	 * 
75  	 * @param sentence NMEA-0183 formatted sentence
76  	 * @throws IllegalArgumentException on malformed sentence
77  	 * @see #getField(int)
78  	 */
79  	public void parseSentence(String sentence) throws IllegalArgumentException {
80  		// Remove CRLF and everything after.
81  		this.sentence = sentence.split(CRLF)[0];
82  
83  		// Check for $ at start of sentence and remove it.
84  		if (sentence.charAt(0) == START) {
85  			this.sentence = this.sentence.substring(1);
86  		} else {
87  			//throw new IllegalArgumentException("Bad NMEA-0183 sentence format: "
88  			//		+ "first character is not '" + START + "'");
89  		}
90  
91  		fields = this.sentence.split(DELIMITER + "");
92  
93  		// Look for checksum.
94  		String csField = fields[fields.length - 1];
95  		if (this.sentence.matches(".*//*[0-9a-fA-F]{2}$")) {
96  			checksum = csField.substring(csField.length() - 2);
97  			fields[fields.length - 1]
98  			       = csField.substring(0, csField.length() - 3);
99  		} else {
100 			checksum = null;
101 		}
102 
103 		validateSentence();
104 		
105 		// Find talker ID.
106 		if (fields[0].substring(0, 1).equals(NMEATalkerID.P.name())) {
107 			proprietary = true;
108 			talkerID = NMEATalkerID.P;
109 		} else {
110 			proprietary = false;
111 			talkerID = NMEATalkerID.valueOf(fields[0].substring(0, 2));
112 		}
113 		
114 		// Find sentence ID.
115 		if (proprietary) {
116 			sentenceID = NMEASentenceID.valueOf(fields[0].substring(1,5));
117 		} else {
118 			sentenceID = NMEASentenceID.valueOf(fields[0].substring(2,5));
119 		}
120 		
121 	}
122 	
123 	private void validateSentence() throws IllegalArgumentException {
124 		// Check for max. length which includes $ and CR LF (\r\n).
125 		if (sentence.length() > (MAXLENGTH - CRLF.length() - 1))
126 			//throw new IllegalArgumentException("Bad NMEA-0183 sentence format: "
127 			//		+ "length exceeds " + MAXLENGTH + " characters");
128 
129 		// Check for 6 character first field.
130 		if (fields[0].length() != 5)
131 			//throw new IllegalArgumentException("Bad NMEA-0183 sentence format: "
132 			//		+ "sentence identification field is wrong length");
133 		
134 		// Check for more than one field.
135 		if (fields.length < 2)
136 			//throw new IllegalArgumentException("Bad NMEA-0183 sentence format: "
137 			//		+ "no data fields");
138 		
139 		// Check for non-printable ASCII characters.
140         // Under the NMEA-0183 standard, all characters used are printable
141         // ASCII text (plus carriage return and line feed).
142 		if (!sentence.matches("^[//x20-//x7E]+$"))
143 			//throw new IllegalArgumentException("Bad NMEA-0183 sentence format: "
144 			//		+ "non-printable ASCII character(s)");
145 		
146 		
147 		
148         // The optional checksum field consists of a "*" and two hex digits
149         // representing the exclusive OR of all characters between, but not
150         // including, the "$" and "*".  A checksum is required on some
151 		// sentences.
152 
153 		// Run checksum test.
154 		if (checksum != null && !ignoreChecksum) {
155 			int csCalc = 0x00;
156 			int csReal;
157 
158 			//try {
159 				csReal = Integer.valueOf(checksum, 16);
160 			//} catch (NumberFormatException e) {
161 			//	throw new IllegalArgumentException("Bad NMEA-0183 sentence "
162 			//			+ "format: checksum not hexadecimal");
163 			//}
164 
165 			// XOR characters in sentence.
166 			for (int i = 0; i < sentence.length() - 3; i++) {
167 				csCalc ^= sentence.charAt(i);
168 			}
169 			
170 			if (csCalc != csReal) {
171 			//	throw new IllegalArgumentException("Bad NMEA-0183 sentence "
172 			//			+ "format: checksum failed");
173 			}
174 		}
175 
176 		// All tests passed.
177 	}
178 
179 	public String getField(int index) {
180 		return fields[index];
181 	}
182 	
183 	public String getField(String fieldName) {
184 		return null;
185 	}
186 	
187 	public int getFieldCount() {
188 		return fields.length;
189 	}
190 	
191 	public NMEATalkerID getTalkerID() {
192 		return talkerID;
193 	}
194 	
195 	public NMEASentenceID getSentenceID() {
196 		return sentenceID;
197 	}
198 	
199 	public NMEAManufacturerID getManufacturerID() {
200 		return manufacturerID;
201 	}
202 	
203 	public String getSentence() {
204 		return sentence;
205 	}
206 	
207 	public boolean isChecksumPresent() {
208 		return (checksum != null);
209 	}
210 
211 	public boolean isIgnoreChecksum() {
212 		return ignoreChecksum;
213 	}
214 
215 	public void setIgnoreChecksum(boolean ignoreChecksum) {
216 		this.ignoreChecksum = ignoreChecksum;
217 	}
218 
219 	public boolean isProprietary() {
220 		return proprietary;
221 	}
222 	
223 }