Package: NMEAParser

NMEAParser

nameinstructionbranchcomplexitylinemethod
NMEAParser()
M: 0 C: 12
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
checkPosition(List, int)
M: 0 C: 10
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
computeNMEACksum(String)
M: 0 C: 40
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 7
100%
M: 0 C: 1
100%
convertPosition(String, String, int)
M: 2 C: 38
95%
M: 1 C: 5
83%
M: 1 C: 3
75%
M: 1 C: 9
90%
M: 0 C: 1
100%
convertPositionlat(String, String)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
convertPositionlon(String, String)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getAltNmea()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getAltitude()
M: 0 C: 7
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getDOPNmea()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getDateNmea()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getFix3DNmea()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getFixQuality()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getHDOPNmea()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getLatNmea()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getLatitude()
M: 0 C: 8
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getLatitudeHemisphere()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getLongNmea()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getLongitude()
M: 0 C: 8
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getLongitudeHemisphere()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getNmeaPosition()
M: 0 C: 34
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
getNrSatellites()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getPDOPNmea()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getPosition()
M: 0 C: 14
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getSpeed()
M: 0 C: 7
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getSpeedNmea()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getTimeNmea()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getTrack()
M: 0 C: 8
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getTrackNmea()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getVDOPNmea()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getValidFix()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
isValidPosition()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
parseGGASentence(List)
M: 3 C: 144
98%
M: 10 C: 12
55%
M: 10 C: 2
17%
M: 1 C: 22
96%
M: 0 C: 1
100%
parseGLLSentence(List)
M: 7 C: 97
93%
M: 8 C: 8
50%
M: 8 C: 1
11%
M: 3 C: 12
80%
M: 0 C: 1
100%
parseGSASentence(List)
M: 3 C: 89
97%
M: 5 C: 7
58%
M: 5 C: 2
29%
M: 1 C: 15
94%
M: 0 C: 1
100%
parseRMCSentence(List)
M: 9 C: 150
94%
M: 10 C: 12
55%
M: 10 C: 2
17%
M: 3 C: 24
89%
M: 0 C: 1
100%
parseSentence(String)
M: 0 C: 116
100%
M: 3 C: 17
85%
M: 3 C: 8
73%
M: 0 C: 19
100%
M: 0 C: 1
100%
parseVTGSentence(List)
M: 0 C: 20
100%
M: 2 C: 2
50%
M: 2 C: 1
33%
M: 0 C: 3
100%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2011, 2018 Eurotech and/or its affiliates
3: *
4: * All rights reserved. This program and the accompanying materials
5: * are made available under the terms of the Eclipse Public License v1.0
6: * which accompanies this distribution, and is available at
7: * http://www.eclipse.org/legal/epl-v10.html
8: *
9: * Contributors:
10: * Eurotech
11: *******************************************************************************/
12: package org.eclipse.kura.linux.position;
13:
14: import static java.lang.Math.toRadians;
15:
16: import java.util.Arrays;
17: import java.util.List;
18:
19: import org.eclipse.kura.position.NmeaPosition;
20: import org.osgi.util.measurement.Measurement;
21: import org.osgi.util.measurement.Unit;
22: import org.osgi.util.position.Position;
23:
24: /**
25: * Implements NMEA sentences parser functions.
26: *
27: */
28: public class NMEAParser {
29:
30: private int fixQuality;
31: private String timeNmea;
32: private String dateNmea;
33: private double longNmea;
34: private double latNmea;
35: private double speedNmea;
36: private double altNmea;
37: private double trackNmea;
38: private double dopNmea;
39: private double pdopNmea;
40: private double hdopNmea;
41: private double vdopNmea;
42: private int fix3DNmea;
43: private int nrSatellites;
44: private boolean validPosition;
45: private char validFix = 0;
46: private char latitudeHemisphere = 0;
47: private char longitudeHemisphere = 0;
48:
49: /**
50: * Fill the fields of GPS position depending of the type of the sentence
51: *
52: * @param sentence
53: * most recent sentence String from GPS modem
54: */
55: public boolean parseSentence(String sentence) throws ParseException {
56:
57:• if (!computeNMEACksum(sentence)) {
58: throw new ParseException(Code.BAD_CHECKSUM);
59: }
60:
61: // first remove the end "*"+chksum
62: int starpos = sentence.indexOf('*');
63: final List<String> tokens = Arrays.asList(sentence.substring(0, starpos).split(","));
64:
65: /*
66: * Starting from 4.0 NMEA specs the GPS device can send messages representing different talkers
67: *
68: * $GP = GPS
69: * $GS = Glonass
70: * $GN = GNSS, that is GPS + Glonass + possibly others
71: */
72:• if (!tokens.get(0).startsWith("$G")) {
73: // Not a valid token. Return.
74: throw new ParseException(Code.INVALID);
75: }
76:
77:• if (tokens.get(0).endsWith("GGA")) {
78: parseGGASentence(tokens);
79:• } else if (tokens.get(0).endsWith("GLL")) {
80: parseGLLSentence(tokens);
81:• } else if (tokens.get(0).endsWith("RMC")) {
82: parseRMCSentence(tokens);
83:• } else if (tokens.get(0).endsWith("GSA")) {
84: parseGSASentence(tokens);
85:• } else if (tokens.get(0).endsWith("VTG")) {
86: parseVTGSentence(tokens);
87:• } else if (!tokens.get(0).endsWith("GSV") && sentence.indexOf("FOM") == -1 && sentence.indexOf("PPS") == -1) {
88: throw new ParseException(Code.UNRECOGNIZED);
89: }
90:
91: return this.validPosition;
92: }
93:
94: private void parseVTGSentence(List<String> tokens) {
95:• if (tokens.size() > 7 && !tokens.get(7).isEmpty()) {
96: // conversion km/h in m/s : 1 km/h -> 0,277777778 m/s
97: this.speedNmea = Double.parseDouble(tokens.get(7)) * 0.277777778;
98: }
99: }
100:
101: private void parseGSASentence(List<String> tokens) {
102:• if (tokens.size() > 5) {
103: // Check only last 3 items for validity
104: checkPosition(tokens.subList(tokens.size() - 3, tokens.size()), 3);
105:• if (!tokens.get(2).isEmpty()) {
106: this.fix3DNmea = Integer.parseInt(tokens.get(2));
107:• if (this.fix3DNmea == 1) {
108: this.validPosition = false;
109: }
110: }
111: int index = tokens.size() - 3;
112:• if (!tokens.get(index).isEmpty()) {
113: this.pdopNmea = Double.parseDouble(tokens.get(index));
114: }
115:• if (!tokens.get(index + 1).isEmpty()) {
116: this.hdopNmea = Double.parseDouble(tokens.get(index + 1));
117: }
118:• if (!tokens.get(index + 2).isEmpty()) {
119: this.vdopNmea = Double.parseDouble(tokens.get(index + 2));
120: }
121: } else {
122: this.validPosition = false;
123: }
124: }
125:
126: private void parseRMCSentence(List<String> tokens) {
127:• if (tokens.size() > 9) {
128:• if (!tokens.get(1).isEmpty()) {
129: this.timeNmea = tokens.get(1);
130: }
131:• if (!tokens.get(2).isEmpty()) { // check validity
132: this.validFix = tokens.get(2).charAt(0);
133:• if (!"A".equals(tokens.get(2))) {
134: this.validPosition = false;
135: } else {
136: this.validPosition = true;
137: }
138: } else {
139: this.validFix = 'V';
140: this.validPosition = false;
141: }
142:• if (!tokens.get(3).isEmpty() && !tokens.get(4).isEmpty()) {
143: this.latNmea = convertPositionlat(tokens.get(3), tokens.get(4));
144: this.latitudeHemisphere = tokens.get(4).charAt(0);
145: }
146:• if (!tokens.get(5).isEmpty() && !tokens.get(6).isEmpty()) {
147: this.longNmea = convertPositionlon(tokens.get(5), tokens.get(6));
148: this.longitudeHemisphere = tokens.get(6).charAt(0);
149: }
150:• if (!tokens.get(7).isEmpty()) {
151: // conversion speed in knots to m/s : 1 m/s = 1.94384449 knots
152: this.speedNmea = Double.parseDouble(tokens.get(7)) / 1.94384449;
153: }
154:• if (!tokens.get(8).isEmpty()) {
155: this.trackNmea = Double.parseDouble(tokens.get(8));
156: }
157:• if (!tokens.get(9).isEmpty()) {
158: this.dateNmea = tokens.get(9);
159: }
160: } else {
161: this.validPosition = false;
162: }
163: }
164:
165: private void parseGLLSentence(List<String> tokens) {
166:• if (tokens.size() > 6) {
167: checkPosition(tokens, 7);
168:• if (!tokens.get(1).isEmpty() && !tokens.get(2).isEmpty()) {
169: this.latNmea = convertPositionlat(tokens.get(1), tokens.get(2));
170: this.latitudeHemisphere = tokens.get(2).charAt(0);
171: }
172:• if (!tokens.get(3).isEmpty() && !tokens.get(4).isEmpty()) {
173: this.longNmea = convertPositionlon(tokens.get(3), tokens.get(4));
174: this.longitudeHemisphere = tokens.get(4).charAt(0);
175: }
176:• if (!tokens.get(5).isEmpty()) {
177: this.timeNmea = tokens.get(5);
178: }
179:• if (!tokens.get(6).isEmpty() && !"A".equals(tokens.get(6))) { // check validity
180: this.validPosition = false;
181: }
182: } else {
183: this.validPosition = false;
184: }
185: }
186:
187: private void parseGGASentence(List<String> tokens) {
188:• if (tokens.size() > 9) {
189: checkPosition(tokens, 10);
190:• if (!tokens.get(1).isEmpty()) {
191: this.timeNmea = tokens.get(1);
192: }
193:• if (!tokens.get(2).isEmpty() && !tokens.get(3).isEmpty()) {
194: this.latNmea = convertPositionlat(tokens.get(2), tokens.get(3));
195: this.latitudeHemisphere = tokens.get(3).charAt(0);
196: }
197:• if (!tokens.get(4).isEmpty() && !tokens.get(5).isEmpty()) {
198: this.longNmea = convertPositionlon(tokens.get(4), tokens.get(5));
199: this.longitudeHemisphere = tokens.get(5).charAt(0);
200: }
201:• if (!tokens.get(6).isEmpty()) {
202: this.fixQuality = Integer.parseInt(tokens.get(6));
203:• if (this.fixQuality == 0) {
204: this.validPosition = false;
205: }
206: }
207:• if (!tokens.get(7).isEmpty()) {
208: this.nrSatellites = Integer.parseInt(tokens.get(7));
209: }
210:• if (!tokens.get(8).isEmpty()) {
211: this.dopNmea = Double.parseDouble(tokens.get(8));
212: }
213:• if (!tokens.get(9).isEmpty()) {
214: this.altNmea = Double.parseDouble(tokens.get(9));
215: }
216: } else {
217: this.validPosition = false;
218: }
219: }
220:
221: private void checkPosition(List<String> tokens, int size) {
222: // Create a list containing the indexes of empty tokens
223: this.validPosition = tokens.stream().limit(size).noneMatch(String::isEmpty);
224: }
225:
226: /**
227: * @param pos
228: * DDD?MM?.dddd
229: * @param direction
230: * N/S, E/W
231: * @param degChars
232: * number of characters representing degrees
233: * @return
234: */
235: private double convertPosition(String pos, String direction, int degChars) {
236:• if (pos.length() < 6) {
237: return 0;
238: }
239:
240: String s = pos.substring(0, degChars);
241: double deg = Double.parseDouble(s);
242: s = pos.substring(degChars);
243: double min = Double.parseDouble(s);
244: deg = deg + min / 60;
245:• if (direction.contains("S") || direction.contains("W")) {
246: deg = -deg;
247: }
248: return deg;
249: }
250:
251: double convertPositionlat(String pos, String direction) {
252: return convertPosition(pos, direction, 2);
253: }
254:
255: double convertPositionlon(String pos, String direction) {
256: return convertPosition(pos, direction, 3);
257: }
258:
259: boolean computeNMEACksum(String nmeaMessageIn) {
260: final int starpos = nmeaMessageIn.indexOf('*');
261: final String strChecksum = nmeaMessageIn.substring(starpos + 1, nmeaMessageIn.length() - 1);
262: final int parsedChecksum = Integer.parseInt(strChecksum, 16); // Check sum is coded in hex string
263:
264: int actualChecksum = 0;
265:• for (int i = 1; i < starpos; i++) {
266: actualChecksum ^= nmeaMessageIn.charAt(i);
267: }
268:
269:• return actualChecksum == parsedChecksum;
270: }
271:
272: public String getTimeNmea() {
273: return this.timeNmea;
274: }
275:
276: public int getFixQuality() {
277: return this.fixQuality;
278: }
279:
280: public String getDateNmea() {
281: return this.dateNmea;
282: }
283:
284: public double getLongNmea() {
285: return this.longNmea;
286: }
287:
288: public double getLatNmea() {
289: return this.latNmea;
290: }
291:
292: public double getSpeedNmea() {
293: return this.speedNmea;
294: }
295:
296: public double getAltNmea() {
297: return this.altNmea;
298: }
299:
300: public double getTrackNmea() {
301: return this.trackNmea;
302: }
303:
304: public double getDOPNmea() {
305: return this.dopNmea;
306: }
307:
308: public double getPDOPNmea() {
309: return this.pdopNmea;
310: }
311:
312: public double getHDOPNmea() {
313: return this.hdopNmea;
314: }
315:
316: public double getVDOPNmea() {
317: return this.vdopNmea;
318: }
319:
320: public int getFix3DNmea() {
321: return this.fix3DNmea;
322: }
323:
324: public int getNrSatellites() {
325: return this.nrSatellites;
326: }
327:
328: public Measurement getLatitude() {
329: return new Measurement(toRadians(this.latNmea), Unit.rad);
330: }
331:
332: public Measurement getLongitude() {
333: return new Measurement(toRadians(this.longNmea), Unit.rad);
334: }
335:
336: public Measurement getAltitude() {
337: return new Measurement(this.altNmea, Unit.m);
338: }
339:
340: public Measurement getSpeed() {
341: return new Measurement(this.speedNmea, Unit.m_s);
342: }
343:
344: public Measurement getTrack() {
345: return new Measurement(toRadians(this.trackNmea), Unit.rad);
346: }
347:
348: public Position getPosition() {
349: return new Position(getLatitude(), getLongitude(), getAltitude(), getSpeed(), getTrack());
350: }
351:
352: public NmeaPosition getNmeaPosition() {
353: return new NmeaPosition(getLatNmea(), getLongNmea(), getAltNmea(), getSpeedNmea(), getTrackNmea(),
354: getFixQuality(), getNrSatellites(), getDOPNmea(), getPDOPNmea(), getHDOPNmea(), getVDOPNmea(),
355: getFix3DNmea(), getValidFix(), getLatitudeHemisphere(), getLongitudeHemisphere());
356: }
357:
358: public boolean isValidPosition() {
359: return this.validPosition;
360: }
361:
362: public char getValidFix() {
363: return this.validFix;
364: }
365:
366: public char getLatitudeHemisphere() {
367: return this.latitudeHemisphere;
368: }
369:
370: public char getLongitudeHemisphere() {
371: return this.longitudeHemisphere;
372: }
373:
374: public enum Code {
375: INVALID,
376: BAD_CHECKSUM,
377: UNRECOGNIZED
378: }
379:
380: public class ParseException extends Exception {
381:
382: private static final long serialVersionUID = -1441433820817330483L;
383: private final Code code;
384:
385: public ParseException(final Code code) {
386: this.code = code;
387: }
388:
389: public Code getCode() {
390: return this.code;
391: }
392: }
393: }