1 package au.gov.amsa.ais;
2
3 import java.nio.charset.Charset;
4 import java.util.Arrays;
5 import java.util.TimeZone;
6
7 import com.google.common.annotations.VisibleForTesting;
8
9
10
11
12 public final class Util {
13
14 private Util() {
15
16 }
17
18 static void forTestCoverageOnly() {
19 new Util();
20 }
21
22
23
24
25
26
27
28
29
30 protected static int getValueByBinStr(String binaryString, boolean signed) {
31
32 Integer value = Integer.parseInt(binaryString, 2);
33 if (signed && binaryString.charAt(0) == '1') {
34 char[] invert = new char[binaryString.length()];
35 Arrays.fill(invert, '1');
36 value ^= Integer.parseInt(new String(invert), 2);
37 value += 1;
38 value = -value;
39 }
40
41 return value;
42 }
43
44 static TimeZone TIME_ZONE_UTC = TimeZone.getTimeZone("UTC");
45
46 private static Charset ASCII_8_BIT_CHARSET = Charset.forName("ISO-8859-1");
47
48
49
50
51
52
53
54
55 protected static String decodeMessage(String encodedMessage) {
56 return getDecodedStr(ascii8To6bitBin(encodedMessage.getBytes(ASCII_8_BIT_CHARSET)));
57 }
58
59
60
61
62
63
64
65 @VisibleForTesting
66 static byte[] ascii8To6bitBin(byte[] toDecBytes) {
67
68 byte[] convertedBytes = new byte[toDecBytes.length];
69 int sum = 0;
70 int _6bitBin = 0;
71
72 for (int i = 0; i < toDecBytes.length; i++) {
73 sum = 0;
74 _6bitBin = 0;
75
76 if (toDecBytes[i] < 48) {
77 throw new AisParseException(AisParseException.INVALID_CHARACTER + " "
78 + (char) toDecBytes[i]);
79 } else {
80 if (toDecBytes[i] > 119) {
81 throw new AisParseException(AisParseException.INVALID_CHARACTER + " "
82 + (char) toDecBytes[i]);
83 } else {
84 if (toDecBytes[i] > 87) {
85 if (toDecBytes[i] < 96) {
86 throw new AisParseException(AisParseException.INVALID_CHARACTER + " "
87 + (char) toDecBytes[i]);
88 } else {
89 sum = toDecBytes[i] + 40;
90 }
91 } else {
92 sum = toDecBytes[i] + 40;
93 }
94 if (sum != 0) {
95 if (sum > 128) {
96 sum += 32;
97 } else {
98 sum += 40;
99 }
100 _6bitBin = sum & 0x3F;
101 convertedBytes[i] = (byte) _6bitBin;
102 }
103 }
104 }
105 }
106
107 return convertedBytes;
108 }
109
110
111
112
113
114
115 private static String getDecodedStr(byte[] decBytes) {
116
117
118
119 int n = decBytes.length * 6;
120 int capacity = leastPowerOf2GreaterThanOrEqualTo(n);
121 StringBuilder decStr = new StringBuilder(capacity);
122
123 for (int i = 0; i < decBytes.length; i++) {
124
125 int decByte = decBytes[i];
126 String bitStr = Integer.toBinaryString(decByte);
127
128 int padding = Math.max(0, 6 - bitStr.length());
129
130 for (int j = 0; j < padding; j++) {
131 decStr.append('0');
132 }
133
134 for (int j = 0; j < 6 - padding; j++) {
135 decStr.append(bitStr.charAt(j));
136 }
137 }
138 return decStr.toString();
139 }
140
141 static int leastPowerOf2GreaterThanOrEqualTo(int n) {
142 return n > 1 ? Integer.highestOneBit(n - 1) << 1 : 1;
143 }
144
145
146
147
148
149
150
151
152
153
154 protected static String getAsciiStringFrom6BitStr(String str) {
155
156 StringBuilder txt = new StringBuilder();
157 for (int i = 0; i < str.length(); i = i + 6) {
158 byte _byte = (byte) Integer.parseInt(str.substring(i, i + 6), 2);
159 _byte = convert6BitCharToStandardAscii(_byte);
160 char convChar = (char) _byte;
161
162 if (convChar == '@') {
163 break;
164 }
165 txt.append((char) _byte);
166 }
167
168 return txt.toString().trim();
169 }
170
171
172
173
174
175
176
177 @VisibleForTesting
178 static byte convert6BitCharToStandardAscii(byte byteToConvert) {
179
180 byte b = 0;
181 if (byteToConvert < 32) {
182 b = (byte) (byteToConvert + 64);
183 } else if (byteToConvert < 63) {
184 b = byteToConvert;
185 }
186
187 return b;
188 }
189
190
191
192
193
194
195
196
197 public static void checkLatLong(double lat, double lon) {
198 checkArgument(lon <= 181.0, "longitude out of range " + lon);
199 checkArgument(lon > -180.0, "longitude out of range " + lon);
200 checkArgument(lat <= 91.0, "latitude out of range " + lat);
201 checkArgument(lat > -90.0, "latitude out of range " + lat);
202 }
203
204
205
206
207
208
209
210 public static void checkLat(double lat) {
211 checkArgument(lat <= 91.0, "latitude out of range ");
212 checkArgument(lat > -90.0, "latitude out of range ");
213 }
214
215
216
217
218
219
220
221 public static void checkLong(double lon) {
222 checkArgument(lon <= 181.0, "longitude out of range");
223 checkArgument(lon > -180.0, "longitude out of range");
224 }
225
226
227
228
229
230
231
232 public static void checkArgument(boolean b, String message) {
233 if (!b)
234 throw new AisParseException(message);
235 }
236
237 private static AisExtractorFactory extractorFactory = new AisExtractorFactory() {
238
239 @Override
240 public AisExtractor create(String message, int minLength, int padBits) {
241 return new AisExtractor(message, minLength, padBits);
242 }
243
244 };
245
246
247
248
249
250
251 public static AisExtractorFactory getAisExtractorFactory() {
252 return extractorFactory;
253 }
254
255
256
257
258
259
260
261 public static void checkMessageId(int messageId, AisMessageType... messageTypes) {
262 boolean found = false;
263 for (AisMessageType messageType : messageTypes) {
264 if (messageType.getId() == messageId)
265 found = true;
266 }
267 if (!found) {
268 StringBuffer s = new StringBuffer();
269 for (AisMessageType messageType : messageTypes) {
270 if (s.length() > 0)
271 s.append(",");
272 s.append(messageType.getId() + "");
273 }
274 checkArgument(found, "messageId must be in [" + s + "] but was " + messageId);
275 }
276 }
277
278
279
280
281
282
283
284
285
286 public static boolean areEqual(int i, int j) {
287 return i == j;
288 }
289
290
291
292
293
294
295
296
297 public static boolean isClassAPositionReport(int messageId) {
298 return messageId == 1 || messageId == 2 || messageId == 3;
299 }
300
301 }