/* 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 18 * * CLASS B position report implemented according to ITU-R M.1371-4 * */ public class AisMessage18 extends AisMessage implements IVesselPositionMessage { /** serialVersionUID. */ private static final long serialVersionUID = 1L; /** Not used. Should be set to zero. Reserved for future use */ private int spareAfterUserId; // 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 spare; // 2 bits /** * Class B unit flag: 0 = Class B SOTDMA unit 1 = Class B CS unit */ private int classBUnitFlag; // 1 bit /** * Class B display flag: 0 = No display available; not capable of displaying Message 12 and 14 1 = Equipped with * integrated display displaying Message 12 and 14 */ private int classBDisplayFlag; // 1 bit /** * Class B DSC flag: 0 = Not equipped with DSC function 1 = Equipped with DSC function (dedicated or time-shared) */ private int classBDscFlag; // 1 bit /** * Class B band flag: 0 = Capable of operating over the upper 525 kHz band of the marine band 1 = Capable of * operating over the whole marine band (irrelevant if Class B Message 22 flag is 0) */ private int classBBandFlag; // 1 bit /** * 0 = No frequency management via Message 22 , operating on AIS1, AIS2 only 1 = Frequency management via Message 22 */ private int classBMsg22Flag; // 1 bit /** * Mode flag: 0 = Station operating in autonomous and continuous mode = default 1 = Station operating in assigned * mode */ private int modeFlag; // 1 bit /** * 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 raim; // 1 bit /** * Communication state selector flag: 0 = SOTDMA communication state follows 1 = ITDMA communication state follows * (always 1 for Class-B CS) */ private int commStateSelectorFlag; // 1 bit /** * Communication state: SOTDMA communication state (see 3.3.7.2.1, Annex 2), if communication state selector flag is * set to 0, or ITDMA communication state (see 3.3.7.3.2, Annex 2), if communication state selector flag is set to 1 * Because Class B CS does not use any Communication State information, this field should be filled with the * following value: 1100000000000000110 */ private int commState; // 19 bits : SOTDMA sync state public AisMessage18() { super(18); } public AisMessage18(Vdm vdm) throws AisMessageException, SixbitException { super(vdm); this.parse(); } public void parse() throws AisMessageException, SixbitException { BinArray sixbit = vdm.getBinArray(); if (sixbit.getLength() != 168) { throw new AisMessageException("Message 18 wrong length " + sixbit.getLength()); } super.parse(sixbit); this.spareAfterUserId = (int) sixbit.getVal(8); this.sog = (int) sixbit.getVal(10); this.posAcc = (int) sixbit.getVal(1); // Extract position 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.spare = (int) sixbit.getVal(2); // Extract class B flags this.classBUnitFlag = (int) sixbit.getVal(1); this.classBDisplayFlag = (int) sixbit.getVal(1); this.classBDscFlag = (int) sixbit.getVal(1); this.classBBandFlag = (int) sixbit.getVal(1); this.classBMsg22Flag = (int) sixbit.getVal(1); this.modeFlag = (int) sixbit.getVal(1); // Raim and communication this.raim = (int) sixbit.getVal(1); this.commStateSelectorFlag = (int) sixbit.getVal(1); this.commState = (int) sixbit.getVal(19); } @Override public SixbitEncoder getEncoded() { SixbitEncoder encoder = super.encode(); encoder.addVal(spareAfterUserId, 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(spare, 2); encoder.addVal(classBUnitFlag, 1); encoder.addVal(classBDisplayFlag, 1); encoder.addVal(classBDscFlag, 1); encoder.addVal(classBBandFlag, 1); encoder.addVal(classBMsg22Flag, 1); encoder.addVal(modeFlag, 1); encoder.addVal(raim, 1); encoder.addVal(commStateSelectorFlag, 1); encoder.addVal(commState, 19); return encoder; } /** * @return the spareAfterUserId */ public int getSpareAfterUserId() { return spareAfterUserId; } /** * @param spareAfterUserId * the spareAfterUserId to set */ public void setSpareAfterUserId(int spareAfterUserId) { this.spareAfterUserId = spareAfterUserId; } /** * @return the sog */ public int getSog() { return sog; } /** * @param sog * the sog to set */ public void setSog(int sog) { this.sog = sog; } /** * @return the posAcc */ public int getPosAcc() { return posAcc; } /** * @param posAcc * the posAcc to set */ public void setPosAcc(int posAcc) { this.posAcc = posAcc; } /** * @return the pos */ public AisPosition getPos() { return pos; } @Override public Position getValidPosition() { AisPosition pos = this.pos; return pos == null ? null : pos.getGeoLocation(); } /** * @param pos * the pos to set */ public void setPos(AisPosition pos) { this.pos = pos; } /** * @return the cog */ public int getCog() { return cog; } /** * @param cog * the cog to set */ public void setCog(int cog) { this.cog = cog; } /** * @return the trueHeading */ public int getTrueHeading() { return trueHeading; } /** * @param trueHeading * the trueHeading to set */ public void setTrueHeading(int trueHeading) { this.trueHeading = trueHeading; } /** * @return the utcSec */ public int getUtcSec() { return utcSec; } /** * @param utcSec * the utcSec to set */ public void setUtcSec(int utcSec) { this.utcSec = utcSec; } /** * @return the spare */ public int getSpare() { return spare; } /** * @param spare * the spare to set */ public void setSpare(int spare) { this.spare = spare; } /** * @return the classBUnitFlag */ public int getClassBUnitFlag() { return classBUnitFlag; } /** * @param classBUnitFlag * the classBUnitFlag to set */ public void setClassBUnitFlag(int classBUnitFlag) { this.classBUnitFlag = classBUnitFlag; } /** * @return the classBDisplayFlag */ public int getClassBDisplayFlag() { return classBDisplayFlag; } /** * @param classBDisplayFlag * the classBDisplayFlag to set */ public void setClassBDisplayFlag(int classBDisplayFlag) { this.classBDisplayFlag = classBDisplayFlag; } /** * @return the classBDscFlag */ public int getClassBDscFlag() { return classBDscFlag; } /** * @param classBDscFlag * the classBDscFlag to set */ public void setClassBDscFlag(int classBDscFlag) { this.classBDscFlag = classBDscFlag; } /** * @return the classBBandFlag */ public int getClassBBandFlag() { return classBBandFlag; } /** * @param classBBandFlag * the classBBandFlag to set */ public void setClassBBandFlag(int classBBandFlag) { this.classBBandFlag = classBBandFlag; } /** * @return the classBMsg22Flag */ public int getClassBMsg22Flag() { return classBMsg22Flag; } /** * @param classBMsg22Flag * the classBMsg22Flag to set */ public void setClassBMsg22Flag(int classBMsg22Flag) { this.classBMsg22Flag = classBMsg22Flag; } /** * @return the modeFlag */ public int getModeFlag() { return modeFlag; } /** * @param modeFlag * the modeFlag to set */ public void setModeFlag(int modeFlag) { this.modeFlag = modeFlag; } /** * @return the raimFlag */ public int getRaim() { return raim; } /** * @param raimFlag * the raimFlag to set */ public void setRaim(int raim) { this.raim = raim; } /** * @return the commStateSelectorFlag */ public int getCommStateSelectorFlag() { return commStateSelectorFlag; } /** * @param commStateSelectorFlag * the commStateSelectorFlag to set */ public void setCommStateSelectorFlag(int commStateSelectorFlag) { this.commStateSelectorFlag = commStateSelectorFlag; } /** * @return the commState */ public int getCommState() { return commState; } /** * @param commState * the commState to set */ public void setCommState(int commState) { this.commState = commState; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(super.toString()); builder.append(", classBBandFlag="); builder.append(classBBandFlag); builder.append(", classBDisplayFlag="); builder.append(classBDisplayFlag); builder.append(", classBDscFlag="); builder.append(classBDscFlag); builder.append(", classBMsg22Flag="); builder.append(classBMsg22Flag); builder.append(", classBUnitFlag="); builder.append(classBUnitFlag); builder.append(", cog="); builder.append(cog); builder.append(", commState="); builder.append(commState); builder.append(", commStateSelectorFlag="); builder.append(commStateSelectorFlag); builder.append(", modeFlag="); builder.append(modeFlag); builder.append(", pos="); builder.append(pos); builder.append(", posAcc="); builder.append(posAcc); builder.append(", raim="); builder.append(raim); builder.append(", sog="); builder.append(sog); builder.append(", spare="); builder.append(spare); builder.append(", spareAfterUserId="); builder.append(spareAfterUserId); builder.append(", trueHeading="); builder.append(trueHeading); builder.append(", utcSec="); builder.append(utcSec); builder.append("]"); return builder.toString(); } public boolean isPositionValid() { return pos.getGeoLocation() != null; } public boolean isCogValid() { return cog < 3600; } public boolean isSogValid() { return sog < 1023; } public boolean isHeadingValid() { return trueHeading < 360; } }