1 package au.gov.amsa.ais.message;
2
3 import com.google.common.base.Optional;
4
5 import au.gov.amsa.ais.AisExtractor;
6 import au.gov.amsa.ais.AisExtractorFactory;
7 import au.gov.amsa.ais.AisMessageType;
8 import au.gov.amsa.ais.Util;
9
10 public class AisPositionBExtended implements AisPosition, AisShipStatic {
11
12 private static final Integer TRUE_HEADING_NOT_AVAILABLE = 511;
13 private static final Integer COG_NOT_AVAILABLE = 3600;
14 private static final Integer SOG_NOT_AVAILABLE = 1023;
15 private static final Integer LONGITUDE_NOT_AVAILABLE = 181 * 600000;
16 private static final Integer LATITUDE_NOT_AVAILABLE = 91 * 600000;
17 private final AisExtractor extractor;
18 private final String source;
19 private final int messageId;
20 private final int repeatIndicator;
21 private final int mmsi;
22 private final Double speedOverGroundKnots;
23 private final Double courseOverGround;
24 private final Double latitude;
25 private final Double longitude;
26 private final Integer trueHeading;
27 private final String name;
28 private final int shipType;
29 private final int dimensionA;
30 private final int dimensionB;
31 private final int dimensionC;
32 private final int dimensionD;
33 private final int timeSecondsOnly;
34 private final boolean isHighAccuracyPosition;
35 private final boolean isUsingRAIM;
36
37 public AisPositionBExtended(String message, int padBits) {
38 this(message, null, padBits);
39 }
40
41 public AisPositionBExtended(String message, String source, int padBits) {
42 this(Util.getAisExtractorFactory(), message, source, padBits);
43 }
44
45 public AisPositionBExtended(AisExtractorFactory factory, String message, String source,
46 int padBits) {
47 this.source = source;
48 this.extractor = factory.create(message, 301, padBits);
49 messageId = extractor.getMessageId();
50 Util.checkMessageId(getMessageId(), AisMessageType.POSITION_REPORT_CLASS_B_EXTENDED);
51 repeatIndicator = extractor.getValue(6, 8);
52 mmsi = extractor.getValue(8, 38);
53 speedOverGroundKnots = extractSpeedOverGround(extractor);
54 isHighAccuracyPosition = Util.areEqual(extractor.getValue(56, 57), 1);
55 longitude = extractLongitude(extractor);
56 latitude = extractLatitude(extractor);
57 courseOverGround = extractCourseOverGround(extractor);
58 trueHeading = extractTrueHeading(extractor);
59 timeSecondsOnly = extractor.getValue(133, 139);
60 name = extractor.getString(143, 263);
61 shipType = extractor.getValue(263, 271);
62 dimensionA = extractor.getValue(271, 280);
63 dimensionB = extractor.getValue(280, 289);
64 dimensionC = extractor.getValue(289, 295);
65 dimensionD = extractor.getValue(295, 301);
66 isUsingRAIM = Util.areEqual(extractor.getValue(305, 306), 1);
67 }
68
69 static Integer extractTrueHeading(AisExtractor extractor) {
70 int val = extractor.getValue(124, 133);
71 if (val == TRUE_HEADING_NOT_AVAILABLE)
72 return null;
73 else
74 return val;
75 }
76
77 static Double extractCourseOverGround(AisExtractor extractor) {
78 int val = extractor.getValue(112, 124);
79 if (val == COG_NOT_AVAILABLE)
80 return null;
81 else
82 return val / 10.0;
83 }
84
85 static Double extractSpeedOverGround(AisExtractor extractor) {
86 int val = extractor.getValue(46, 56);
87 if (val == SOG_NOT_AVAILABLE)
88 return null;
89 else
90 return val / 10.0;
91 }
92
93 static Double extractLongitude(AisExtractor extractor) {
94 int val = extractor.getSignedValue(57, 85);
95 if (val == LONGITUDE_NOT_AVAILABLE) {
96 return null;
97 } else {
98 Util.checkLong(val / 600000.0);
99 return val / 600000.0;
100 }
101 }
102
103 static Double extractLatitude(AisExtractor extractor) {
104 int val = extractor.getSignedValue(85, 112);
105 if (val == LATITUDE_NOT_AVAILABLE) {
106 return null;
107 } else {
108 Util.checkLat(val / 600000.0);
109 return val / 600000.0;
110 }
111 }
112
113 @Override
114 public int getMessageId() {
115 return messageId;
116 }
117
118 @Override
119 public int getRepeatIndicator() {
120 return repeatIndicator;
121 }
122
123 @Override
124 public int getMmsi() {
125 return mmsi;
126 }
127
128 @Override
129 public Double getSpeedOverGroundKnots() {
130 return speedOverGroundKnots;
131 }
132
133 @Override
134 public Double getLongitude() {
135 return longitude;
136 }
137
138 @Override
139 public Double getLatitude() {
140 return latitude;
141 }
142
143 @Override
144 public Double getCourseOverGround() {
145 return courseOverGround;
146 }
147
148 @Override
149 public Integer getTrueHeading() {
150 return trueHeading;
151 }
152
153 @Override
154 public int getTimeSecondsOnly() {
155 return timeSecondsOnly;
156 }
157
158 @Override
159 public String getName() {
160 return name;
161
162 }
163
164 @Override
165 public int getShipType() {
166 return shipType;
167 }
168
169 @Override
170 public Optional<Integer> getDimensionA() {
171 if (dimensionA == 0)
172 return Optional.absent();
173 else
174 return Optional.of(dimensionA);
175 }
176
177 @Override
178 public Optional<Integer> getDimensionB() {
179 if (dimensionB == 0)
180 return Optional.absent();
181 else
182 return Optional.of(dimensionB);
183 }
184
185 @Override
186 public Optional<Integer> getDimensionC() {
187 if (dimensionC == 0)
188 return Optional.absent();
189 else
190 return Optional.of(dimensionC);
191 }
192
193 @Override
194 public Optional<Integer> getDimensionD() {
195 if (dimensionD == 0)
196 return Optional.absent();
197 else
198 return Optional.of(dimensionD);
199 }
200
201 @Override
202 public Optional<Integer> getLengthMetres() {
203 Optional<Integer> a = getDimensionA();
204 if (!a.isPresent())
205 return Optional.absent();
206 Optional<Integer> b = getDimensionB();
207 if (!b.isPresent())
208 return Optional.absent();
209 return Optional.of(a.get() + b.get());
210 }
211
212 @Override
213 public Optional<Integer> getWidthMetres() {
214 Optional<Integer> c = getDimensionC();
215 if (!c.isPresent())
216 return Optional.absent();
217 Optional<Integer> d = getDimensionD();
218 if (!d.isPresent())
219 return Optional.absent();
220 return Optional.of(c.get() + d.get());
221 }
222
223 @Override
224 public String getSource() {
225 return source;
226 }
227
228 @Override
229 public boolean isHighAccuracyPosition() {
230 return isHighAccuracyPosition;
231 }
232
233 @Override
234 public boolean isUsingRAIM() {
235 return isUsingRAIM;
236 }
237
238 @Override
239 public String toString() {
240 StringBuilder builder = new StringBuilder();
241 builder.append("AisPositionBExtended [source=");
242 builder.append(source);
243 builder.append(", messageId=");
244 builder.append(messageId);
245 builder.append(", mmsi=");
246 builder.append(mmsi);
247 builder.append(", repeatIndicator=");
248 builder.append(getRepeatIndicator());
249 builder.append(", speedOverGroundKnots=");
250 builder.append(getSpeedOverGroundKnots());
251 builder.append(", isHighAccuracyPosition=");
252 builder.append(isHighAccuracyPosition());
253 builder.append(", courseOverGround=");
254 builder.append(getCourseOverGround());
255 builder.append(", latitude=");
256 builder.append(getLatitude());
257 builder.append(", longitude=");
258 builder.append(getLongitude());
259 builder.append(", trueHeading=");
260 builder.append(getTrueHeading());
261 builder.append(", name=");
262 builder.append(getName());
263 builder.append(", shipType=");
264 builder.append(getShipType());
265 builder.append(", dimensionA=");
266 builder.append(getDimensionA());
267 builder.append(", dimensionB=");
268 builder.append(getDimensionB());
269 builder.append(", dimensionC=");
270 builder.append(getDimensionC());
271 builder.append(", dimensionD=");
272 builder.append(getDimensionD());
273 builder.append(", timeSecondsOnly=");
274 builder.append(getTimeSecondsOnly());
275 builder.append("]");
276 return builder.toString();
277 }
278
279 }