1 package au.gov.amsa.util.nmea;
2
3 import java.util.LinkedHashMap;
4 import java.util.List;
5 import java.util.Map.Entry;
6 import java.util.Set;
7
8 import au.gov.amsa.ais.AisParseException;
9
10 import com.google.common.collect.Sets;
11
12 public final class NmeaUtil {
13
14 private static final char BACKSLASH = '\\';
15
16 private NmeaUtil() {
17
18 }
19
20 static void forTestCoverageOnly() {
21 new NmeaUtil();
22 }
23
24 private static final Set<Integer> invalidFieldCharacters = Sets.newHashSet(33, 36, 42, 44, 92,
25 94, 126, 127);
26
27 private static final Set<Character> validCharacterSymbols = createValidCharacterSymbols();
28
29 private static Set<Character> createValidCharacterSymbols() {
30 String s = "AaBCcDdEFfGgHhIJKkLlMmNnPQRrSsTtUuVWxyZ";
31 Set<Character> set = Sets.newHashSet();
32 for (char ch : s.toCharArray())
33 set.add(ch);
34 return set;
35 }
36
37 static boolean isValidFieldCharacter(char ch) {
38 return ch <= 127 && ch >= 32 && !invalidFieldCharacters.contains((int) ch);
39 }
40
41 static boolean isValidCharacterSymbol(char ch) {
42 return validCharacterSymbols.contains(ch);
43 }
44
45
46
47
48
49
50
51
52 public static boolean isValid(String sentence) {
53
54
55 try {
56 return sentence.substring(sentence.lastIndexOf("*") + 1)
57 .equalsIgnoreCase(getChecksum(sentence));
58 } catch (AisParseException e) {
59 return false;
60 }
61 }
62
63 public static String getChecksum(String sentence) {
64 return getChecksum(sentence, true);
65 }
66
67 public static String getChecksum(String sentence, boolean ignoreLeadingDollarOrExclamation) {
68
69 int startIndex;
70
71 if (sentence.startsWith("\\")) {
72 startIndex = sentence.indexOf('\\', 1) + 1;
73 if (startIndex == 0)
74 throw new AisParseException("no closing \\ for tag block");
75 } else
76 startIndex = 0;
77
78 int checksum = 0;
79 for (int i = startIndex; i < sentence.length(); i++) {
80 char ch = sentence.charAt(i);
81 if (ignoreLeadingDollarOrExclamation && (ch == '$' || ch == '!')) {
82
83 } else if (ch == '*') {
84
85 break;
86 } else {
87
88 if (checksum == 0) {
89
90 checksum = ch;
91 } else {
92
93 checksum = checksum ^ ch;
94 }
95 }
96 }
97
98 String s = Integer.toHexString(checksum % 256);
99 if (s.length() == 1)
100 s = "0" + s;
101 return s.toUpperCase();
102 }
103
104 private static NmeaMessageParser nmeaParser = new NmeaMessageParser();
105
106 public static NmeaMessage parseNmea(String line) {
107 return nmeaParser.parse(line);
108 }
109
110 public static String insertKeyValueInTagBlock(String line, String name, String value) {
111 line = line.trim();
112 if (line.startsWith("\\")) {
113
114
115 int i = line.indexOf(BACKSLASH, 1);
116 if (i == -1) {
117
118 return line;
119 }
120 if (i < 4) {
121
122 return line;
123 }
124 String content = line.substring(1, i - 3);
125 StringBuilder s = new StringBuilder(content);
126 s.append(",");
127 s.append(name);
128 s.append(":");
129 s.append(value);
130 String checksum = NmeaUtil.getChecksum(s.toString(), false);
131 s.append('*');
132 s.append(checksum);
133 s.append(line.substring(i));
134 s.insert(0, BACKSLASH);
135 return s.toString();
136 } else {
137 return line;
138 }
139 }
140
141 public static String supplementWithTime(String line, long arrivalTime) {
142 line = line.trim();
143 final String amendedLine;
144 NmeaMessage m = parseNmea(line);
145 Long t = m.getUnixTimeMillis();
146 Long a = m.getArrivalTimeMillis();
147 if (t == null) {
148
149 t = arrivalTime;
150
151
152 if (line.startsWith("\\")) {
153
154
155 int i = line.indexOf(BACKSLASH, 1);
156 if (i == -1)
157 throw new RuntimeException(
158 "line starts with \\ but does not have closing tag block delimiter \\");
159 if (i < 4)
160 throw new RuntimeException("tag block not long enough to have a checksum");
161 String content = line.substring(1, i - 3);
162 StringBuilder s = new StringBuilder(content);
163 s.append(",");
164 appendTimes(arrivalTime, t, s);
165 String checksum = NmeaUtil.getChecksum(s.toString(), false);
166 s.append('*');
167 s.append(checksum);
168 s.append(line.substring(i));
169 s.insert(0, BACKSLASH);
170 amendedLine = s.toString();
171 } else {
172 StringBuilder s = new StringBuilder();
173 appendTimes(t, arrivalTime, s);
174 String checksum = NmeaUtil.getChecksum(s.toString(), false);
175 s.append("*");
176 s.append(checksum);
177 s.append(BACKSLASH);
178 s.append(line);
179 s.insert(0, BACKSLASH);
180 amendedLine = s.toString();
181 }
182 } else if (a == null) {
183
184
185
186
187 int i = line.indexOf(BACKSLASH, 1);
188 String content = line.substring(1, i - 3);
189 StringBuilder s = new StringBuilder(content);
190 s.append(",a:");
191 s.append(arrivalTime);
192 String checksum = NmeaUtil.getChecksum(s.toString(), false);
193 s.append('*');
194 s.append(checksum);
195 s.append(line.substring(i));
196 s.insert(0, BACKSLASH);
197 amendedLine = s.toString();
198 } else {
199 amendedLine = line;
200 }
201 return amendedLine;
202
203 }
204
205 private static void appendTimes(long arrivalTime, Long t, StringBuilder s) {
206 s.append("c:");
207 s.append(t / 1000);
208 s.append(",a:");
209 s.append(arrivalTime);
210 }
211
212 public static Talker getTalker(String s) {
213 if (s == null)
214 return null;
215 else {
216 try {
217 return Talker.valueOf(s);
218 } catch (RuntimeException e) {
219 return Talker.UNKNOWN;
220 }
221 }
222 }
223
224 public static String createTagBlock(LinkedHashMap<String, String> tags) {
225 if (tags == null || tags.size() == 0)
226 return "";
227 StringBuilder s = new StringBuilder(128);
228 s.append("\\");
229 int startChecksum = s.length();
230 boolean first = true;
231 for (Entry<String, String> entry : tags.entrySet()) {
232 if (!first)
233 s.append(",");
234 s.append(entry.getKey());
235 s.append(":");
236 s.append(entry.getValue());
237 first = false;
238 }
239 String checksum = NmeaUtil.getChecksum(s.substring(startChecksum));
240 s.append("*");
241 s.append(checksum);
242 s.append("\\");
243 return s.toString();
244 }
245
246 public static String createNmeaLine(LinkedHashMap<String, String> tags, List<String> items) {
247 StringBuilder s = new StringBuilder(40);
248 s.append(createTagBlock(tags));
249 int startForChecksum = s.length();
250 boolean first = true;
251 for (String item : items) {
252 if (!first)
253 s.append(",");
254 s.append(item);
255 first = false;
256 }
257 String checksum = NmeaUtil.getChecksum(s.substring(startForChecksum));
258 s.append("*");
259 s.append(checksum);
260
261 return s.toString();
262 }
263
264 }