1 package au.gov.amsa.ais.message;
2
3 import static au.gov.amsa.ais.AisMessageType.POSITION_REPORT_ASSIGNED;
4 import static au.gov.amsa.ais.AisMessageType.POSITION_REPORT_SCHEDULED;
5 import static au.gov.amsa.ais.AisMessageType.POSITION_REPORT_SPECIAL;
6
7 import au.gov.amsa.ais.AisExtractor;
8 import au.gov.amsa.ais.AisExtractorFactory;
9 import au.gov.amsa.ais.AisParseException;
10 import au.gov.amsa.ais.Communications;
11 import au.gov.amsa.ais.HasCommunications;
12 import au.gov.amsa.ais.Util;
13
14
15
16
17
18
19
20 public class AisPositionA implements AisPosition, HasCommunications {
21
22 private static final Integer TRUE_HEADING_NOT_AVAILABLE = 511;
23 private static final Integer COG_NOT_AVAILABLE = 3600;
24 private static final Integer SOG_NOT_AVAILABLE = 1023;
25 private static final Integer ROT_NOT_AVAILABLE = -128;
26 private static final Integer LONGITUDE_NOT_AVAILABLE = 181 * 600000;
27 private static final Integer LATITUDE_NOT_AVAILABLE = 91 * 600000;
28 private final AisExtractor extractor;
29 private final String source;
30 private final int messageId;
31 private final int mmsi;
32 private final Double longitude;
33 private final Double latitude;
34
35 public AisPositionA(String message, String source, int padBits) {
36 this(Util.getAisExtractorFactory(), message, source, padBits);
37 }
38
39 public AisPositionA(String message, int padBits) {
40 this(Util.getAisExtractorFactory(), message, null, padBits);
41 }
42
43 public AisPositionA(AisExtractorFactory factory, String message, String source, int padBits) {
44 this.source = source;
45 this.extractor = factory.create(message, 137, padBits);
46 messageId = extractor.getMessageId();
47 Util.checkMessageId(messageId, POSITION_REPORT_SCHEDULED, POSITION_REPORT_ASSIGNED,
48 POSITION_REPORT_SPECIAL);
49 mmsi = extractor.getValue(8, 38);
50 longitude = extractLongitude(extractor);
51 latitude = extractLatitude(extractor);
52 }
53
54 static Integer extractTrueHeading(AisExtractor extractor) {
55 try {
56 int val = extractor.getValue(128, 137);
57 if (val == TRUE_HEADING_NOT_AVAILABLE)
58 return null;
59 else if (val > 359) {
60
61 return null;
62 } else
63 return val;
64 } catch (AisParseException e) {
65 return null;
66 }
67 }
68
69 static Double extractCourseOverGround(AisExtractor extractor) {
70 try {
71 int val = extractor.getValue(116, 128);
72 if (val == COG_NOT_AVAILABLE || val >= 3600)
73 return null;
74 else
75 return val / 10.0;
76 } catch (AisParseException e) {
77 return null;
78 }
79 }
80
81 static Double extractSpeedOverGround(AisExtractor extractor) {
82 try {
83 int val = extractor.getValue(50, 60);
84 if (val == SOG_NOT_AVAILABLE)
85 return null;
86 else
87 return val / 10.0;
88 } catch (AisParseException e) {
89 return null;
90 }
91 }
92
93 static Integer extractRateOfTurn(AisExtractor extractor) {
94 try {
95 byte val = (byte) extractor.getSignedValue(42, 50);
96 if (val == ROT_NOT_AVAILABLE)
97 return null;
98 else
99 return (int) val;
100 } catch (AisParseException e) {
101 return null;
102 }
103 }
104
105 static Double extractLongitude(AisExtractor extractor) {
106 try {
107 int val = extractor.getSignedValue(61, 89);
108 if (val == LONGITUDE_NOT_AVAILABLE) {
109 return null;
110 } else {
111 Util.checkLong(val / 600000.0);
112 return val / 600000.0;
113 }
114 } catch (AisParseException e) {
115 return null;
116 }
117 }
118
119 static Double extractLatitude(AisExtractor extractor) {
120
121 try {
122 int val = extractor.getSignedValue(89, 116);
123 if (val == LATITUDE_NOT_AVAILABLE) {
124 return null;
125 } else {
126 Util.checkLat(val / 600000.0);
127 return val / 600000.0;
128 }
129 } catch (AisParseException e) {
130 return null;
131 }
132 }
133
134 @Override
135 public int getMessageId() {
136 return messageId;
137 }
138
139 @Override
140 public int getRepeatIndicator() {
141 return extractor.getValue(6, 8);
142 }
143
144 @Override
145 public int getMmsi() {
146 return mmsi;
147 }
148
149 public NavigationalStatus getNavigationalStatus() {
150 return NavigationalStatus.values()[extractor.getValue(38, 42)];
151 }
152
153 public Integer getRateOfTurn() {
154 return extractRateOfTurn(extractor);
155 }
156
157 @Override
158 public Double getSpeedOverGroundKnots() {
159 return extractSpeedOverGround(extractor);
160 }
161
162 @Override
163 public boolean isHighAccuracyPosition() {
164 return Util.areEqual(extractor.getValue(60, 61), 1);
165 }
166
167 @Override
168 public Double getLongitude() {
169 return longitude;
170 }
171
172 @Override
173 public Double getLatitude() {
174 return latitude;
175 }
176
177 @Override
178 public Double getCourseOverGround() {
179 return extractCourseOverGround(extractor);
180 }
181
182 @Override
183 public Integer getTrueHeading() {
184 return extractTrueHeading(extractor);
185 }
186
187 @Override
188 public int getTimeSecondsOnly() {
189 return extractor.getValue(137, 143);
190 }
191
192 public int getSpecialManoeuvreIndicator() {
193 return extractor.getValue(143, 145);
194 }
195
196 public int getSpare() {
197 return extractor.getValue(145, 148);
198 }
199
200 @Override
201 public boolean isUsingRAIM() {
202 return Util.areEqual(extractor.getValue(148, 149), 1);
203 }
204
205 @Override
206 public Communications getCommunications() {
207 return new Communications(extractor, 149);
208 }
209
210 @Override
211 public String getSource() {
212 return source;
213 }
214
215 @Override
216 public String toString() {
217 StringBuilder builder = new StringBuilder();
218 builder.append("AisPositionA [source=");
219 builder.append(source);
220 builder.append(", messageId=");
221 builder.append(messageId);
222 builder.append(", repeatIndicator=");
223 builder.append(getRepeatIndicator());
224 builder.append(", mmsi=");
225 builder.append(mmsi);
226 builder.append(", navigationalStatus=");
227 builder.append(getNavigationalStatus());
228 builder.append(", rateOfTurn=");
229 builder.append(getRateOfTurn());
230 builder.append(", speedOverGroundKnots=");
231 builder.append(getSpeedOverGroundKnots());
232 builder.append(", isHighAccuracyPosition=");
233 builder.append(isHighAccuracyPosition());
234 builder.append(", longitude=");
235 builder.append(longitude);
236 builder.append(", latitude=");
237 builder.append(latitude);
238 builder.append(", courseOverGround=");
239 builder.append(getCourseOverGround());
240 builder.append(", trueHeading=");
241 builder.append(getTrueHeading());
242 builder.append(", timeSecondsOnly=");
243 builder.append(getTimeSecondsOnly());
244 builder.append(", specialManoeuvreIndicator=");
245 builder.append(getSpecialManoeuvreIndicator());
246 builder.append(", spare=");
247 builder.append(getSpare());
248 builder.append(", isUsingRAIM=");
249 builder.append(isUsingRAIM());
250 builder.append(", communications=");
251 builder.append(getCommunications());
252 builder.append("]");
253 return builder.toString();
254 }
255
256 }