/* Copyright (c) 2011 Danish Maritime Authority.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dk.dma.ais.message;
import dk.dma.ais.binary.BinArray;
import dk.dma.ais.binary.SixbitEncoder;
import dk.dma.ais.binary.SixbitException;
import dk.dma.ais.sentence.Vdm;
import dk.dma.enav.model.geometry.Position;
/**
* AIS message 19
*
* Extended Class B equipment position report as defined by ITU-R M.1371-4
*
*/
public class AisMessage19 extends AisStaticCommon implements IVesselPositionMessage {
/** serialVersionUID. */
private static final long serialVersionUID = 1L;
/**
* Reserved for definition by a competent regional or local authority. Should be set to zero, if not used for any
* regional or local application. Regional applications should not use zero.
*/
private int spare1; // 8 bits
/**
* Speed over ground in 1/10 knot steps (0-102.2 knots) 1023 = not available, 1022 = 102.2 knots or higher
*/
private int sog; // 10 bits
/**
* AisPosition Accuracy 1 = high ( =< 10 m) 0 = low (>10 m) 0 = default The PA flag should be determined in
* accordance with Table 47
*/
private int posAcc; // 1 bit
/**
* Store the positions just as in message 1-3
*/
private AisPosition pos; // : Lat/Long 1/10000 minute
/**
* Course over ground in 1/10 = (0-3599). 3600 (E10h) = not available = default; 3601-4095 should not be used
*/
private int cog; // 12 bits
/**
* True heading Degrees (0-359) (511 indicates not available = default)
*/
private int trueHeading; // 9 bits
/**
* Time stamp: UTC second when the report was generated by the EPFS (0-59 or 60 if time stamp is not available,
* which should also be the default value or 61 if positioning system is in manual input mode or 62 if electronic
* position fixing system operates in estimated (dead reckoning) mode or 63 if the positioning system is
* inoperative) 61, 62, 63 are not used by CS AIS
*/
private int utcSec; // 6 bits : UTC Seconds
/**
* Not used. Should be set to zero. Reserved for future use
*/
private int spare2; // 4 bits
/**
* Type of electronic position fixing device: 0 = undefined (default) 1 = GPS 2 = GLONASS 3 = combined GPS/GLONASS 4
* = Loran-C 5 = Chayka 6 = integrated navigation system 7 = surveyed 8 = Galileo, 9-14 = not used 15 = internal
* GNSS
*/
private int posType; // 4 bits
/**
* RAIM-flag: RAIM (Receiver autonomous integrity monitoring) flag of electronic position fixing device; 0 = RAIM
* not in use = default; 1 = RAIM in use see Table 47
*/
private int raimFlag; // 1 bit
/**
* DTE: Data terminal equipment (DTE) ready 0 = available 1 = not available = default see 3.3.1
*/
private int dte; // 1 bit : DTE flag
/**
* Mode flag: 0 = Station operating in autonomous and continuous mode = default 1 = Station operating in assigned
* mode
*/
private int modeFlag; // 1 bit
/**
* Not used. Should be set to zero. Reserved for future use
*/
private int spare3; // 4 bits
public AisMessage19() {
super(19);
}
public AisMessage19(Vdm vdm) throws AisMessageException, SixbitException {
super(vdm);
parse(vdm.getBinArray());
}
@Override
protected void parse(BinArray binArray) throws AisMessageException, SixbitException {
BinArray sixbit = vdm.getBinArray();
if (sixbit.getLength() != 312) {
throw new AisMessageException("Message 19 wrong length " + sixbit.getLength());
}
super.parse(sixbit);
this.spare1 = (int) sixbit.getVal(8);
this.sog = (int) sixbit.getVal(10);
this.posAcc = (int) sixbit.getVal(1);
this.pos = new AisPosition();
this.pos.setRawLongitude(sixbit.getVal(28));
this.pos.setRawLatitude(sixbit.getVal(27));
this.cog = (int) sixbit.getVal(12);
this.trueHeading = (int) sixbit.getVal(9);
this.utcSec = (int) sixbit.getVal(6);
this.spare2 = (int) sixbit.getVal(4);
this.name = sixbit.getString(20);
this.shipType = (int) sixbit.getVal(8);
this.dimBow = (int) sixbit.getVal(9);
this.dimStern = (int) sixbit.getVal(9);
this.dimPort = (int) sixbit.getVal(6);
this.dimStarboard = (int) sixbit.getVal(6);
this.posType = (int) sixbit.getVal(4);
this.raimFlag = (int) sixbit.getVal(1);
this.dte = (int) sixbit.getVal(1);
this.modeFlag = (int) sixbit.getVal(1);
this.spare3 = (int) sixbit.getVal(4);
}
@Override
public SixbitEncoder getEncoded() {
SixbitEncoder encoder = super.encode();
encoder.addVal(spare1, 8);
encoder.addVal(sog, 10);
encoder.addVal(posAcc, 1);
encoder.addVal(pos.getRawLongitude(), 28);
encoder.addVal(pos.getRawLatitude(), 27);
encoder.addVal(cog, 12);
encoder.addVal(trueHeading, 9);
encoder.addVal(utcSec, 6);
encoder.addVal(spare2, 4);
encoder.addString(name, 20);
encoder.addVal(shipType, 8);
encoder.addVal(dimBow, 9);
encoder.addVal(dimStern, 9);
encoder.addVal(dimPort, 6);
encoder.addVal(dimStarboard, 6);
encoder.addVal(posType, 4);
encoder.addVal(raimFlag, 1);
encoder.addVal(dte, 1);
encoder.addVal(modeFlag, 1);
encoder.addVal(spare3, 4);
return encoder;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(super.toString());
builder.append(", spare1=");
builder.append(spare1);
builder.append(", sog=");
builder.append(sog);
builder.append(", posAcc=");
builder.append(posAcc);
builder.append(", pos=");
builder.append(pos);
builder.append(", cog=");
builder.append(cog);
builder.append(", trueHeading=");
builder.append(trueHeading);
builder.append(", utcSec=");
builder.append(utcSec);
builder.append(", spare2=");
builder.append(spare2);
builder.append(", name=");
builder.append(name);
builder.append(", shipType=");
builder.append(shipType);
builder.append(", dimBow=");
builder.append(dimBow);
builder.append(", dimStern=");
builder.append(dimStern);
builder.append(", dimPort=");
builder.append(dimPort);
builder.append(", dimStarboard=");
builder.append(dimStarboard);
builder.append(", posType=");
builder.append(posType);
builder.append(", raimFlag=");
builder.append(raimFlag);
builder.append(", dte=");
builder.append(dte);
builder.append(", modeFlag=");
builder.append(modeFlag);
builder.append(", spare3=");
builder.append(spare3);
builder.append("]");
return builder.toString();
}
public int getSpare1() {
return spare1;
}
public void setSpare1(int spare1) {
this.spare1 = spare1;
}
public int getSog() {
return sog;
}
public void setSog(int sog) {
this.sog = sog;
}
public int getPosAcc() {
return posAcc;
}
public void setPosAcc(int posAcc) {
this.posAcc = posAcc;
}
@Override
public Position getValidPosition() {
AisPosition pos = this.pos;
return pos == null ? null : pos.getGeoLocation();
}
public AisPosition getPos() {
return pos;
}
public void setPos(AisPosition pos) {
this.pos = pos;
}
public int getCog() {
return cog;
}
public void setCog(int cog) {
this.cog = cog;
}
public int getTrueHeading() {
return trueHeading;
}
public void setTrueHeading(int trueHeading) {
this.trueHeading = trueHeading;
}
public int getUtcSec() {
return utcSec;
}
public void setUtcSec(int utcSec) {
this.utcSec = utcSec;
}
public int getSpare2() {
return spare2;
}
public void setSpare2(int spare2) {
this.spare2 = spare2;
}
public int getPosType() {
return posType;
}
public void setPosType(int posType) {
this.posType = posType;
}
public int getRaimFlag() {
return raimFlag;
}
public void setRaimFlag(int raimFlag) {
this.raimFlag = raimFlag;
}
public int getDte() {
return dte;
}
public void setDte(int dte) {
this.dte = dte;
}
public int getModeFlag() {
return modeFlag;
}
public void setModeFlag(int modeFlag) {
this.modeFlag = modeFlag;
}
public int getSpare3() {
return spare3;
}
public void setSpare3(int spare3) {
this.spare3 = spare3;
}
@Override
public boolean isPositionValid() {
Position geo = pos.getGeoLocation();
return geo != null;
}
@Override
public boolean isCogValid() {
return cog < 3600;
}
@Override
public boolean isSogValid() {
return sog < 1023;
}
@Override
public boolean isHeadingValid() {
return trueHeading < 360;
}
@Override
public int getRaim() {
return getRaimFlag();
}
}