forked from ktuukkan/marine-api
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for AIS message 27 (ktuukkan#124)
- Loading branch information
Showing
7 changed files
with
474 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
src/main/java/net/sf/marineapi/ais/message/AISMessage27.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package net.sf.marineapi.ais.message; | ||
|
||
/** | ||
* Implementation of https://www.navcen.uscg.gov/?pageName=AISMessage27 | ||
* | ||
* @author Krzysztof Borowski | ||
*/ | ||
public interface AISMessage27 extends AISPositionReport { | ||
|
||
/** | ||
* Returns the RAIM flag. | ||
* | ||
* @return {@code true} if RAIM in use, otherwise {@code false}. | ||
*/ | ||
boolean getRAIMFlag(); | ||
|
||
|
||
/** | ||
* Returns Position Latency. | ||
* | ||
* @return 0 = Reported position latency is less than 5 seconds; 1 = Reported position latency is greater than 5 seconds = default | ||
*/ | ||
int getPositionLatency(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
205 changes: 205 additions & 0 deletions
205
src/main/java/net/sf/marineapi/ais/parser/AisMessage27Parser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
package net.sf.marineapi.ais.parser; | ||
|
||
import net.sf.marineapi.ais.message.AISMessage27; | ||
import net.sf.marineapi.ais.util.*; | ||
|
||
/** | ||
* AIS Message 27 implementation - LONG-RANGE AUTOMATIC IDENTIFCATION SYSTEM BROADCAST MESSAGE | ||
* see: https://www.navcen.uscg.gov/?pageName=AISMessage27 | ||
* <p> | ||
* This message is primarily intended for long-range detection of AIS Class A and Class B “SO” equipped vessels (typically by satellite). | ||
* This message has a similar content to Messages 1, 2 and 3, | ||
* but the total number of bits has been compressed | ||
* to allow for increased propagation delays associated with long-range detection. | ||
* Note there is no time stamp in this message. | ||
* The receiving system is expected to provide the time stamp when this message is received. | ||
* | ||
* <pre> | ||
* Field Name Bits (from, to ) | ||
* ------------------------------------------------------------------------ | ||
* 1 messageID 6 ( 1, 6) - always 27 | ||
* 2 repeatIndicator 2 ( 7, 8) - always 3 | ||
* 3 userID 30 ( 9, 38) | ||
* 4 positionAccuracy 1 ( 39, 39) | ||
* 5 raimFlag 1 ( 40, 40) | ||
* 6 navigationalStatus 4 ( 41, 44) | ||
* 7 longitude 18 ( 45, 62) | ||
* 8 latitude 17 ( 63, 79) | ||
* 9 speedOverGround 6 ( 80, 85) | ||
* 10 courseOverGround 9 ( 86, 94) | ||
* 11 positionLatency 1 ( 95, 95) | ||
* 12 spare 1 ( 96, 96) - always 0 | ||
* ---- + | ||
* sum 96 | ||
* </pre> | ||
* | ||
* @author Krzysztof Borowski | ||
*/ | ||
public class AisMessage27Parser extends AISMessageParser implements AISMessage27 { | ||
|
||
private final static String SEPARATOR = "\n\t"; | ||
private final static int POSITIONACCURACY = 0; | ||
private final static int RAIMFLAG = 1; | ||
private final static int NAVIGATIONALSTATUS = 2; | ||
private final static int LONGITUDE = 3; | ||
private final static int LATITUDE = 4; | ||
private final static int SPEEDOVERGROUND = 5; | ||
private final static int COURSEOVERGROUND = 6; | ||
private final static int POSITIONLATENCY = 7; | ||
private final static int SPARE = 8; | ||
private final static int[] FROM = { | ||
38, 38, 49, 44, 62, 79, 85, 94, 95}; | ||
private final static int[] TO = { | ||
38, 39, 44, 62, 79, 85, 94, 95, 96}; | ||
|
||
|
||
private boolean fPositionAccuracy; | ||
private boolean fRaimFlag; | ||
private int fNavigationalStatus; | ||
private int fLongitude; | ||
private int fLatitude; | ||
private int fSOG; | ||
private int fCOG; | ||
private int fPositionLatency; | ||
|
||
// not available in this Message27 Position Report, filled in with defaults | ||
private int fTrueHeading = 511; | ||
private int fRateOfTurn = -128; | ||
private int fTimeStamp = 60; | ||
private int fManouverIndicator = 0; | ||
|
||
|
||
public AisMessage27Parser(Sixbit content) { | ||
super(content, 96, 96); | ||
fPositionAccuracy = content.getBoolean(TO[POSITIONACCURACY]); | ||
fRaimFlag = content.getBoolean(TO[RAIMFLAG]); | ||
fNavigationalStatus = content.getInt(FROM[NAVIGATIONALSTATUS], TO[NAVIGATIONALSTATUS]); | ||
if (!NavigationalStatus.isCorrect(fNavigationalStatus)) | ||
addViolation(new AISRuleViolation("NavigationalStatus", fNavigationalStatus, NavigationalStatus.RANGE)); | ||
|
||
fLongitude = content.getAs18BitInt(FROM[LONGITUDE], TO[LONGITUDE]); | ||
if (!Longitude18.isCorrect(fLongitude)) | ||
addViolation(new AISRuleViolation("LongitudeInDegrees", fLongitude, Longitude18.RANGE)); | ||
fLatitude = content.getAs17BitInt(FROM[LATITUDE], TO[LATITUDE]); | ||
if (!Latitude17.isCorrect(fLatitude)) | ||
addViolation(new AISRuleViolation("LatitudeInDegrees", fLatitude, Latitude17.RANGE)); | ||
|
||
fSOG = content.getInt(FROM[SPEEDOVERGROUND], TO[SPEEDOVERGROUND]); | ||
fCOG = content.getInt(FROM[COURSEOVERGROUND], TO[COURSEOVERGROUND]); | ||
|
||
if (!Angle9.isCorrect(fCOG)) | ||
addViolation(new AISRuleViolation("CourseOverGround", fCOG, Angle9.RANGE)); | ||
|
||
fPositionLatency = content.getInt(FROM[POSITIONLATENCY], TO[POSITIONLATENCY]); | ||
} | ||
|
||
@Override | ||
public boolean getRAIMFlag() { | ||
return fRaimFlag; | ||
} | ||
|
||
@Override | ||
public int getNavigationalStatus() { | ||
return fNavigationalStatus; | ||
} | ||
|
||
@Override | ||
public double getRateOfTurn() { | ||
return RateOfTurn.toDegreesPerMinute(fRateOfTurn); | ||
} | ||
|
||
@Override | ||
public double getSpeedOverGround() { | ||
return fSOG; | ||
} | ||
|
||
@Override | ||
public boolean isAccurate() { | ||
return fPositionAccuracy; | ||
} | ||
|
||
@Override | ||
public double getLongitudeInDegrees() { | ||
return Longitude18.toDegrees(fLongitude); | ||
} | ||
|
||
@Override | ||
public double getLatitudeInDegrees() { | ||
return Latitude17.toDegrees(fLatitude); | ||
} | ||
|
||
@Override | ||
public double getCourseOverGround() { | ||
return fCOG; | ||
} | ||
|
||
@Override | ||
public int getTrueHeading() { | ||
return fTrueHeading; | ||
} | ||
|
||
@Override | ||
public int getTimeStamp() { | ||
return fTimeStamp; | ||
} | ||
|
||
@Override | ||
public int getManouverIndicator() { | ||
return fManouverIndicator; | ||
} | ||
|
||
@Override | ||
public boolean hasRateOfTurn() { | ||
return RateOfTurn.isTurnIndicatorAvailable(fRateOfTurn); | ||
} | ||
|
||
@Override | ||
public boolean hasSpeedOverGround() { | ||
return SpeedOverGround.isAvailable(fSOG); | ||
} | ||
|
||
@Override | ||
public boolean hasCourseOverGround() { | ||
return Angle12.isAvailable(fCOG); | ||
} | ||
|
||
@Override | ||
public boolean hasTrueHeading() { | ||
return Angle9.isAvailable(fTrueHeading); | ||
} | ||
|
||
@Override | ||
public boolean hasTimeStamp() { | ||
return TimeStamp.isAvailable(fTimeStamp); | ||
} | ||
|
||
@Override | ||
public boolean hasLongitude() { | ||
return Longitude18.isAvailable(fLongitude); | ||
} | ||
|
||
@Override | ||
public boolean hasLatitude() { | ||
return Latitude17.isAvailable(fLatitude); | ||
} | ||
|
||
@Override | ||
public int getPositionLatency() { | ||
return fPositionLatency; | ||
} | ||
|
||
public String toString() { | ||
String result = "\tNav st: " + NavigationalStatus.toString(fNavigationalStatus); | ||
result += SEPARATOR + "ROT: " + RateOfTurn.toString(fRateOfTurn); | ||
result += SEPARATOR + "SOG: " + SpeedOverGround.toString(fSOG); | ||
result += SEPARATOR + "Pos acc: " + (fPositionAccuracy ? "high" : "low") + " accuracy"; | ||
result += SEPARATOR + "Lon: " + Longitude18.toString(fLongitude); | ||
result += SEPARATOR + "Lat: " + Latitude17.toString(fLatitude); | ||
result += SEPARATOR + "COG: " + Angle9.toString(fCOG); | ||
result += SEPARATOR + "Heading: " + Angle9.getTrueHeadingString(fTrueHeading); | ||
result += SEPARATOR + "Time: " + TimeStamp.toString(fTimeStamp); | ||
result += SEPARATOR + "Man ind: " + ManeuverIndicator.toString(fManouverIndicator); | ||
result += SEPARATOR + "Latency: " + (fPositionLatency == 0 ? "<5s" : ">5s"); | ||
return result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
/* | ||
* Latitude17.java | ||
* Copyright (C) 2015 Lázár József | ||
* | ||
* This file is part of Java Marine API. | ||
* <http://ktuukkan.github.io/marine-api/> | ||
* | ||
* Java Marine API is free software: you can redistribute it and/or modify it | ||
* under the terms of the GNU Lesser General Public License as published by the | ||
* Free Software Foundation, either version 3 of the License, or (at your | ||
* option) any later version. | ||
* | ||
* Java Marine API is distributed in the hope that it will be useful, but | ||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License | ||
* for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public License | ||
* along with Java Marine API. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
package net.sf.marineapi.ais.util; | ||
|
||
import java.text.DecimalFormat; | ||
|
||
/** | ||
* Checks a 17-bit signed integer latitude value for validity. | ||
* | ||
*/ | ||
public class Latitude17 { | ||
private static final DecimalFormat COORD_FORMAT = new DecimalFormat("##0.000000;-##0.000000"); | ||
|
||
private static final int MINUTE_PART_MULTIPLIER = 60 * 10; | ||
private static final int MIN_VALUE = -90 * MINUTE_PART_MULTIPLIER; | ||
private static final int MAX_VALUE = 90 * MINUTE_PART_MULTIPLIER; | ||
private static final int DEFAULT_VALUE = 91 * MINUTE_PART_MULTIPLIER; | ||
|
||
/** Valid range with default value for "no value" */ | ||
public static final String RANGE = "[" + MIN_VALUE + "," + MAX_VALUE + "] + {" + DEFAULT_VALUE + "}"; | ||
|
||
/** | ||
* Converts the latitude value (in 1/10000 minutes) to degrees. | ||
* | ||
* @param value Int value to convert | ||
* @return The latitude value in degrees | ||
*/ | ||
public static double toDegrees(int value) { | ||
return (double)value / (double)MINUTE_PART_MULTIPLIER; | ||
} | ||
|
||
/** | ||
* Tells if the given latitude is available, i.e. within expected range. | ||
* | ||
* @param value Latitude value to validate | ||
* @return {@code true} if available, otherwise {@code false}. | ||
*/ | ||
public static boolean isAvailable(int value) { | ||
return value >= MIN_VALUE && value <= MAX_VALUE; | ||
} | ||
|
||
/** | ||
* Tells if the given value is correct, i.e. within expected range and not | ||
* the no-value. | ||
* | ||
* @param value Latitude value to validate | ||
* @return {@code true} if correct, otherwise {@code false}. | ||
*/ | ||
public static boolean isCorrect(int value) { | ||
return isAvailable(value) || (value == DEFAULT_VALUE); | ||
} | ||
|
||
/** | ||
* Returns the String representation of given latitude value. | ||
* @param value Value to stringify | ||
* @return "invalid latitude", "latitude not available" or value formatted | ||
* in degrees. | ||
*/ | ||
public static String toString(int value) { | ||
if (!isCorrect(value)) { | ||
return "invalid latitude"; | ||
} else if (!isAvailable(value)) { | ||
return "latitude not available"; | ||
} else { | ||
return COORD_FORMAT.format(toDegrees(value)); | ||
} | ||
} | ||
} |
Oops, something went wrong.