From c428decc084eb214ea982e3f51bd99db57b34838 Mon Sep 17 00:00:00 2001 From: Vasco Figueira Date: Sun, 29 Jan 2012 20:40:02 +0000 Subject: [PATCH] Initial commit of FreeAIS fork for the AIS message decoder --- .gitignore | 4 + LICENSE | 12 + README | 1 + pom.xml | 59 +++ .../java/org/freeais/ais/AISBaseStation.java | 308 ++++++++++++++ src/main/java/org/freeais/ais/AISDecoder.java | 260 ++++++++++++ .../org/freeais/ais/AISParseException.java | 75 ++++ src/main/java/org/freeais/ais/AISParser.java | 265 +++++++++++++ .../java/org/freeais/ais/AISPositionA.java | 360 +++++++++++++++++ .../java/org/freeais/ais/AISPositionB.java | 270 +++++++++++++ .../java/org/freeais/ais/AISPositionExtB.java | 319 +++++++++++++++ src/main/java/org/freeais/ais/AISVessel.java | 375 ++++++++++++++++++ .../java/org/freeais/ais/IAISDecodable.java | 47 +++ .../java/org/freeais/ais/IAISMessage.java | 42 ++ src/main/java/org/freeais/i18n/I18N.java | 66 +++ .../java/org/freeais/i18n/messages.properties | 47 +++ .../freeais/i18n/messages_de_DE.properties | 37 ++ src/main/resources/logging.properties | 8 + 18 files changed, 2555 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README create mode 100644 pom.xml create mode 100644 src/main/java/org/freeais/ais/AISBaseStation.java create mode 100644 src/main/java/org/freeais/ais/AISDecoder.java create mode 100644 src/main/java/org/freeais/ais/AISParseException.java create mode 100644 src/main/java/org/freeais/ais/AISParser.java create mode 100644 src/main/java/org/freeais/ais/AISPositionA.java create mode 100644 src/main/java/org/freeais/ais/AISPositionB.java create mode 100644 src/main/java/org/freeais/ais/AISPositionExtB.java create mode 100644 src/main/java/org/freeais/ais/AISVessel.java create mode 100644 src/main/java/org/freeais/ais/IAISDecodable.java create mode 100644 src/main/java/org/freeais/ais/IAISMessage.java create mode 100644 src/main/java/org/freeais/i18n/I18N.java create mode 100644 src/main/java/org/freeais/i18n/messages.properties create mode 100644 src/main/java/org/freeais/i18n/messages_de_DE.properties create mode 100644 src/main/resources/logging.properties diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8751506 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.project +.classpath +target/ +.settings/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2906c1f --- /dev/null +++ b/LICENSE @@ -0,0 +1,12 @@ +Freeais.org is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +Freeais.org 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . diff --git a/README b/README new file mode 100644 index 0000000..b1a509e --- /dev/null +++ b/README @@ -0,0 +1 @@ +The the wiki: https://github.com/vlfig/somewhere diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0b3525b --- /dev/null +++ b/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + org.freeais + ais-decoder + jar + 0.9.5 + AIS Decoder + AIS Messages decoding library from FreeAIS system. + + 1.6 + 1.6 + UTF-8 + + + + GNU GPL + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + java.util.logging.config.file + logging.properties + + + + + + + + + junit + junit + 4.8.1 + test + + + org.jmock + jmock-junit4 + 2.5.1 + test + + + apache-log4j + log4j + 1.2.14 + + + + scm:git:https://git@github.com:vlfig/ais-decoder.git + + diff --git a/src/main/java/org/freeais/ais/AISBaseStation.java b/src/main/java/org/freeais/ais/AISBaseStation.java new file mode 100644 index 0000000..daee2dc --- /dev/null +++ b/src/main/java/org/freeais/ais/AISBaseStation.java @@ -0,0 +1,308 @@ +/****************************************************************************** + * Freeais.org + * http://www.freeais.org info@freeais.org + * + * Copyright (c) 2007 + * + * ynnor systems GmbH + * Mundsburger Damm 45 + * 22087 Hamburg + * Germany + * + * Alexander Lotter lotter@ynnor.de + * David Schmitz schmitz@ynnor.de + * + * This file is part of Freeais.org. + * + * Freeais.org is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Freeais.org 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + ******************************************************************************/ + +package org.freeais.ais; + +import java.sql.Timestamp; +import java.util.Calendar; + +import org.apache.log4j.Logger; + +/** + * This class represents an AIS base station report + * + * @author David Schmitz + * @author Alexander Lotter + * + */ +public class AISBaseStation implements IAISMessage, IAISDecodable, Cloneable { + + private static Logger logger = Logger.getLogger(AISDecoder.class); + + private int msgId; + + private int repeatIndicator; + + private int mmsi; + + /** message timestamp in UTC */ + private Timestamp msgTimestamp; + + /** longitude in degrees +-180° east=positive west=negative */ + private double longitude; + + /** latitude in degrees +-90° north=positive south=negative */ + private double latitude; + + private int deviceType; + + public AISBaseStation() { + + } + + /** + * This is the default constructor of class AISBaseStationReport. + * + * @param msgId + * @param repeatIndicator + * @param mmsi + * @param msgTimestamp + * @param longitude + * @param latitude + * @param deviceType + */ + public AISBaseStation(int msgId, int repeatIndicator, int mmsi, + Timestamp msgTimestamp, double longitude, double latitude, + int deviceType) { + this.msgId = msgId; + this.repeatIndicator = repeatIndicator; + this.mmsi = mmsi; + this.msgTimestamp = msgTimestamp; + this.longitude = longitude; + this.latitude = latitude; + this.deviceType = deviceType; + } + + /** + * Simple getter for MSG ID + * + * @return msgid + */ + public int getMsgId() { + return msgId; + } + + /** + * Simple getter for repeat indicator + * + * @return repeatIndicator + */ + public int getRepeatIndicator() { + return repeatIndicator; + } + + public int getMmsi() { + return mmsi; + } + + public Timestamp getTimestamp() { + return msgTimestamp; + } + + /** + * Simple getter for latitude. + * + * @return latitude + */ + public double getLatitude() { + return latitude; + } + + /** + * Simple getter for longitude. + * + * @return longitude + */ + public double getLongitude() { + return longitude; + } + + /** + * Simple getter for device type. + * + * @return deviceType + */ + public int getDeviceType() { + return deviceType; + } + + @Override + public String toString() { + StringBuffer sBuffer = new StringBuffer(); + sBuffer.append("AISBaseStation\n"); + sBuffer.append("MSGID\t\t" + this.msgId + "\n"); + sBuffer.append("REPEAT IND\t" + this.repeatIndicator + "\n"); + sBuffer.append("MMSI\t\t" + this.mmsi + "\n"); + sBuffer.append("UTC TIMESTAMP\t" + this.msgTimestamp + "\n"); + sBuffer.append("LONGITUDE\t" + this.longitude + "\n"); + sBuffer.append("LATITUDE\t" + this.latitude + "\n"); + sBuffer.append("DEVICE\t\t" + this.deviceType + "\n"); + return sBuffer.toString(); + } + + @Override + public boolean equals(Object o) { + if (o == null) + return false; + if (o == this) + return true; + if (!o.getClass().equals(o.getClass())) + return false; + + AISBaseStation that = (AISBaseStation) o; + boolean same = this.msgId == that.msgId + && this.repeatIndicator == that.repeatIndicator + && this.mmsi == that.mmsi + && this.msgTimestamp.equals(that.msgTimestamp) + && this.latitude == that.latitude + && this.longitude == that.longitude + && this.deviceType == that.deviceType; + return same; + } + + @Override + public AISBaseStation clone() { + try { + return (AISBaseStation) super.clone(); + } + + catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError(); + } + } + + /** + * Parse decoded bytes for the base station data + * + * Message 4, base station report, ITU-R M.1371-1 + * + * @param decBytes + * @throws AISParseException + */ + public IAISMessage decode(String decBytes) throws AISParseException { + + if (decBytes.length() < 168) + throw new AISParseException( + AISParseException.NOT_CONSISTENT_DECODED_STRING); + + /* Base Station Report message ID, bits 0-5 */ + this.msgId = AISDecoder.getDecValueByBinStr(decBytes.substring(0, 6), + false); + logger.debug("messageId = " + msgId); + + /* repeat indicator, bits 6-7 */ + this.repeatIndicator = AISDecoder.getDecValueByBinStr( + decBytes.substring(6, 8), false); + logger.debug("repeat ind = " + repeatIndicator); + + /* mmsi, bits 8-37 */ + this.mmsi = AISDecoder.getDecValueByBinStr(decBytes.substring(8, 38), + false); + logger.debug("mmsi = " + mmsi); + + /* year, bits 38-51 */ + int year = AISDecoder.getDecValueByBinStr(decBytes.substring(38, 52), + false); + logger.debug("year = " + year); + + /* month, bits 52-55 */ + int month = AISDecoder.getDecValueByBinStr(decBytes.substring(52, 56), + false); + logger.debug("month = " + month); + + /* day, bits 56-60 */ + int day = AISDecoder.getDecValueByBinStr(decBytes.substring(56, 61), + false); + logger.debug("day = " + day); + + /* hour, bits 61-65 */ + int hour = AISDecoder.getDecValueByBinStr(decBytes.substring(61, 66), + false); + logger.debug("hour = " + hour); + + /* minute, bits 66-71 */ + int minute = AISDecoder.getDecValueByBinStr(decBytes.substring(66, 72), + false); + logger.debug("minute = " + minute); + + /* second, bits 72-77 */ + int second = AISDecoder.getDecValueByBinStr(decBytes.substring(72, 78), + false); + logger.debug("second = " + second); + + Calendar cal = Calendar.getInstance();// TimeZone.getTimeZone("UTC")); + cal.clear(); + cal.set(year, month - 1, day, hour, minute, second); + this.msgTimestamp = new Timestamp(cal.getTimeInMillis()); + logger.debug("UTC timestamp = " + msgTimestamp); + + // bit 78 - position accuracy won't be read. + + /* longitude, bits 79-106 */ + int longitudeHour = AISDecoder.getDecValueByBinStr( + decBytes.substring(79, 107), true); + logger.debug("longitudeHour = " + longitudeHour); + + this.longitude = longitudeHour / 600000.0; + logger.debug("longitude = " + longitude); + + if (this.longitude > 180.0 || this.longitude < -180.0) + throw new AISParseException( + AISParseException.LONGITUDE_OUT_OF_RANGE + " " + longitude); + + /* latitude, bits 107-133 */ + int latitudeHour = AISDecoder.getDecValueByBinStr( + decBytes.substring(107, 134), true); + this.latitude = latitudeHour / 600000.0; + logger.debug("latitude = " + latitude); + + if (this.latitude > 90.0 || this.latitude < -90.0) + throw new AISParseException(AISParseException.LATITUDE_OUT_OF_RANGE + + " " + latitude); + + /* device type, bits 134-137 */ + this.deviceType = AISDecoder.getDecValueByBinStr( + decBytes.substring(134, 138), false); + logger.debug("deviceType = " + deviceType); + + // TODO: implement the rest bits + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(latitude); + result = prime * result + (int) (temp ^ temp >>> 32); + temp = Double.doubleToLongBits(longitude); + result = prime * result + (int) (temp ^ temp >>> 32); + result = prime * result + mmsi; + result = prime * result + msgId; + result = prime * result + repeatIndicator; + result = prime * result + + (msgTimestamp == null ? 0 : msgTimestamp.hashCode()); + return result; + } + +} diff --git a/src/main/java/org/freeais/ais/AISDecoder.java b/src/main/java/org/freeais/ais/AISDecoder.java new file mode 100644 index 0000000..136b710 --- /dev/null +++ b/src/main/java/org/freeais/ais/AISDecoder.java @@ -0,0 +1,260 @@ +/****************************************************************************** + * Freeais.org + * http://www.freeais.org info@freeais.org + * + * Copyright (c) 2007 + * + * ynnor systems GmbH + * Mundsburger Damm 45 + * 22087 Hamburg + * Germany + * + * Alexander Lotter lotter@ynnor.de + * David Schmitz schmitz@ynnor.de + * + * This file is part of Freeais.org. + * + * Freeais.org is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Freeais.org 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + ******************************************************************************/ + +package org.freeais.ais; + +import java.util.Arrays; + +import org.apache.log4j.Logger; + +/** + * Implementation of the AISDecoder in accordance with
+ * IMO Recommendation ITU-R.M.1371-1
+ * and INTERNATIONAL STANDARD IEC 61993-2:
+ * "Maritime navigation and radio communication equipment and systems – + * Automatic identification systems (AIS) – Part 2: Class A shipborne equipment + * of the universal automatic identification system (AIS) – Operational and + * performance requirements, methods of test and required test results." + * + * @author Alexander Lotter + * @author David Schmitz + */ +public class AISDecoder { + + private static Logger logger = Logger.getLogger(AISDecoder.class); + + /** + * Encoded message will be decoded and packed into corresponding object + * + * @param encodedMsg + * @return Corresponding IAISMessage implementation + * @throws AISParseException + */ + public static IAISMessage decode(String encodedMsg) + throws AISParseException { + logger.info("decode(encodedMsg) - Entry"); + + if (encodedMsg == null || encodedMsg.isEmpty()) { + throw new AISParseException(AISParseException.EMPTY_AIS_MESSAGE); + } + byte[] toDecBytes = encodedMsg.getBytes(); + byte[] decBytes = ascii8To6bitBin(toDecBytes); + + int msgId = decBytes[0]; + + String decodedBinString = getDecodedStr(decBytes); + + switch (msgId) { + case 1: + case 2: + case 3: + logger.debug("decode(encodedMsg) - AISPositionA"); + return new AISPositionA().decode(decodedBinString); + case 4: + case 11: + logger.debug("decode(encodedMsg) - AISBaseStation"); + return new AISBaseStation().decode(decodedBinString); + case 5: + logger.debug("decode(encodedMsg) - AISVessel"); + return new AISVessel().decode(decodedBinString); + case 18: + logger.debug("decode(encodedMsg) - AISPositionB"); + return new AISPositionB().decode(decodedBinString); + case 19: + logger.debug("decode(encodedMsg) - AISPositionExtB"); + return new AISPositionExtB().decode(decodedBinString); + } + + logger.info("decode(encodedMsg) - Exit"); + + throw new AISParseException(AISParseException.WRONG_MESSAGE_ID + " " + + msgId); + } + + /** + * Method to convert ASCII-coded character to 6-bit binary + * + * @param toDecBytes + * @return decodedBytes + */ + private static byte[] ascii8To6bitBin(byte[] toDecBytes) + throws AISParseException { + logger.info("ascii8To6bitBin(toDecBytes) - Entry"); + + byte[] convertedBytes = new byte[toDecBytes.length]; + int sum = 0; + int _6bitBin = 0; + + for (int i = 0; i < toDecBytes.length; i++) { + sum = 0; + _6bitBin = 0; + + if (toDecBytes[i] < 48) { + throw new AISParseException(AISParseException.INVALID_CHARACTER + + " " + (char) toDecBytes[i]); + } + if (toDecBytes[i] > 119) { + throw new AISParseException(AISParseException.INVALID_CHARACTER + + " " + (char) toDecBytes[i]); + } + if (toDecBytes[i] > 87) { + if (toDecBytes[i] < 96) { + throw new AISParseException( + AISParseException.INVALID_CHARACTER + " " + + (char) toDecBytes[i]); + } + sum = toDecBytes[i] + 40; + } else { + sum = toDecBytes[i] + 40; + } + if (sum != 0) { + if (sum > 128) { + sum += 32; + } else { + sum += 40; + } + _6bitBin = sum & 0x3F; + convertedBytes[i] = (byte) _6bitBin; + } + } + + logger.info("ascii8To6bitBin(toDecBytes) - Exit"); + return convertedBytes; + } + + /** + * Set a String of decoded bytes + * + * @param decBytes + * @throws AISParseException + */ + private static String getDecodedStr(byte[] decBytes) { + logger.info("getDecodedStr(decBytes) - Entry"); + + String decStr = ""; + for (int i = 0; i < decBytes.length; i++) { + + int decByte = decBytes[i]; + String bitStr = Integer.toBinaryString(decByte); + + if (bitStr.length() < 6) { + + String zerosToAdd = ""; + + for (int j = bitStr.length() - 1; j < 5; j++) { + zerosToAdd += "0"; + } + bitStr = zerosToAdd + bitStr; + } + for (int j = 0; j < 6; j++) { + decStr += bitStr.charAt(j); + } + } + + logger.info("getDecodedStr(decBytes) - Exit"); + return decStr; + } + + /** + * Convert one 6 bit ASCII character to 8 bit ASCII character + * + * @param byteToDec + * @return + * @throws AISParseException + */ + private static byte convert6BitCharToStandartdAscii(byte byteToDec) { + logger.info("convert6BitCharToStandartdAscii(byteToDec) - Entry"); + + byte decByte = 0; + if (byteToDec < 32) { + decByte = (byte) (byteToDec + 64); + } else if (byteToDec < 63) { + decByte = byteToDec; + } + + logger.info("convert6BitCharToStandartdAscii(byteToDec) - Exit"); + return decByte; + } + + /** + * Get a value for specified bits from the binary string. + * + * @param fromBit + * @param toBit + * @return + * @throws AISParseException + */ + protected static int getDecValueByBinStr(String decStr, boolean signum) { + logger.info("getDecValueByBinStr(decStr) - Entry"); + + Integer decValue = Integer.parseInt(decStr, 2); + if (signum && decStr.charAt(0) == '1') { + char[] invert = new char[decStr.length()]; + Arrays.fill(invert, '1'); + decValue ^= Integer.parseInt(new String(invert), 2); + decValue += 1; + decValue = -decValue; + } + + logger.info("getDecValueByBinStr(decStr) - Exit"); + return decValue; + } + + /** + * Decode 6 bit String to standard ASCII String + * + * Input is a binary string of 0 and 1 each 6 bit is a character that will + * be converted to the standard ASCII character + * + * @param str + * @return + * @throws AISParseException + */ + protected static String getDecStringFrom6BitStr(String str) { + logger.info("getDecStringFrom6BitStr(str) - Entry"); + + String txt = ""; + for (int i = 0; i < str.length(); i = i + 6) { + byte _byte = (byte) Integer.parseInt(str.substring(i, i + 6), 2); + _byte = convert6BitCharToStandartdAscii(_byte); + char convChar = (char) _byte; + + if (convChar == '@') { + break; + } + txt += (char) _byte; + } + txt = txt.trim(); + + logger.info("getDecStringFrom6BitStr(str) - Exit"); + return txt; + } +} diff --git a/src/main/java/org/freeais/ais/AISParseException.java b/src/main/java/org/freeais/ais/AISParseException.java new file mode 100644 index 0000000..2f17bc7 --- /dev/null +++ b/src/main/java/org/freeais/ais/AISParseException.java @@ -0,0 +1,75 @@ +/****************************************************************************** + * Freeais.org + * http://www.freeais.org info@freeais.org + * + * Copyright (c) 2007 + * + * ynnor systems GmbH + * Mundsburger Damm 45 + * 22087 Hamburg + * Germany + * + * Alexander Lotter lotter@ynnor.de + * David Schmitz schmitz@ynnor.de + * + * This file is part of Freeais.org. + * + * Freeais.org is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Freeais.org 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + ******************************************************************************/ + +package org.freeais.ais; + +import org.freeais.i18n.I18N; + +public class AISParseException extends Exception { + + private static final long serialVersionUID = -6332039076147587942L; + + public static final String INVALID_CHARACTER = I18N + .getString("AISParseException.INVALID_CHARATER"); //$NON-NLS-1$ + + public static final String NO_BYTES_TO_PARSE = I18N + .getString("AISParseException.NO_BYTES_TO_PARSE"); //$NON-NLS-1$ + + public static final String WRONG_MESSAGE_ID = I18N + .getString("AISParseException.WRONG_MESSAGE_ID"); //$NON-NLS-1$ + + public static final String NO_AIS_MESSAGE = I18N.getString("AISParseException.NO_AIS_MESSAGE"); //$NON-NLS-1$ + + public static final String EMPTY_AIS_MESSAGE = I18N + .getString("AISParseException.EMPTY_AIS_MESSAGE"); //$NON-NLS-1$ + + public static final String BITSET_OUT_OF_RANGE = I18N + .getString("AISParseException.BITSET_OUT_OF_RANGE"); //$NON-NLS-1$ + + public static final String NO_DECODED_BYTES = I18N + .getString("AISParseException.NO_DECODED_BYTES"); //$NON-NLS-1$ + + public static final String NOT_CONSISTENT_DECODED_STRING = I18N + .getString("AISParseException.NOT_CONSISTENT_DECODED_STRING"); //$NON-NLS-1$ + + public static final String LONGITUDE_OUT_OF_RANGE = I18N + .getString("AISParseException.LONGITUDE_OUT_OF_RANGE"); //$NON-NLS-1$ + + public static final String LATITUDE_OUT_OF_RANGE = I18N + .getString("AISParseException.LATITUDE_OUT_OF_RANGE"); //$NON-NLS-1$ + + public AISParseException() { + } + + public AISParseException(String errorMsg) { + super(errorMsg); + } +} diff --git a/src/main/java/org/freeais/ais/AISParser.java b/src/main/java/org/freeais/ais/AISParser.java new file mode 100644 index 0000000..5437905 --- /dev/null +++ b/src/main/java/org/freeais/ais/AISParser.java @@ -0,0 +1,265 @@ +/****************************************************************************** + * Freeais.org + * http://www.freeais.org info@freeais.org + * + * Copyright (c) 2007 + * + * ynnor systems GmbH + * Mundsburger Damm 45 + * 22087 Hamburg + * Germany + * + * Alexander Lotter lotter@ynnor.de + * David Schmitz schmitz@ynnor.de + * + * This file is part of Freeais.org. + * + * Freeais.org is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Freeais.org 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + ******************************************************************************/ + +package org.freeais.ais; + +import java.util.regex.Pattern; + +import org.apache.log4j.Logger; + +/** + * This class parses an AIS message.
+ * AIS-Message format:
+ * !AIVDM,[1],[2],[3],[4],[5]*[6]
+ * 1 - Total number of sentences needed to
+ * transfer the message (1-9)
+ * 2 - Sentence number (1-9)
+ * 3 - Sequential Message identifier (Channel A-B)
+ * 4 - Decoded AIS-Message
+ * 5 - Number of fill-bits (0-5)
+ * 6 - Check sum Compare IEC - 61993-2 Standard
+ * + * @author David Schmitz + * @author Alexander Lotter + * + */ +public class AISParser { + + private static Logger logger = Logger.getLogger(AISParser.class); + + /** + * previous total number of sentences needed to transfer the message, in + * case the message was sent in multiparts + */ + private int oldTotalNumOfMsgs = 0; + + /** + * current total number of sentences needed to transfer the message, in case + * the message was sent in multiparts + */ + private int currTotalNumOfMsgs = 0; + + /** + * previous number of sentences, in case the message was sent in multiparts + */ + private int oldSentenceNumber = 0; + + /** + * current number of sentences, in case the message was sent in multiparts + */ + private int currSentenceNumber = 0; + + /** + * previous sequence number identifier, in case the message was sent in + * multiparts + */ + private int oldSequenceNumber = 0; + + /** + * current sequence number identifier, in case the message was sent in + * multiparts + */ + private int currSequenceNumber = 0; + + /** + * current decoded message + */ + private String currMsg = ""; + + private boolean isWholeMsg = false; + + /** Regular expression for AIS-Messages */ + private static final Pattern pattern = Pattern + .compile("!AIVDM\\,[1-9]{1}\\,[1-9]{1}\\,([0-9]{0,1})\\,[0-3A-B]{1}\\,([0-9\\:\\;\\<\\=\\>\\?\\@A-W\\`a-w]+)\\,[0-5]\\*[A-F0-9]{2}"); + + /** + * Method for parsing encoded AIS-Messages. All messages are passed to the + * parser in a raw format, will be validated and decoded. + * + * @param encodedMsg + * @return decoded object of a type IAISMessage + * @throws AISParseException + */ + public IAISMessage parse(String encodedMsg) throws AISParseException { + logger.info("parse(encodedMsg) - Entry"); + + if (isValidAIS(encodedMsg)) { + + String[] msgTokens = new String[6]; + int tokenCnt = 0; + int index = 5; + while ((index = encodedMsg.indexOf(",", index)) != -1) { + + int currIndex = encodedMsg.indexOf(",", index + 1); + if (currIndex != -1) { + msgTokens[tokenCnt] = encodedMsg.substring(index + 1, + currIndex); + } else { + msgTokens[tokenCnt] = encodedMsg.substring(index + 1, + encodedMsg.length()); + } + tokenCnt++; + index++; + } + isWholeMsg = false; + if (msgTokens[0].equals("1")) { + currMsg = msgTokens[4]; + isWholeMsg = true; + } else { + currTotalNumOfMsgs = Integer.valueOf(msgTokens[0]).intValue(); + currSentenceNumber = Integer.valueOf(msgTokens[1]).intValue(); + currSequenceNumber = Integer.valueOf(msgTokens[2]).intValue(); + + if (currSentenceNumber == 1) { + oldTotalNumOfMsgs = currTotalNumOfMsgs; + oldSentenceNumber = currSentenceNumber; + oldSequenceNumber = currSequenceNumber; + currMsg = msgTokens[4]; + } else { + if (currTotalNumOfMsgs > oldTotalNumOfMsgs + || currSentenceNumber != oldSentenceNumber + 1 + || currSequenceNumber != oldSequenceNumber) { + initMsgParams(); + return null; + } + currMsg += msgTokens[4]; + oldSentenceNumber = currSentenceNumber; + if (currSentenceNumber == oldTotalNumOfMsgs) { + isWholeMsg = true; + } + } + } + if (isWholeMsg) { + initMsgParams(); + IAISMessage aisMessage = AISDecoder.decode(currMsg); + return aisMessage; + } + return null; + } + throw new AISParseException(AISParseException.NO_AIS_MESSAGE); + + } + + /** + * Prepare parser for a new raw AIS-Message to parse. + */ + private void initMsgParams() { + logger.info("initMsgParams() - Entry"); + + oldTotalNumOfMsgs = 0; + oldSentenceNumber = 0; + oldSequenceNumber = 0; + + logger.info("initMsgParams() - Exit"); + } + + /** + * Validation of an AIS-Message + * + * @param ais encoded AIS-Message + * @return true if message is valid false if message is invalid + */ + public static boolean isValidAIS(String ais) { + logger.info("isValidAIS(ais) - Entry"); + boolean isValid; + int index = ais.indexOf("!AIVDM"); + String msg = ais; + if (index != -1) { + msg = ais.substring(index, ais.length()); + } + if (!validCRC(msg)) { + isValid = false; + } else { + isValid = pattern.matcher(msg).matches(); + } + logger.info("isValidAIS(ais) - Exit"); + return isValid; + } + + /** + * This method calculates the checksum for a plain AIS string AIVDM,,,,,,0 + * or fullfletched string !AIVDM,,,,,,0*11 + */ + public static String calcCRC(String ais) { + logger.info("calcCRC(ais) - Entry"); + + byte[] data = null; + if (ais.contains("!") && ais.contains("*")) { + data = ais.substring(1, ais.indexOf("*")).getBytes(); + } else { + data = ais.getBytes(); + } + int crc = 0; + for (byte pos : data) { + if (crc == 0) { + crc = pos; + } else { + crc ^= pos; + } + } + String result = String.format("%1$02X", crc); + + logger.info("calcCRC(ais) - Exit"); + return result; + } + + /** + * This method extracts the checksum of a AIS string and returns it in hex + * notation. + * + * @param ais AIS string to extract the checksum from + * @return checksum + */ + public static String extractCRC(String ais) { + logger.info("extractCRC(ais) - Entry"); + + String crc = ais.substring(ais.indexOf('*') + 1); + + logger.info("extractCRC(ais) - Exit"); + return crc; + } + + /** + * Proof whether the checksum of an AIS string is valid. + * + * @param ais AIS message to check for correct CRC. + * @return true if CRC checksum is correct + */ + public static boolean validCRC(String ais) { + logger.info("validCRC(ais) - Entry"); + + boolean result = extractCRC(ais).equals(calcCRC(ais)); + + logger.info("validCRC(ais) - Exit"); + return result; + } + +} diff --git a/src/main/java/org/freeais/ais/AISPositionA.java b/src/main/java/org/freeais/ais/AISPositionA.java new file mode 100644 index 0000000..9aa976a --- /dev/null +++ b/src/main/java/org/freeais/ais/AISPositionA.java @@ -0,0 +1,360 @@ +/****************************************************************************** + * Freeais.org + * http://www.freeais.org info@freeais.org + * + * Copyright (c) 2007 + * + * ynnor systems GmbH + * Mundsburger Damm 45 + * 22087 Hamburg + * Germany + * + * Alexander Lotter lotter@ynnor.de + * David Schmitz schmitz@ynnor.de + * + * This file is part of Freeais.org. + * + * Freeais.org is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Freeais.org 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + ******************************************************************************/ + +package org.freeais.ais; + +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.TimeZone; + +import org.apache.log4j.Logger; + +/** + * This class represents an AIS position report + * + * @author David Schmitz + * @author Alexander Lotter + * + */ +public class AISPositionA implements IAISMessage, IAISDecodable, Cloneable { + + private static Logger logger = Logger.getLogger(AISDecoder.class); + + private int msgId; + + private int repeatIndicator; + + private int mmsi; + + /** navigation status */ + private int navState; + + /** rate of turn +-127 */ + private double rot; + + /** + * speed over ground in knots range 0-102.2 where 102.2knots denotes + * 102.2knots and more + */ + private double sog; + + /** longitude in degrees +-180° east=positive west=negative */ + private double longitude; + + /** latitude in degrees +-90° north=positive south=negative */ + private double latitude; + + /** course over ground in knots */ + private double cog; + + /** degrees 0-359 or 511 for n/a */ + private int trueHeading; + + /** message timestamp in UTC */ + private Timestamp msgTimestamp; + + public AISPositionA() { + + } + + /** + * This is the default constructor of class AISPosition. + * + * @param msgId + * @param repeatIndicator + * @param mmsi + * @param navState navigation state + * @param rot rate of turn + * @param sog speed over ground + * @param longitude + * @param latitude + * @param cog course over ground + * @param trueHeading + * @param msgTimestamp + */ + public AISPositionA(int msgId, int repeatIndicator, int mmsi, int navState, double rot, + double sog, double longitude, double latitude, double cog, + int trueHeading, Timestamp msgTimestamp) { + this.msgId = msgId; + this.repeatIndicator = repeatIndicator; + this.mmsi = mmsi; + this.navState = navState; + this.rot = rot; + this.sog = sog; + this.longitude = longitude; + this.latitude = latitude; + this.cog = cog; + this.trueHeading = trueHeading; + this.msgTimestamp = msgTimestamp; + } + + /** + * Simple getter for course over ground COG. + * @return COG + */ + public double getCog() { + return cog; + } + + /** + * Simple getter for MSG ID + * @return msgid + */ + public int getMsgId() { + return msgId; + } + + /** + * Simple getter for repeat indicator + * @return repeatIndicator + */ + public int getRepeatIndicator() { + return repeatIndicator; + } + + /** + * Simple getter for latitude. + * @return latitude + */ + public double getLatitude() { + return latitude; + } + + /** + * Simple getter for longitude. + * @return longitude + */ + public double getLongitude() { + return longitude; + } + + public Timestamp getMsgTimestamp() { + return msgTimestamp; + } + + /** + * Simple getter for navigation state. + * @return navState + */ + public int getNavState() { + return navState; + } + + /** + * Simple getter for rate of turn. + * @return ROT + */ + public double getRot() { + return rot; + } + + /** + * Simple getter for speed over ground. + * @return SOG + */ + public double getSog() { + return sog; + } + + public int getTrueHeading() { + return trueHeading; + } + + public int getMmsi() { + return mmsi; + } + + @Override + public String toString(){ + StringBuffer sBuffer = new StringBuffer(); + sBuffer.append("AISPositionA\n"); + sBuffer.append("MSGID\t\t" + this.msgId + "\n"); + sBuffer.append("REPEAT IND\t" + this.repeatIndicator + "\n"); + sBuffer.append("MMSI\t\t" + this.mmsi + "\n"); + sBuffer.append("NAVSTATE\t" + this.navState + "\n"); + sBuffer.append("ROT\t\t" + this.rot + "\n"); + sBuffer.append("LONGITUDE\t" + this.longitude + "\n"); + sBuffer.append("LATITUDE\t" + this.latitude + "\n"); + sBuffer.append("COG\t\t" + this.cog + "\n"); + sBuffer.append("HEADING\t\t" + this.trueHeading + "\n"); + sBuffer.append("MSGTIMESTAMP\t" + this.msgTimestamp); + return sBuffer.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(cog); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(latitude); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(longitude); + result = prime * result + (int) (temp ^ (temp >>> 32)); + result = prime * result + mmsi; + result = prime * result + msgId; + result = prime * result + ((msgTimestamp == null) ? 0 : msgTimestamp.hashCode()); + result = prime * result + navState; + result = prime * result + repeatIndicator; + temp = Double.doubleToLongBits(rot); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(sog); + result = prime * result + (int) (temp ^ (temp >>> 32)); + result = prime * result + trueHeading; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final AISPositionA other = (AISPositionA) obj; + if (Double.doubleToLongBits(cog) != Double.doubleToLongBits(other.cog)) + return false; + if (Double.doubleToLongBits(latitude) != Double.doubleToLongBits(other.latitude)) + return false; + if (Double.doubleToLongBits(longitude) != Double.doubleToLongBits(other.longitude)) + return false; + if (mmsi != other.mmsi) + return false; + if (msgId != other.msgId) + return false; + if (msgTimestamp == null) { + if (other.msgTimestamp != null) + return false; + } else if (!msgTimestamp.equals(other.msgTimestamp)) + return false; + if (navState != other.navState) + return false; + if (repeatIndicator != other.repeatIndicator) + return false; + if (Double.doubleToLongBits(rot) != Double.doubleToLongBits(other.rot)) + return false; + if (Double.doubleToLongBits(sog) != Double.doubleToLongBits(other.sog)) + return false; + if (trueHeading != other.trueHeading) + return false; + return true; + } + + @Override + public AISPositionA clone() { + try { + return (AISPositionA) super.clone(); + } + + catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError(); + } + } + + /** + * Parse decoded bytes for the position data + * + * Message 1,2,3 position reports ITU-R M.1371-1 + * + * @param decBytes + * @throws AISParseException + */ + public IAISMessage decode(String decBytes) throws AISParseException { + + if (decBytes.length() < 137) { + throw new AISParseException( + AISParseException.NOT_CONSISTENT_DECODED_STRING); + } + /* Possition Reports Message ID 1,2,3 bits 0-5 */ + this.msgId = AISDecoder.getDecValueByBinStr(decBytes.substring(0, 6), false); + logger.debug("messageId = " + msgId); + + /* repeat indicator, bits 6-7 */ + this.repeatIndicator = AISDecoder.getDecValueByBinStr(decBytes.substring(6, 8), false); + + /* user id mmsi bits 8-37 */ + this.mmsi = AISDecoder.getDecValueByBinStr(decBytes.substring(8, 38), false); + logger.debug("mmsi = " + mmsi); + + /* navigational status bits 38-41 - 4 bits */ + this.navState = AISDecoder.getDecValueByBinStr(decBytes.substring(38, 42), false); + logger.debug("navState = " + navState); + + /* rate of turn bits 42-49 - 8 bits */ + this.rot = AISDecoder.getDecValueByBinStr(decBytes.substring(42, 50), true); + logger.debug("rot = " + rot); + + /* speed over ground bits 50-59 - 10 bits */ + this.sog = (AISDecoder.getDecValueByBinStr(decBytes.substring(50, 60), false) / 10.0); + logger.debug("sog = " + Double.toString((sog))); + + // bit 60 - position accuracy won't be read. + + /* longitude bits 61-88 */ + int longitudeHour = AISDecoder.getDecValueByBinStr(decBytes.substring(61, 89), true); + logger.debug("longitudeHour = " + longitudeHour); + + this.longitude = (longitudeHour / 600000.0); + logger.debug("longitude = " + longitude); + + if (this.longitude > 180.0 || this.longitude < -180.0) + { + throw new AISParseException(AISParseException.LONGITUDE_OUT_OF_RANGE + " " + longitude); + } + + /* latitude bits 89-115 */ + int latitudeHour = AISDecoder.getDecValueByBinStr(decBytes.substring(89, 116), true); + this.latitude = (latitudeHour / 600000.0); + logger.debug("latitude = " + latitude); + + if (this.latitude > 90.0 || this.latitude < -90.0) + { + throw new AISParseException(AISParseException.LATITUDE_OUT_OF_RANGE + " " + latitude); + } + + /* COG bits 117-127 */ + this.cog = (AISDecoder.getDecValueByBinStr(decBytes.substring(116, 128), false) / 10.0); + logger.debug("cog = " + cog); + + /* true heading bits 128-136 */ + this.trueHeading = AISDecoder.getDecValueByBinStr(decBytes.substring(128, 137), false); + logger.debug("true heading = " + trueHeading); + + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + this.msgTimestamp = new Timestamp(cal.getTimeInMillis()); + // time stamp bits 138-143 + // TODO: implement the rest bits + return this; + } +} diff --git a/src/main/java/org/freeais/ais/AISPositionB.java b/src/main/java/org/freeais/ais/AISPositionB.java new file mode 100644 index 0000000..06003a3 --- /dev/null +++ b/src/main/java/org/freeais/ais/AISPositionB.java @@ -0,0 +1,270 @@ +/****************************************************************************** + * Freeais.org + * http://www.freeais.org info@freeais.org + * + * Copyright (c) 2007 + * + * ynnor systems GmbH + * Mundsburger Damm 45 + * 22087 Hamburg + * Germany + * + * Alexander Lotter lotter@ynnor.de + * David Schmitz schmitz@ynnor.de + * + * This file is part of Freeais.org. + * + * Freeais.org is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Freeais.org 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + ******************************************************************************/ + +package org.freeais.ais; + +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.TimeZone; + +import org.apache.log4j.Logger; + +/** + * This class represents an AIS position report + * + * @author David Schmitz + * @author Alexander Lotter + * + */ +public class AISPositionB implements IAISMessage, IAISDecodable, Cloneable { + + private static Logger logger = Logger.getLogger(AISDecoder.class); + + /** message id */ + protected int msgId; + + protected int mmsi; + + /** + * speed over ground in knots range 0-102.2 where 102.2knots denotes + * 102.2knots and more + */ + protected double sog; + + /** longitude in degrees +-180° east=positiv west=negative */ + protected double longitude; + + /** latitude in degrees +-90° north=positiv south=negative */ + protected double latitude; + + /** course over ground in knots */ + protected double cog; + + /** degrees 0-359 or 511 for n/a */ + protected int trueHeading; + + /** message timestamp */ + protected Timestamp msgTimestamp; + + public AISPositionB() { + + } + + /** + * This is the default constructor of class AISPosition. + * + * @param msgId + * @param mmsi + * @param sog + * @param longitude + * @param latitude + * @param cog + * @param trueHeading + * @param msgTimestamp + */ + public AISPositionB(int msgId, int mmsi, double sog, double longitude, + double latitude, double cog, int trueHeading, Timestamp msgTimestamp) { + this.msgId = msgId; + this.mmsi = mmsi; + this.sog = sog; + this.longitude = longitude; + this.latitude = latitude; + this.cog = cog; + this.trueHeading = trueHeading; + this.msgTimestamp = msgTimestamp; + } + + public double getCog() { + return cog; + } + + public int getMsgId() { + return msgId; + } + + public double getLatitude() { + return latitude; + } + + public double getLongitude() { + return longitude; + } + + public Timestamp getMsgTimestamp() { + return msgTimestamp; + } + + public double getSog() { + return sog; + } + + public int getTrueHeading() { + return trueHeading; + } + + public int getMmsi() { + return mmsi; + } + + @Override + public String toString() { + StringBuffer sbuffer = new StringBuffer(); + sbuffer.append("AISPositionB\n"); + sbuffer.append("MSGID\t\t" + this.msgId + "\n"); + sbuffer.append("MMSI\t\t" + this.mmsi + "\n"); + sbuffer.append("SOG\t\t" + this.sog + "\n"); + sbuffer.append("LONGITUTDE\t" + this.longitude + "\n"); + sbuffer.append("LATITUDE\t" + this.latitude + "\n"); + sbuffer.append("COG\t\t" + this.cog + "\n"); + sbuffer.append("HEADING\t\t" + this.trueHeading + "\n"); + sbuffer.append("MSGTIMESTAMP\t" + this.msgTimestamp + "\n"); + return sbuffer.toString(); + } + + @Override + public boolean equals(Object o) { + if (o == null) + return false; + if (o == this) + return true; + if (!o.getClass().equals(o.getClass())) + return false; + + AISPositionB that = (AISPositionB) o; + boolean same = this.cog == that.cog && this.msgId == that.msgId + && this.latitude == that.latitude + && this.longitude == that.longitude + && this.msgTimestamp.equals(that.msgTimestamp) + && this.sog == that.sog && this.trueHeading == that.trueHeading + && this.mmsi == that.mmsi; + return same; + } + + @Override + public AISPositionB clone() { + try { + return (AISPositionB) super.clone(); + } catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError(); + } + } + + /** + * Parse decoded bytes for the position data + * + * Message 18: Standard Class B equipment position report Usually will be + * sent by small vessels + * + * ITU-R M.1371-1 + * + * @param decBytes + * @throws AISParseException + */ + public IAISMessage decode(String decBytes) throws AISParseException { + + if (decBytes.length() < 133) + throw new AISParseException( + AISParseException.NOT_CONSISTENT_DECODED_STRING); + /* Possition Reports Message ID 18 bits 0-5 */ + this.msgId = AISDecoder.getDecValueByBinStr(decBytes.substring(0, 6), + false); + logger.debug("messageId = " + msgId); + + /* User ID mmsi bits 8-37 */ + this.mmsi = AISDecoder.getDecValueByBinStr(decBytes.substring(8, 38), + false); + logger.debug("mmsi = " + mmsi); + + /* Speed over ground bits 46-55 - 10 bits */ + this.sog = AISDecoder.getDecValueByBinStr(decBytes.substring(46, 56), + false) / 10.0; + logger.debug("sog = " + Double.toString(sog)); + + /* Longitude bits 57-84 */ + int longitudeHour = AISDecoder.getDecValueByBinStr( + decBytes.substring(57, 85), true); + this.longitude = longitudeHour / 600000.0; + logger.debug("longitude = " + longitude); + + if (this.longitude > 180.0 || this.longitude < -180.0) + throw new AISParseException( + AISParseException.LONGITUDE_OUT_OF_RANGE + " " + longitude); + + /* Latitude bits 85-111 */ + int latitudeHour = AISDecoder.getDecValueByBinStr( + decBytes.substring(85, 112), true); + this.latitude = latitudeHour / 600000.0; + logger.debug("latitude = " + latitude); + + if (this.latitude > 90.0 || this.latitude < -90.0) + throw new AISParseException(AISParseException.LATITUDE_OUT_OF_RANGE + + " " + latitude); + + /* COG bits 112-123 */ + this.cog = AISDecoder.getDecValueByBinStr(decBytes.substring(112, 124), + false) / 10.0; + logger.debug("cog = " + cog); + + /* true heading bits 124-132 */ + this.trueHeading = AISDecoder.getDecValueByBinStr( + decBytes.substring(124, 133), false); + logger.debug("true heading = " + trueHeading); + + /* time stamp bits 138-143 */ + // TODO: impelemt the rest bits + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + this.msgTimestamp = new Timestamp(cal.getTimeInMillis()); + + return this; + + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(cog); + result = prime * result + (int) (temp ^ temp >>> 32); + temp = Double.doubleToLongBits(latitude); + result = prime * result + (int) (temp ^ temp >>> 32); + temp = Double.doubleToLongBits(longitude); + result = prime * result + (int) (temp ^ temp >>> 32); + result = prime * result + mmsi; + result = prime * result + msgId; + result = prime * result + (msgTimestamp == null ? 0 : msgTimestamp.hashCode()); + temp = Double.doubleToLongBits(sog); + result = prime * result + (int) (temp ^ temp >>> 32); + result = prime * result + trueHeading; + return result; + } +} diff --git a/src/main/java/org/freeais/ais/AISPositionExtB.java b/src/main/java/org/freeais/ais/AISPositionExtB.java new file mode 100644 index 0000000..6845807 --- /dev/null +++ b/src/main/java/org/freeais/ais/AISPositionExtB.java @@ -0,0 +1,319 @@ +/****************************************************************************** + * Freeais.org + * http://www.freeais.org info@freeais.org + * + * Copyright (c) 2007 + * + * ynnor systems GmbH + * Mundsburger Damm 45 + * 22087 Hamburg + * Germany + * + * Alexander Lotter lotter@ynnor.de + * David Schmitz schmitz@ynnor.de + * + * This file is part of Freeais.org. + * + * Freeais.org is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Freeais.org 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + ******************************************************************************/ + +package org.freeais.ais; + +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.TimeZone; + +import org.apache.log4j.Logger; + +/** + * This class represents an AIS position report + * + * @author David Schmitz + * @author Alexander Lotter + * + */ +public class AISPositionExtB extends AISPositionB { + + private static Logger logger = Logger.getLogger(AISDecoder.class); + + /** name of the vessel */ + private String name; + + /** type of vessel */ + private int shipType; + + /** a first part of length */ + private int dimensionA; + + /** a second part of length */ + private int dimensionB; + + /** a first part of width */ + private int dimensionC; + + /** a second part of width */ + private int dimensionD; + + /** countryId from mmsi */ + private int countryId; + + /** message source */ + private int msgSrc; + + public AISPositionExtB() { + + } + + /** + * This is default constructor of class AISPositionExtB. + * + * @param msgId + * @param mmsi + * @param sog + * @param longitude + * @param latitude + * @param cog + * @param trueHeading + * @param name + * @param shipType + * @param dimensionA + * @param dimensionB + * @param dimensionC + * @param dimensionD + * @param countryId + * @param msgSrc + * @param msgTimestamp + */ + public AISPositionExtB(int msgId, int mmsi, double sog, double longitude, + double latitude, double cog, int trueHeading, String name, + int shipType, int dimensionA, int dimensionB, int dimensionC, + int dimensionD, int countryId, int msgSrc, Timestamp msgTimestamp) { + super(msgId, mmsi, sog, longitude, latitude, cog, trueHeading, + msgTimestamp); + this.name = name; + this.shipType = shipType; + this.dimensionA = dimensionA; + this.dimensionB = dimensionB; + this.dimensionC = dimensionC; + this.dimensionD = dimensionD; + this.countryId = countryId; + } + + public int getCountryId() { + return countryId; + } + + public void setCountryId(int countryId) { + this.countryId = countryId; + } + + public int getDimensionA() { + return dimensionA; + } + + public void setDimensionA(int dimensionA) { + this.dimensionA = dimensionA; + } + + public int getDimensionB() { + return dimensionB; + } + + public void setDimensionB(int dimensionB) { + this.dimensionB = dimensionB; + } + + public int getDimensionC() { + return dimensionC; + } + + public void setDimensionC(int dimensionC) { + this.dimensionC = dimensionC; + } + + public int getDimensionD() { + return dimensionD; + } + + public void setDimensionD(int dimensionD) { + this.dimensionD = dimensionD; + } + + public int getMsgSrc() { + return msgSrc; + } + + public void setMsgSrc(int msgSrc) { + this.msgSrc = msgSrc; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + StringBuffer sbuffer = new StringBuffer(); + sbuffer.append("AISPositionExtB\n"); + sbuffer.append(super.toString()); + sbuffer.append("AISPositionExtB\n"); + sbuffer.append("NAME\t\t" + this.name + "\n"); + sbuffer.append("SHIPTYPE\t" + this.shipType + "\n"); + sbuffer.append("DIMA\t\t" + this.dimensionA + "\n"); + sbuffer.append("DIMB\t\t" + this.dimensionB + "\n"); + sbuffer.append("DIMC\t\t" + this.dimensionC + "\n"); + sbuffer.append("DIMD\t\t" + this.dimensionD + "\n"); + sbuffer.append("COUNTRYID\t" + this.countryId + "\n"); + sbuffer.append("MSGSRC\t\t" + this.msgSrc + "\n"); + return sbuffer.toString(); + } + + @Override + public boolean equals(Object o) { + if (o == null) + return false; + if (o == this) + return true; + if (!o.getClass().equals(o.getClass())) + return false; + + AISPositionExtB that = (AISPositionExtB) o; + + boolean same = super.equals(that) && this.shipType == that.shipType + && this.dimensionA == that.dimensionA + && this.dimensionB == that.dimensionB + && this.dimensionC == that.dimensionC + && this.dimensionD == that.dimensionD + && this.countryId == that.countryId; + + return same; + } + + @Override + public AISPositionExtB clone() { + return (AISPositionExtB) super.clone(); + + } + + /** + * Parse decoded bytes for the static data + * + * Message 19: Extended Class B equipment position reposrt + * + * ITU-R M.1371-1 + * + * @param decBytes decoded bytes + * @throws AISParseException + */ + @Override + public IAISMessage decode(String decBytes) throws AISParseException { + + if (decBytes.length() < 301) + throw new AISParseException( + AISParseException.NOT_CONSISTENT_DECODED_STRING); + /* Possition Reports Message ID 19 bits 0-5 */ + this.msgId = AISDecoder.getDecValueByBinStr(decBytes.substring(0, 6), + false); + logger.debug("messageId = " + msgId); + + /* User ID mmsi bits 8-37 */ + this.mmsi = AISDecoder.getDecValueByBinStr(decBytes.substring(8, 38), + false); + logger.debug("mmsi = " + mmsi); + + /* Speed over ground bits 46-55 - 10 bits */ + this.sog = AISDecoder.getDecValueByBinStr(decBytes.substring(46, 56), + false) / 10.0; + logger.debug("sog = " + Double.toString(sog)); + + /* Longitude bits 57-84 */ + int longitudeHour = AISDecoder.getDecValueByBinStr( + decBytes.substring(57, 85), true); + this.longitude = longitudeHour / 600000.0; + logger.debug("longitude = " + longitude); + + if (this.longitude > 180.0 || this.longitude < -180.0) + throw new AISParseException( + AISParseException.LONGITUDE_OUT_OF_RANGE + " " + longitude); + + /* Latitude bits 85-111 */ + int latitudeHour = AISDecoder.getDecValueByBinStr( + decBytes.substring(85, 112), true); + this.latitude = latitudeHour / 600000.0; + logger.debug("latitude = " + latitude); + + if (this.latitude > 90.0 || this.latitude < -90.0) + throw new AISParseException(AISParseException.LATITUDE_OUT_OF_RANGE + + " " + latitude); + + /* COG bits 112-123 */ + this.cog = AISDecoder.getDecValueByBinStr(decBytes.substring(112, 124), + false) / 10.0; + logger.debug("cog = " + cog); + + /* true heading bits 124-132 */ + this.trueHeading = AISDecoder.getDecValueByBinStr( + decBytes.substring(124, 133), false); + logger.debug("true heading = " + trueHeading); + + /* Name bits 143-262 - 120 bits */ + this.name = AISDecoder.getDecStringFrom6BitStr(decBytes.substring(143, + 263)); + logger.debug("name = " + name); + + // Type of ship and cargo type bits 263-270 - 8 bits + + this.shipType = AISDecoder.getDecValueByBinStr( + decBytes.substring(263, 271), false); + logger.debug("shipType = " + shipType); + + String mmsiStr = Integer.toString(this.mmsi); + if (!(mmsiStr.length() < 3)) { + this.countryId = Integer.valueOf(mmsiStr.substring(0, 3)); + } else { + this.countryId = 0; + } + logger.debug("countryId = " + countryId); + + /* Dimension/reference for position bits 271-300 - 30 bits */ + this.dimensionA = AISDecoder.getDecValueByBinStr( + decBytes.substring(271, 280), false); + this.dimensionB = AISDecoder.getDecValueByBinStr( + decBytes.substring(280, 289), false); + this.dimensionC = AISDecoder.getDecValueByBinStr( + decBytes.substring(289, 295), false); + this.dimensionD = AISDecoder.getDecValueByBinStr( + decBytes.substring(295, 301), false); + logger.debug(dimensionA + " " + dimensionB + " " + dimensionC + " " + + dimensionD); + + int length = dimensionA + dimensionB; + int width = dimensionC + dimensionD; + logger.debug("length = " + length + "; width = " + width); + + // TODO: impelemt the rest bits + // TODO: implement vesselType, cargoType, countryId, msgSrc + + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + this.msgTimestamp = new Timestamp(cal.getTimeInMillis()); + + return this; + } + +} diff --git a/src/main/java/org/freeais/ais/AISVessel.java b/src/main/java/org/freeais/ais/AISVessel.java new file mode 100644 index 0000000..540e336 --- /dev/null +++ b/src/main/java/org/freeais/ais/AISVessel.java @@ -0,0 +1,375 @@ +/****************************************************************************** + * Freeais.org + * http://www.freeais.org info@freeais.org + * + * Copyright (c) 2007 + * + * ynnor systems GmbH + * Mundsburger Damm 45 + * 22087 Hamburg + * Germany + * + * Alexander Lotter lotter@ynnor.de + * David Schmitz schmitz@ynnor.de + * + * This file is part of Freeais.org. + * + * Freeais.org is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Freeais.org 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + ******************************************************************************/ + +package org.freeais.ais; + +import java.sql.Timestamp; +import java.util.Calendar; + +import org.apache.log4j.Logger; + +/** + * This class is representation of an AIS vessel report + * + * @author David Schmitz + * @author Alexander Lotter + */ +public class AISVessel implements IAISMessage, IAISDecodable, Cloneable { + + private static Logger logger = Logger.getLogger(AISDecoder.class); + + /** message id */ + private int msgId; + + /** unique and persistent ship identification number */ + private int imo; + + /** maritime mobile service identity */ + private int mmsi; + + /** vessel callsign for radio */ + private String callSign; + + /** name of the vessel */ + private String name; + + /** type of vessel */ + private int shipType; + + /** a first part of length */ + private int dimensionA; + + /** a second part of length */ + private int dimensionB; + + /** a first part of width */ + private int dimensionC; + + /** a second part of width */ + private int dimensionD; + + /** estimated time of arrival */ + private Timestamp eta; + + /** maximum present static draught */ + private double draught; + + /** vessels destionation */ + private String destination; + + /** countryId from mmsi */ + private int countryId; + + /** message source */ + private int msgSrc; + + public AISVessel() { + + } + + /** + * This is the default constructor of class AISVessel. + * + * @param msgId + * @param imo + * @param mmsi + * @param callSign + * @param name + * @param shipType + * @param dimensionA + * @param dimensionB + * @param dimensionC + * @param dimensionD + * @param eta + * @param draught + * @param destination + * @param countryId + * @param msgSrc + */ + public AISVessel(int msgId, int imo, int mmsi, String callSign, String name, int shipType, + int dimensionA, int dimensionB, int dimensionC, int dimensionD, Timestamp eta, + double draught, String destination, int countryId, int msgSrc) { + this.msgId = msgId; + this.imo = imo; + this.mmsi = mmsi; + this.callSign = callSign; + this.name = name; + this.shipType = shipType; + this.dimensionA = dimensionA; + this.dimensionB = dimensionB; + this.dimensionC = dimensionC; + this.dimensionD = dimensionD; + this.eta = eta; + this.draught = draught; + this.destination = destination; + this.countryId = countryId; + this.msgSrc = msgSrc; + + } + + public String getName() { + return name; + } + + public String getCallSign() { + return callSign; + } + + public int getShipType() { + return shipType; + } + + public int getCountryId() { + return countryId; + } + + public int getDimensionA() { + return dimensionA; + } + + public int getDimensionB() { + return dimensionB; + } + + public int getDimensionC() { + return dimensionC; + } + + public int getDimensionD() { + return dimensionD; + } + + public int getMsgId() { + return msgId; + } + + public int getImo() { + return imo; + } + + public int getMmsi() { + return mmsi; + } + + public int getMsgSrc() { + return msgSrc; + } + + public double getDraught() { + return draught; + } + + public Timestamp getEta() { + return eta; + } + + public String getDestination() { + return destination; + } + + @Override + public boolean equals(Object o) { + if (o == null) + return false; + if (o == this) + return true; + if (!o.getClass().equals(o.getClass())) + return false; + + AISVessel that = (AISVessel) o; + + boolean same = this.destination.equals(that.destination) + && this.callSign.equals(that.callSign) && this.countryId == that.countryId + && this.dimensionA == that.dimensionA && this.dimensionB == that.dimensionB + && this.dimensionC == that.dimensionC && this.dimensionD == that.dimensionD + && this.msgId == that.msgId && this.imo == that.imo && this.mmsi == that.mmsi + && this.msgSrc == that.msgSrc && this.name.equals(that.name) + && this.shipType == that.shipType; + + return same; + } + + @Override + public AISVessel clone() { + try { + return (AISVessel) super.clone(); + } + + catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError(); + } + } + + @Override + public String toString() { + StringBuffer sbuffer = new StringBuffer(); + sbuffer.append("AISVessel\n"); + sbuffer.append("MSGID\t\t" + this.msgId + "\n"); + sbuffer.append("IMO\t\t" + this.imo + "\n"); + sbuffer.append("MMSI\t\t" + this.mmsi + "\n"); + sbuffer.append("CALLSIGN\t" + this.callSign + "\n"); + sbuffer.append("NAME\t\t" + this.name + "\n"); + sbuffer.append("SHIPTYPE\t" + this.shipType + "\n"); + sbuffer.append("DIMA\t\t" + this.dimensionA + "\n"); + sbuffer.append("DIMB\t\t" + this.dimensionB + "\n"); + sbuffer.append("DIMC\t\t" + this.dimensionC + "\n"); + sbuffer.append("DIMD\t\t" + this.dimensionD + "\n"); + sbuffer.append("ETA\t\t" + this.eta + "\n"); + sbuffer.append("DRAUGHT\t\t" + this.draught + "\n"); + sbuffer.append("DESTINATION\t" + this.destination + "\n"); + sbuffer.append("COUNTRYID\t" + this.countryId + "\n"); + sbuffer.append("MSGSRC\t\t" + this.msgSrc + "\n"); + return sbuffer.toString(); + } + + /** + * Parse decoded bytes for the static data + * + * Message 5: Ship static and voyage related data ITU-R M.1371-1 + * + * + * @param decBytes decoded bytes + * @throws AISParseException + */ + public IAISMessage decode(String decBytes) throws AISParseException { + + if (decBytes.length() < 421) + throw new AISParseException(AISParseException.NOT_CONSISTENT_DECODED_STRING); + /* Possition Reports Message ID 1,2,3 bits 0-5 */ + this.msgId = AISDecoder.getDecValueByBinStr(decBytes.substring(0, 6), false); + logger.debug("messageId = " + msgId); + + /* User ID mmsi bits 8-37 */ + this.mmsi = AISDecoder.getDecValueByBinStr(decBytes.substring(8, 38), false); + logger.debug("mmsi = " + mmsi); + + // AIS version indicator bits 38-39 - 2 bits + // int navState = decBytes[6] & 0x3; + // System.out.println("navState = "+navState); + + /* IMO number bits 40-69 - 30 bits */ + this.imo = AISDecoder.getDecValueByBinStr(decBytes.substring(40, 70), false); + logger.debug("imo = " + imo); + + /* Call sign bits 70-111 - 42 bits */ + this.callSign = AISDecoder.getDecStringFrom6BitStr(decBytes.substring(70, 112)); + logger.debug("CallSign = " + callSign); + + /* Name bits 112-231 - 120 bits */ + this.name = AISDecoder.getDecStringFrom6BitStr(decBytes.substring(112, 232)); + logger.debug("name = " + name); + + /* Type of ship and cargo type bits 232-239 - 8 bits */ + // :TODO Ship and Cargo digits should be splitted + this.shipType = AISDecoder.getDecValueByBinStr(decBytes.substring(232, 240), false); + logger.debug("shipType = " + shipType); + + String mmsiStr = Integer.toString(this.mmsi); + if (!(mmsiStr.length() < 3)) { + this.countryId = Integer.valueOf(mmsiStr.substring(0, 3)); + } else { + this.countryId = 0; + } + logger.debug("countryId = " + countryId); + + /* Dimension/reference for position bits 240-269 - 30 bits */ + this.dimensionA = AISDecoder.getDecValueByBinStr(decBytes.substring(240, 249), false); + this.dimensionB = AISDecoder.getDecValueByBinStr(decBytes.substring(249, 258), false); + this.dimensionC = AISDecoder.getDecValueByBinStr(decBytes.substring(258, 264), false); + this.dimensionD = AISDecoder.getDecValueByBinStr(decBytes.substring(264, 270), false); + logger.debug(dimensionA + " " + dimensionB + " " + dimensionC + " " + dimensionD); + + int length = this.dimensionA + this.dimensionB; + int width = this.dimensionC + this.dimensionD; + logger.debug("length = " + length + "; width = " + width); + + // Type of electronic positon fixing device + // bits 270-273 - 4 bits + + /* ETA bits 274-293 - 20 bits */ + int etaMinute = AISDecoder.getDecValueByBinStr(decBytes.substring(288, 294), false); + int etaHour = AISDecoder.getDecValueByBinStr(decBytes.substring(283, 288), false); + int etaDay = AISDecoder.getDecValueByBinStr(decBytes.substring(278, 283), false); + int etaMonth = AISDecoder.getDecValueByBinStr(decBytes.substring(274, 278), false); + Calendar cal = Calendar.getInstance();//TimeZone.getTimeZone("UTC")); + int year = cal.get(Calendar.YEAR); + cal.clear(); + cal.set(year, etaMonth - 1, etaDay, etaHour, etaMinute); + + this.eta =new Timestamp(cal.getTimeInMillis()); + logger.debug("ETA " + " month = " + etaMonth + " day = " + etaDay + " hour = " + etaHour + + " min = " + etaMinute); + + /* Maximum present static draught bits 294-301 - 8 bits */ + int draughtInt = AISDecoder.getDecValueByBinStr(decBytes.substring(294, 302), false); + this.draught = draughtInt / 10.0; + logger.debug("draught = " + draught); + + /* Destionation bits 302-421 - 120 bits */ + this.destination = AISDecoder.getDecStringFrom6BitStr(decBytes.substring(302, 422)); + logger.debug("destination = " + destination); + + // TODO: impelemt the rest bits + + // TODO: implement vesselType, countryId, msgSrc, cargoType + // AISVessel aisVessel = new + // AISVessel(messageId,imo,mmsi,callSign,name,vesselType,cargoType, + // dimA,dimB,dimC,dimD,countryId,msgSrc); + + logger.info("parseMsgForStaticData(encodedMsg) - Exit"); + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(draught); + result = prime * result + (int) (temp ^ temp >>> 32); + result = prime * result + msgId; + result = prime * result + imo; + result = prime * result + mmsi; + result = prime * result + shipType; + result = prime * result + (eta == null ? 0 : eta.hashCode()); + result = prime * result + (callSign == null ? 0 : callSign.hashCode()); + result = prime * result + (name == null ? 0 : name.hashCode()); + result = prime * result + (destination == null ? 0 : destination.hashCode()); + result = prime * result + countryId; + result = prime * result + msgSrc; + result = prime * result + dimensionA; + result = prime * result + dimensionB; + result = prime * result + dimensionC; + result = prime * result + dimensionD; + return result; + } +} diff --git a/src/main/java/org/freeais/ais/IAISDecodable.java b/src/main/java/org/freeais/ais/IAISDecodable.java new file mode 100644 index 0000000..1c8fc0a --- /dev/null +++ b/src/main/java/org/freeais/ais/IAISDecodable.java @@ -0,0 +1,47 @@ +/****************************************************************************** + * Freeais.org + * http://www.freeais.org info@freeais.org + * + * Copyright (c) 2007 + * + * ynnor systems GmbH + * Mundsburger Damm 45 + * 22087 Hamburg + * Germany + * + * Alexander Lotter lotter@ynnor.de + * David Schmitz schmitz@ynnor.de + * + * This file is part of Freeais.org. + * + * Freeais.org is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Freeais.org 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + ******************************************************************************/ + +package org.freeais.ais; + +/** + * + * All position reports, which implement this interface can be decoded by the + * parser. + * + * @author Alexander Lotter + * @author David Schmitz + * + */ +public interface IAISDecodable { + + public IAISMessage decode(String decBytes) throws AISParseException; + +} diff --git a/src/main/java/org/freeais/ais/IAISMessage.java b/src/main/java/org/freeais/ais/IAISMessage.java new file mode 100644 index 0000000..47c94fa --- /dev/null +++ b/src/main/java/org/freeais/ais/IAISMessage.java @@ -0,0 +1,42 @@ +/****************************************************************************** + * Freeais.org + * http://www.freeais.org info@freeais.org + * + * Copyright (c) 2007 + * + * ynnor systems GmbH + * Mundsburger Damm 45 + * 22087 Hamburg + * Germany + * + * Alexander Lotter lotter@ynnor.de + * David Schmitz schmitz@ynnor.de + * + * This file is part of Freeais.org. + * + * Freeais.org is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Freeais.org 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + ******************************************************************************/ + +package org.freeais.ais; + +/** + * This is a marker interface to express classes are of kind IAISMessage. + * @author David Schmitz + * @author Alexander Lotter + * + */ +public interface IAISMessage { + //Do not add any methods because this is a marker interface +} diff --git a/src/main/java/org/freeais/i18n/I18N.java b/src/main/java/org/freeais/i18n/I18N.java new file mode 100644 index 0000000..9cd033a --- /dev/null +++ b/src/main/java/org/freeais/i18n/I18N.java @@ -0,0 +1,66 @@ +/****************************************************************************** + * Freeais.org + * http://www.freeais.org info@freeais.org + * + * Copyright (c) 2007 + * + * ynnor systems GmbH + * Mundsburger Damm 45 + * 22087 Hamburg + * Germany + * + * Alexander Lotter lotter@ynnor.de + * David Schmitz schmitz@ynnor.de + * + * This file is part of Freeais.org. + * + * Freeais.org is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Freeais.org 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + ******************************************************************************/ + +package org.freeais.i18n; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import org.apache.log4j.Logger; + +public class I18N { + + private static Logger logger = Logger.getLogger(I18N.class); + + private static final String BUNDLE_NAME = "org.freeais.i18n.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle + .getBundle(BUNDLE_NAME); + + private I18N(){ + } + + + public static String getString(String key) { + logger.info("getString(key) - Entry"); + + try { + logger.info("getString(key) - Exit"); + return RESOURCE_BUNDLE.getString(key); + } catch (MissingResourceException e) { + logger.error("getString(key) - MissingResourceException " + + e.getMessage()); + logger.info("getString(key) - Exit"); + return '!' + key + '!'; + } + + } +} diff --git a/src/main/java/org/freeais/i18n/messages.properties b/src/main/java/org/freeais/i18n/messages.properties new file mode 100644 index 0000000..1d0ca08 --- /dev/null +++ b/src/main/java/org/freeais/i18n/messages.properties @@ -0,0 +1,47 @@ +AISParseException.INVALID_CHARACTER=Wrong character in the string +AISParseException.NO_BYTES_TO_PARSE=There are no bytes to parse +AISParseException.WRONG_MESSAGE_ID=MessageId not supported +AISParseException.NO_AIS_MESSAGE=Not an ais-message +AISParseException.EMPTY_AIS_MESSAGE=Empty ais-message +AISParseException.BITSET_OUT_OF_RANGE=BitSet is out of range +AISParseException.NO_DECODED_BYTES=There are no bytes to decode +AISParseException.NOT_CONSISTENT_DECODED_STRING=Decoded binary string is not consistent +AISParseException.LONGITUDE_OUT_OF_RANGE=Longitude value is out of range +AISParseException.LATITUDE_OUT_OF_RANGE=Latitude value is out of range + +InvalidPropertyFileException.INVALID_PROPERTY_FILE=Invalid property-file +InvalidPropertyFileException.SERIAL_PORT_NOT_DEFINED=Serial port wasn't defined properly + +DBException.CANNOT_CREATE_DB_FOLDER=Cannot create database folder +DBException.CANNOT_ESTABLISH_DB_CONNECTION=Cannot establish database connection of type +DBException.CANNOT_INIT_DB_TABLE=Cannot initialize database table +DBException.CANNOT_CREATE_DB=Cannot create database +DBException.CANNOT_INIT_DB=Cannot initialize database +DBException.CANNOT_INSERT_DATA_INTO_DB=Cannot insert into database +DBException.CANNOT_DROP_DATABASE=Cannot drop database +DBException.DATABASE_DOES_NOT_EXIST=Database doesn't exist +DBException.CANNOT_SHUTDOWN_DATABASE=Cannot shutdown database + +PointFormatException.WRONG_POINT_FORMAT=Wrong point format +PointFormatException.WRONG_LATITUDE_FORMAT=Wrong latitude format +PointFormatException.WRONG_LONGITUDE_FORMAT=Wrong longitude format + +CalibrationException.NOT_ENOUGHT_CALIBRATION_POINTS=Not enough points for calibration +CalibrationException.MAP_CALIBRATOR_IS_NOT_DEFINED=Map calibrator is not defined + +# GUI +ViewSystray.ITEM_FREEAIS=Freeais View +ViewSystray.ITEM_GOOGLE_EARTH=Google Earth View +ViewSystray.ITEM_CONFIGURATION=Configuration +ViewSystray.ITEM_START=Start +ViewSystray.ITEM_STOP=Stop +ViewSystray.ITEM_HELP=Help +ViewSystray.ITEM_FINISH=Finish +ViewSystray.DESCRIPTION=Freeais vessel monitoring +ViewSystray.8=Unable to add to system tray: +ViewSystray.9=No system tray available + +Radar.NORTH=N (North Up) +Radar.WEST=W +Radar.EAST=E +Radar.SOUTH=S diff --git a/src/main/java/org/freeais/i18n/messages_de_DE.properties b/src/main/java/org/freeais/i18n/messages_de_DE.properties new file mode 100644 index 0000000..05c19c9 --- /dev/null +++ b/src/main/java/org/freeais/i18n/messages_de_DE.properties @@ -0,0 +1,37 @@ +AISParseException.INVALID_CHARACTER=Falsches Zeichen im String +AISParseException.NO_BYTES_TO_PARSE=Es sind keine bytes zum parsen vorhanden +AISParseException.WRONG_MESSAGE_ID=Falsche messageId +AISParseException.NO_AIS_MESSAGE=Keine AIS-Nachricht +AISParseException.EMPTY_AIS_MESSAGE=Leere AIS-Nachricht +AISParseException.BITSET_OUT_OF_RANGE=BitSet is out of range +AISParseException.NO_DECODED_BYTES=Keine bytes zur Decodierung vorhanden +AISParseException.NOT_CONSISTENT_DECODED_STRING=Decodierter binary string ist inkonsistent + +InvalidPropertyFileException.INVALID_PROPERTY_FILE=Invalides property-file +InvalidPropertyFileException.SERIAL_PORT_NOT_DEFINED=Serielle Schnittstelle wurde nicht richtig definiert + +DBException.CANNOT_CREATE_DB_FOLDER=Datenbank Ordner konnte nicht erstellt werden +DBException.CANNOT_ESTABLISH_DB_CONNECTION=Cannot establish database connection of type +DBException.CANNOT_INIT_DB_TABLE=Datenbank Tabelle konnte nicht initialisiert werden +DBException.CANNOT_CREATE_DB=Datenbank konnte nicht erstellt werden +DBException.CANNOT_INIT_DB=Datenbank konnte nicht initialisiert werden +DBException.CANNOT_INSERT_DATA_INTO_DB=Cannot insert into database + +DBException.CANNOT_DROP_DATABASE=Cannot drop database +DBException.DATABASE_DOES_NOT_EXIST=Datenbank existiert nicht +DBException.CANNOT_SHUTDOWN_DATABASE=Datenbank konnte nicht beendet werden + +PointFormatException.WRONG_POINT_FORMAT=Falsches Punktformat +PointFormatException.WRONG_LATITUDE_FORMAT=Falsches latitude format +PointFormatException.WRONG_LONGITUDE_FORMAT=Falsches longitude format + +ViewSystray.ITEM_FREEAIS=Freeais View +ViewSystray.ITEM_GOOGLE_EARTH=Google Earth View +ViewSystray.ITEM_CONFIGURATION=Konfiguration +ViewSystray.ITEM_START=Start +ViewSystray.ITEM_STOP=Stop +ViewSystray.ITEM_HELP=Hilfe +ViewSystray.ITEM_FINISH=Beenden +ViewSystray.DESCRIPTION=Freeais Schiff Monitor +ViewSystray.8=Konnte nicht zum System Tray hinzugefügt werden: +ViewSystray.9=Kein System Tray verfügbar diff --git a/src/main/resources/logging.properties b/src/main/resources/logging.properties new file mode 100644 index 0000000..2f42c72 --- /dev/null +++ b/src/main/resources/logging.properties @@ -0,0 +1,8 @@ +handlers = java.util.logging.ConsoleHandler + +.level = WARNING + +java.util.logging.ConsoleHandler.level = ALL +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter + +org.freeais.level=ALL