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