From c63a9bb68972a0d639200b4e7bfe8feb07545764 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 21 Oct 2021 19:38:57 +0200 Subject: [PATCH] initial import of ais stuff --- lib/nmea2kto0183/N2kToNMEA0183Functions.h | 1 + lib/nmea2ktoais/NMEA0183AISMessages.cpp | 571 ++++++++++++++++++++++ lib/nmea2ktoais/NMEA0183AISMessages.h | 86 ++++ lib/nmea2ktoais/NMEA0183AISMsg.cpp | 299 +++++++++++ lib/nmea2ktoais/NMEA0183AISMsg.h | 92 ++++ lib/nmea2ktoais/README.md | 49 ++ 6 files changed, 1098 insertions(+) create mode 100644 lib/nmea2ktoais/NMEA0183AISMessages.cpp create mode 100644 lib/nmea2ktoais/NMEA0183AISMessages.h create mode 100644 lib/nmea2ktoais/NMEA0183AISMsg.cpp create mode 100644 lib/nmea2ktoais/NMEA0183AISMsg.h create mode 100644 lib/nmea2ktoais/README.md diff --git a/lib/nmea2kto0183/N2kToNMEA0183Functions.h b/lib/nmea2kto0183/N2kToNMEA0183Functions.h index c998b75..e4529ce 100644 --- a/lib/nmea2kto0183/N2kToNMEA0183Functions.h +++ b/lib/nmea2kto0183/N2kToNMEA0183Functions.h @@ -11,6 +11,7 @@ #include #include "N2kDataToNMEA0183.h" #include +#include "NMEA0183AISMessages.h" class N2kToNMEA0183Functions : public N2kDataToNMEA0183 { typedef void (N2kToNMEA0183Functions::*N2KConverter)(const tN2kMsg &N2kMsg); diff --git a/lib/nmea2ktoais/NMEA0183AISMessages.cpp b/lib/nmea2ktoais/NMEA0183AISMessages.cpp new file mode 100644 index 0000000..a7b4ed7 --- /dev/null +++ b/lib/nmea2ktoais/NMEA0183AISMessages.cpp @@ -0,0 +1,571 @@ +/* +NMEA0183AISMessages.cpp + +Copyright (c) 2019 Ronnie Zeiller + +Based on the works of Timo Lappalainen NMEA2000 and NMEA0183 Library +Thanks to Eric S. Raymond (https://gpsd.gitlab.io/gpsd/AIVDM.html) +and Kurt Schwehr for their informations on AIS encoding. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include +#include +#include +//#include +//#include +#include +#include +#include + +const double pi=3.1415926535897932384626433832795; +const double kmhToms=1000.0/3600.0; +const double knToms=1852.0/3600.0; +const double degToRad=pi/180.0; +const double radToDeg=180.0/pi; +const double msTokmh=3600.0/1000.0; +const double msTokn=3600.0/1852.0; +const double nmTom=1.852*1000; +const double mToFathoms=0.546806649; +const double mToFeet=3.2808398950131; +const double radsToDegMin = 60 * 360.0 / (2 * pi); // [rad/s -> degree/minute] +const char Prefix='!'; + +std::vector vships; + +// ************************ Helper for AIS *********************************** +static bool AddMessageType(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageType); +static bool AddRepeat(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t Repeat); +static bool AddUserID(tNMEA0183AISMsg &NMEA0183AISMsg, uint32_t UserID); +static bool AddIMONumber(tNMEA0183AISMsg &NMEA0183AISMsg, uint32_t &IMONumber); +static bool AddText(tNMEA0183AISMsg &NMEA0183AISMsg, char *FieldVal, uint8_t length); +static bool AddVesselType(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t VesselType); +static bool AddDimensions(tNMEA0183AISMsg &NMEA0183AISMsg, double Length, double Beam, double PosRefStbd, double PosRefBow); +static bool AddNavStatus(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t &NavStatus); +static bool AddROT(tNMEA0183AISMsg &NMEA0183AISMsg, double &rot); +static bool AddSOG (tNMEA0183AISMsg &NMEA0183AISMsg, double &sog); +static bool AddLongitude(tNMEA0183AISMsg &NMEA0183AISMsg, double &Longitude); +static bool AddLatitude(tNMEA0183AISMsg &NMEA0183AISMsg, double &Latitude); +static bool AddHeading (tNMEA0183AISMsg &NMEA0183AISMsg, double &heading); +static bool AddCOG(tNMEA0183AISMsg &NMEA0183AISMsg, double cog); +static bool AddSeconds (tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t &Seconds); +static bool AddEPFDFixType(tNMEA0183AISMsg &NMEA0183AISMsg, tN2kGNSStype &GNSStype); +static bool AddStaticDraught(tNMEA0183AISMsg &NMEA0183AISMsg, double &Draught); +static bool AddETADateTime(tNMEA0183AISMsg &NMEA0183AISMsg, uint16_t &ETAdate, double &ETAtime); + +//***************************************************************************** +// Types 1, 2 and 3: Position Report Class A or B -> https://gpsd.gitlab.io/gpsd/AIVDM.html +// total of 168 bits, occupying one AIVDM sentence +// Example: !AIVDM,1,1,,A,133m@ogP00PD;88MD5MTDww@2D7k,0*46 +// Payload: Payload: 133m@ogP00PD;88MD5MTDww@2D7k +// Message type 1 has a payload length of 168 bits. +// because AIS encodes messages using a 6-bits ASCII mechanism and 168 divided by 6 is 28. +// +// Got values from: ParseN2kPGN129038() +bool SetAISClassABMessage1( tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageType, uint8_t Repeat, + uint32_t UserID, double Latitude, double Longitude, bool Accuracy, bool RAIM, uint8_t Seconds, + double COG, double SOG, double Heading, double ROT, uint8_t NavStatus ) { + + NMEA0183AISMsg.ClearAIS(); + if ( !AddMessageType(NMEA0183AISMsg, MessageType) ) return false; // 0 - 5 | 6 Message Type -> Constant: 1 + if ( !AddRepeat(NMEA0183AISMsg, Repeat) ) return false; // 6 - 7 | 2 Repeat Indicator: 0 = default; 3 = do not repeat any more + if ( !AddUserID(NMEA0183AISMsg, UserID) ) return false; // 8 - 37 | 30 MMSI + if ( !AddNavStatus(NMEA0183AISMsg, NavStatus) ) return false; // 38-41 | 4 Navigational Status e.g.: "Under way sailing" + if ( !AddROT(NMEA0183AISMsg, ROT) ) return false; // 42-49 | 8 Rate of Turn (ROT) + if ( !AddSOG(NMEA0183AISMsg, SOG) ) return false; // 50-59 | 10 [m/s -> kts] SOG with one digit x10, 1023 = N/A + if ( !NMEA0183AISMsg.AddBoolToPayloadBin(Accuracy, 1) ) return false;// 60 | 1 GPS Accuracy 1 oder 0, Default 0 + if ( !AddLongitude(NMEA0183AISMsg, Longitude) ) return false; // 61-88 | 28 Longitude in Minutes / 10000 + if ( !AddLatitude(NMEA0183AISMsg, Latitude) ) return false; // 89-115 | 27 Latitude in Minutes / 10000 + if ( !AddCOG(NMEA0183AISMsg, COG) ) return false; // 116-127 | 12 Course over ground will be 3600 (0xE10) if that data is not available. + if ( !AddHeading (NMEA0183AISMsg, Heading) ) return false; // 128-136 | 9 True Heading (HDG) + if ( !AddSeconds(NMEA0183AISMsg, Seconds) ) return false; // 137-142 | 6 Seconds in UTC timestamp) + if ( !NMEA0183AISMsg.AddIntToPayloadBin(0, 2) ) return false; // 143-144 | 2 Maneuver Indicator: 0 (default) 1, 2 (not delivered within this PGN) + if ( !NMEA0183AISMsg.AddIntToPayloadBin(0, 3) ) return false; // 145-147 | 3 Spare + if ( !NMEA0183AISMsg.AddBoolToPayloadBin(RAIM, 1) ) return false; // 148-148 | 1 RAIM flag 0 = RAIM not in use (default), 1 = RAIM in use + if ( !NMEA0183AISMsg.AddIntToPayloadBin(0, 19) ) return false; // 149-167 | 19 Radio Status (-> 0 NOT SENT WITH THIS PGN!!!!!) + + if ( !NMEA0183AISMsg.Init("VDM","AI", Prefix) ) return false; + if ( !NMEA0183AISMsg.AddStrField("1") ) return false; + if ( !NMEA0183AISMsg.AddStrField("1") ) return false; + if ( !NMEA0183AISMsg.AddEmptyField() ) return false; + if ( !NMEA0183AISMsg.AddStrField("A") ) return false; + if ( !NMEA0183AISMsg.AddStrField( NMEA0183AISMsg.GetPayload() ) ) return false; + if ( !NMEA0183AISMsg.AddStrField("0") ) return false; // Message 1,2,3 has always Zero Padding + + return true; +} + +// ***************************************************************************** +// https://www.navcen.uscg.gov/?pageName=AISMessagesAStatic# +// AIS class A Static and Voyage Related Data +// Values derived from ParseN2kPGN129794(); +bool SetAISClassAMessage5(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageID, uint8_t Repeat, + uint32_t UserID, uint32_t IMONumber, char *Callsign, char *Name, + uint8_t VesselType, double Length, double Beam, double PosRefStbd, + double PosRefBow, uint16_t ETAdate, double ETAtime, double Draught, + char *Destination, tN2kGNSStype GNSStype, uint8_t DTE ) { + + // AIS Type 5 Message + NMEA0183AISMsg.ClearAIS(); + if ( !AddMessageType(NMEA0183AISMsg, 5) ) return false; // 0 - 5 | 6 Message Type -> Constant: 5 + if ( !AddRepeat(NMEA0183AISMsg, Repeat) ) return false; // 6 - 7 | 2 Repeat Indicator: 0 = default; 3 = do not repeat any more + if ( !AddUserID(NMEA0183AISMsg, UserID) ) return false; // 8 - 37 | 30 MMSI + if ( !NMEA0183AISMsg.AddIntToPayloadBin(1, 2) ) return false; // 38 - 39 | 2 AIS Version -> 0 oder 1 NOT DERIVED FROM N2k, Always 1!!!! + if ( !AddIMONumber(NMEA0183AISMsg, IMONumber) ) return false; // 40 - 69 | 30 IMO Number unisgned + if ( !AddText(NMEA0183AISMsg, Callsign, 42) ) return false; // 70 - 111 | 42 Call Sign WDE4178 -> 7 6-bit characters -> Ascii lt. Table) + if ( !AddText(NMEA0183AISMsg, Name, 120) ) return false; // 112-231 | 120 Vessel Name POINT FERMIN -> 20 6-bit characters -> Ascii lt. Table + if ( !NMEA0183AISMsg.AddIntToPayloadBin(VesselType, 8) ) return false; // 232-239 | 8 Ship Type 0....255 e.g. 31 Towing + if ( !AddDimensions(NMEA0183AISMsg, Length, Beam, PosRefStbd, PosRefBow) ) return false; // 240 - 269 | 30 Dimensions + if ( !AddEPFDFixType(NMEA0183AISMsg, GNSStype) ) return false; // 270-273 | 4 Position Fix Type, 0 (default) + if ( !AddETADateTime(NMEA0183AISMsg, ETAdate, ETAtime) ) return false; // 274 -293 | 20 Estimated time of arrival; MMDDHHMM UTC + if ( !AddStaticDraught(NMEA0183AISMsg, Draught) ) return false; // 294-301 | 8 Maximum Present Static Draught + if ( !AddText(NMEA0183AISMsg, Destination, 120) ) return false; // 302-421 | 120 | 20 Destination 20 6-bit characters + if ( !NMEA0183AISMsg.AddIntToPayloadBin(DTE, 1) ) return false; // 422 | 1 | Data terminal equipment (DTE) ready (0 = available, 1 = not available = default) + if ( !NMEA0183AISMsg.AddIntToPayloadBin(0, 1) ) return false; // 423 | 1 | spare + + return true; +} + +// **************************************************************************** +// AIS position report (class B 129039) -> Type 18: Standard Class B CS Position Report +// ParseN2kPGN129039(const tN2kMsg &N2kMsg, uint8_t &MessageID, tN2kAISRepeat &Repeat, uint32_t &UserID, +// double &Latitude, double &Longitude, bool &Accuracy, bool &RAIM, +// uint8_t &Seconds, double &COG, double &SOG, double &Heading, tN2kAISUnit &Unit, +// bool &Display, bool &DSC, bool &Band, bool &Msg22, tN2kAISMode &Mode, bool &State) +// VDM, VDO (AIS VHF Data-link message 18) +bool SetAISClassBMessage18(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageID, uint8_t Repeat, uint32_t UserID, + double Latitude, double Longitude, bool Accuracy, bool RAIM, + uint8_t Seconds, double COG, double SOG, double Heading, tN2kAISUnit Unit, + bool Display, bool DSC, bool Band, bool Msg22, bool Mode, bool State) { + // + NMEA0183AISMsg.ClearAIS(); + if ( !AddMessageType(NMEA0183AISMsg, MessageID) ) return false; // 0 - 5 | 6 Message Type -> Constant: 18 + if ( !AddRepeat(NMEA0183AISMsg, Repeat) ) return false; // 6 - 7 | 2 Repeat Indicator: 0 = default; 3 = do not repeat any more + if ( !AddUserID(NMEA0183AISMsg, UserID) ) return false; // 8 - 37 | 30 MMSI + if ( !NMEA0183AISMsg.AddIntToPayloadBin(0, 8) ) return false; // 38-45 | 8 Regional Reserved + if ( !AddSOG(NMEA0183AISMsg, SOG) ) return false; // 46-55 | 10 [m/s -> kts] SOG with one digit x10, 1023 = N/A + if ( !NMEA0183AISMsg.AddBoolToPayloadBin(Accuracy, 1)) return false; // 56 | 1 GPS Accuracy 1 oder 0, Default 0 + if ( !AddLongitude(NMEA0183AISMsg, Longitude) ) return false; // 57-84 | 28 Longitude in Minutes / 10000 + if ( !AddLatitude(NMEA0183AISMsg, Latitude) ) return false; // 85-111 | 27 Latitude in Minutes / 10000 + if ( !AddCOG(NMEA0183AISMsg, COG) ) return false; // 112-123 | 12 Course over ground will be 3600 (0xE10) if that data is not available. + if ( !AddHeading (NMEA0183AISMsg, Heading) ) return false; // 124-132 | 9 True Heading (HDG) + if ( !AddSeconds(NMEA0183AISMsg, Seconds) ) return false; // 133-138 | 6 Seconds in UTC timestamp) + if ( !NMEA0183AISMsg.AddIntToPayloadBin(0, 2) ) return false; // 139-140 | 2 Regional Reserved + if ( !NMEA0183AISMsg.AddIntToPayloadBin(Unit, 1) ) return false; // 141 | 1 0=Class B SOTDMA unit 1=Class B CS (Carrier Sense) unit + if ( !NMEA0183AISMsg.AddIntToPayloadBin(Display, 1) ) return false; // 142 | 1 0=No visual display, 1=Has display, (Probably not reliable). + if ( !NMEA0183AISMsg.AddBoolToPayloadBin(DSC, 1) ) return false; // 143 | 1 If 1, unit is attached to a VHF voice radio with DSC capability. + if ( !NMEA0183AISMsg.AddBoolToPayloadBin(Band, 1) ) return false; // 144 | 1 If this flag is 1, the unit can use any part of the marine channel. + if ( !NMEA0183AISMsg.AddBoolToPayloadBin(Msg22, 1) ) return false; // 145 | 1 If 1, unit can accept a channel assignment via Message Type 22. + if ( !NMEA0183AISMsg.AddBoolToPayloadBin(Mode, 1) ) return false; // 146 | 1 Assigned-mode flag: 0 = autonomous mode (default), 1 = assigned mode + if ( !NMEA0183AISMsg.AddBoolToPayloadBin(RAIM, 1) ) return false; // 147 | 1 as for Message Type 1,2,3 + if ( !NMEA0183AISMsg.AddIntToPayloadBin(0, 20) ) return false; // 148-167 | 20 Radio Status not in PGN 129039 + + if ( !NMEA0183AISMsg.Init("VDM","AI", Prefix) ) return false; + if ( !NMEA0183AISMsg.AddStrField("1") ) return false; + if ( !NMEA0183AISMsg.AddStrField("1") ) return false; + if ( !NMEA0183AISMsg.AddEmptyField() ) return false; + if ( !NMEA0183AISMsg.AddStrField("B") ) return false; + if ( !NMEA0183AISMsg.AddStrField( NMEA0183AISMsg.GetPayload() ) ) return false; + if ( !NMEA0183AISMsg.AddStrField("0") ) return false; // Message 18, has always Zero Padding + + return true; +} + +// **************************************************************************** +// Type 24: Static Data Report +// Equivalent of a Type 5 message for ships using Class B equipment. Also used to associate an MMSI +// with a name on either class A or class B equipment. +// +// A "Type 24" may be in part A or part B format; According to the standard, parts A and B are expected +// to be broadcast in adjacent pairs; in the real world they may (due to quirks in various aggregation methods) +// be separated by other sentences or even interleaved with different Type 24 pairs; decoders must cope with this. +// The interpretation of some fields in Type B format changes depending on the range of the Type B MMSI field. +// +// 160 bits for part A, 168 bits for part B. +// According to the standard, both the A and B parts are supposed to be 168 bits. +// However, in the wild, A parts are often transmitted with only 160 bits, omitting the spare 7 bits at the end. +// Implementers should be permissive about this. +// +// If the Part Number field is 0, the rest of the message is interpreted as a Part A; +// If it is 1, the rest of the message is interpreted as a Part B; values 2 and 3 are not allowed. +// +// PGN 129809 AIS Class B "CS" Static Data Report, Part A -> AIS VHF Data-link message 24 +// PGN 129810 AIS Class B "CS" Static Data Report, Part B -> AIS VHF Data-link message 24 +// ParseN2kPGN129809 (const tN2kMsg &N2kMsg, uint8_t &MessageID, tN2kAISRepeat &Repeat, uint32_t &UserID, char *Name) -> store to vector +// ParseN2kPGN129810(const tN2kMsg &N2kMsg, uint8_t &MessageID, tN2kAISRepeat &Repeat, uint32_t &UserID, +// uint8_t &VesselType, char *Vendor, char *Callsign, double &Length, double &Beam, +// double &PosRefStbd, double &PosRefBow, uint32_t &MothershipID); +// +// Part A: MessageID, Repeat, UserID, ShipName -> store in vector to call on Part B arrivals!!! +// Part B: MessageID, Repeat, UserID, VesselType (5), Callsign (5), Length & Beam, PosRefBow,.. (5) +bool SetAISClassBMessage24PartA(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageID, uint8_t Repeat, uint32_t UserID, char *Name) { + + bool found = false; + for (int i = 0; i < vships.size(); i++) { + if ( vships[i]->_userID == UserID ) { + found = true; + break; + } + } + if ( ! found ) { + std::string nm; + nm+= Name; + vships.push_back(new ship(UserID, nm)); + } + return true; +} + +// *************************************************************************************************************** +bool SetAISClassBMessage24(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageID, uint8_t Repeat, + uint32_t UserID, uint8_t VesselType, char *VendorID, char *Callsign, + double Length, double Beam, double PosRefStbd, double PosRefBow, uint32_t MothershipID ) { + + uint8_t PartNr = 0; // Identifier for the message part number; always 0 for Part A + char *ShipName = (char*)" "; // get from vector to look up for sent Messages Part A + + uint8_t i; + for ( i = 0; i < vships.size(); i++) { + if ( vships[i]->_userID == UserID ) { + Serial.print("UserID gefunden: "); Serial.print(UserID); + ShipName = const_cast( vships[i]->_shipName.c_str() ); + Serial.print(" / "); Serial.println( ShipName); + } + } + if ( i > MAX_SHIP_IN_VECTOR ) { + vships.erase(vships.begin()); + } + + // AIS Type 24 Message + NMEA0183AISMsg.ClearAIS(); + // Common for PART A AND Part B Bit 0 - 39 / len 40 + if ( !AddMessageType(NMEA0183AISMsg, 24) ) return false; // 0 - 5 | 6 Message Type -> Constant: 24 + if ( !AddRepeat(NMEA0183AISMsg, Repeat) ) return false; // 6 - 7 | 2 Repeat Indicator: 0 = default; 3 = do not repeat any more + if ( !AddUserID(NMEA0183AISMsg, UserID) ) return false; // 8 - 37 | 30 MMSI + if ( !NMEA0183AISMsg.AddIntToPayloadBin(PartNr, 2) ) return false; // 38-39 | 2 Part Number 0-1 -> + + // Part A: 40 + 128 = len 168 + if ( !AddText(NMEA0183AISMsg, ShipName, 120) ) return false; // 40-159 | 120 Vessel Name 20 6-bit characters -> Ascii Table + if ( !NMEA0183AISMsg.AddIntToPayloadBin(0, 8) ) return false; // 160-167 | 8 Spare + + // https://www.navcen.uscg.gov/?pageName=AISMessagesB + // PART B: 40 + 128 = len 168 + if ( !NMEA0183AISMsg.AddIntToPayloadBin(VesselType, 8) ) return false; // 168-175 | 40-47 | 8 Ship Type 0....99 + if ( !AddText(NMEA0183AISMsg, VendorID, 42) ) return false; // 176-217 | 48-89 | 42 Vendor ID + Unit Model Code + Serial Number + if ( !AddText(NMEA0183AISMsg, Callsign, 42) ) return false; // 218-259 | 90-131 | 42 Call Sign WDE4178 -> 7 6-bit characters, as in Msg Type 5 + if ( !AddDimensions(NMEA0183AISMsg, Length, Beam, PosRefStbd, PosRefBow) ) return false; // 260-289 | 132-161 | 30 Dimensions + if ( !NMEA0183AISMsg.AddIntToPayloadBin(0, 6) ) return false; // 290-295 | 162-167 | 6 Spare + + return true; +} + +//****************************************************************************** +// Validations and Unit Transformations +//****************************************************************************** + +// ***************************************************************************** +// 6bit Message Type -> Constant: 1 or 3, 5, 24 etc. +bool AddMessageType(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageType) { + + if (MessageType < 0 || MessageType > 24 ) MessageType = 1; + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(MessageType, 6) ) return false; + return true; +} + +// ***************************************************************************** +// 2bit Repeat Indicator: 0 = default; 3 = do not repeat any more +bool AddRepeat(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t Repeat) { + + if (Repeat < 0 || Repeat > 3) Repeat = 0; + if ( !NMEA0183AISMsg.AddIntToPayloadBin(Repeat, 2) ) return false; + return true; +} + +// ***************************************************************************** +// 30bit UserID = MMSI (9 decimal digits) +bool AddUserID(tNMEA0183AISMsg &NMEA0183AISMsg, uint32_t UserID) { + + if (UserID < 0||UserID > 999999999) UserID = 0; + if ( !NMEA0183AISMsg.AddIntToPayloadBin(UserID, 30) ) return false; + return true; +} + +// ***************************************************************************** +// 30 bit IMO Number +// 0 = not available = default – Not applicable to SAR aircraft +// 0000000001-0000999999 not used +// 0001000000-0009999999 = valid IMO number; +// 0010000000-1073741823 = official flag state number. +bool AddIMONumber(tNMEA0183AISMsg &NMEA0183AISMsg, uint32_t &IMONumber) { + uint32_t iTemp; + ( (IMONumber >= 999999 && IMONumber <= 9999999)||(IMONumber >= 10000000 && IMONumber <= 1073741823) )? iTemp = IMONumber : iTemp = 0; + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(iTemp, 30) ) return false; + return true; +} + +// ***************************************************************************** +// 42bit Callsign alphanumeric value, max 7 six-bit characters +// 120bit Name or Destination +bool AddText(tNMEA0183AISMsg &NMEA0183AISMsg, char *FieldVal, uint8_t length) { + uint8_t len = length/6; + + if ( strlen(FieldVal) > len ) FieldVal[len] = 0; + if ( !NMEA0183AISMsg.AddEncodedCharToPayloadBin(FieldVal, length) ) return false; + return true; +} + +// ***************************************************************************** +// Calculate Dimension A, B, C, D +// double PosRefBow 240-248 | 9 [m] Dimension to Bow, reference for pos. A +// Length - PosRefBow 249-257 | 9 [m] Dimension to Stern, reference for pos. B +// Beam - PosRefStbd 258-263 | 6 [m] Dimension to Port, reference for pos. C +// PosRefStbd 264-269 | 6 [m] Dimension to Starboard, reference for pos. D +// Ship dimensions will be 0 if not available. For the dimensions to bow and stern, +// the special value 511 indicates 511 meters or greater; +// for the dimensions to port and starboard, the special value 63 indicates 63 meters or greater. +// 30 Bit +bool AddDimensions(tNMEA0183AISMsg &NMEA0183AISMsg, double Length, double Beam, double PosRefStbd, double PosRefBow) { + uint16_t _PosRefBow = 0; + uint16_t _PosRefStern = 0; + uint16_t _PosRefStbd = 0; + uint16_t _PosRefPort = 0; + + if ( PosRefBow >= 0.0 && PosRefBow <= 511.0 ) { + _PosRefBow = ceil(PosRefBow); + } else { + _PosRefBow = 511; + } + + if ( PosRefStbd >= 0.0 && PosRefStbd <= 63.0 ) { + _PosRefStbd = ceil(PosRefStbd); + } else { + _PosRefStbd = 63; + } + + if ( !N2kIsNA(Length) ) { + _PosRefStern = ceil( Length ) - _PosRefBow; + if ( _PosRefStern < 0 ) _PosRefStern = 0; + if ( _PosRefStern > 511 ) _PosRefStern = 511; + } + if ( !N2kIsNA(Beam) ) { + _PosRefPort = ceil( Beam ) - _PosRefStbd; + if ( _PosRefPort < 0 ) _PosRefPort = 0; + if ( _PosRefPort > 63 ) _PosRefPort = 63; + } + + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(_PosRefBow, 9) ) return false; + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(_PosRefStern, 9) ) return false; + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(_PosRefPort, 6) ) return false; + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(_PosRefStbd, 6) ) return false; + return true; +} + +// ***************************************************************************** +// 4 Bit Navigational Status e.g.: "Under way sailing" +// Same values used as in tN2kAISNavStatus, so we can use direct numbers +bool AddNavStatus(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t &NavStatus) { + uint8_t iTemp; + (NavStatus >= 0 && NavStatus <= 15 )? iTemp = NavStatus : iTemp = 15; + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(iTemp, 4) ) return false; + return true; +} + +// ***************************************************************************** +// 8bit [rad/s -> degree/minute] Rate of Turn ROT 128 = N/A +// 0 = not turning +// 1…126 = turning right at up to 708 degrees per minute or higher +// 1…-126 = turning left at up to 708 degrees per minute or higher +// 127 = turning right at more than 5deg/30s (No TI available) +// -127 = turning left at more than 5deg/30s (No TI available) +// 128 (80 hex) indicates no turn information available (default) +bool AddROT(tNMEA0183AISMsg &NMEA0183AISMsg, double &rot) { + int8_t iTemp; + if ( N2kIsNA(rot)) iTemp = 128; + else { + rot *= radsToDegMin; + (rot > -128.0 && rot < 128.0)? iTemp = aRoundToInt(rot) : iTemp = 128; + } + + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(iTemp, 8) ) return false; + return true; +} + +// ***************************************************************************** +// 10 bit [m/s -> kts] SOG x10, 1023 = N/A +// Speed over ground is in 0.1-knot resolution from 0 to 102 knots. +// Value 1023 indicates speed is not available, value 1022 indicates 102.2 knots or higher. +bool AddSOG (tNMEA0183AISMsg &NMEA0183AISMsg, double &sog) { + int16_t iTemp; + if ( sog < 0.0 ) iTemp = 1023; + else { + sog *= msTokn; + if (sog > 102.2) iTemp = 1023; + else iTemp = aRoundToInt( 10 * sog ); + } + + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(iTemp, 10) ) return false; + return true; +} + +// ***************************************************************************** +// 28 bit @TODO check negative values +// Values up to plus or minus 180 degrees, East = positive, West = negative. +// A value of 181 degrees (0x6791AC0 hex) indicates that longitude is not available and is the default. +// AIS Longitude is given in in 1/10000 min; divide by 600000.0 to obtain degrees. +bool AddLongitude(tNMEA0183AISMsg &NMEA0183AISMsg, double &Longitude) { + int32_t iTemp; + (Longitude >= -180.0 && Longitude <= 180.0)? iTemp = (int) (Longitude * 600000) : iTemp = 181 * 600000; + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(iTemp, 28) ) return false; + return true; +} + +// ***************************************************************************** +// 27 bit +// Values up to plus or minus 90 degrees, North = positive, South = negative. +// A value of 91 degrees (0x3412140 hex) indicates latitude is not available and is the default. +bool AddLatitude(tNMEA0183AISMsg &NMEA0183AISMsg, double &Latitude) { + int32_t iTemp; + (Latitude >= -90.0 && Latitude <= 90.0)? iTemp = (int) (Latitude * 600000) : iTemp = 91 * 600000; + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(iTemp, 27) ) return false; + return true; +} + +// **************************************************************************** +// 9 bit True Heading (HDG) 0 to 359 degrees, 511 = not available. +bool AddHeading (tNMEA0183AISMsg &NMEA0183AISMsg, double &heading) { + uint16_t iTemp; + if ( N2kIsNA(heading) ) iTemp = 511; + else { + heading *= radToDeg; + (heading >= 0.0 && heading <= 359.0 )? iTemp = aRoundToInt( heading ) : iTemp = 511; + } + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(iTemp, 9) ) return false; + return true; +} + +// ***************************************************************************** +// 12bit Relative to true north, to 0.1 degree precision +bool AddCOG(tNMEA0183AISMsg &NMEA0183AISMsg, double cog) { + int16_t iTemp; + cog *= radToDeg; + if ( cog >= 0.0 && cog < 360.0 ) { iTemp = aRoundToInt( cog * 10 ); } else { iTemp = 3600; } + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(iTemp, 12) ) return false; + return true; +} + +// ***************************************************************************** +// 6bit Seconds in UTC timestamp should be 0-59, except for these special values: +// 60 if time stamp is not available (default) +// 61 if positioning system is in manual input mode +// 62 if Electronic Position Fixing System operates in estimated (dead reckoning) mode, +// 63 if the positioning system is inoperative. +bool AddSeconds (tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t &Seconds) { + uint8_t iTemp; + (Seconds >= 0 && Seconds <= 63 )? iTemp = Seconds : iTemp = 60; + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(iTemp, 6) ) return false; + return true; +} + +// ***************************************************************************** +// 4 bit Position Fix Type, See "EPFD Fix Types" 0 (default) +bool AddEPFDFixType(tNMEA0183AISMsg &NMEA0183AISMsg, tN2kGNSStype &GNSStype) { + // Translate tN2kGNSStype to AIS conventions + // 3 & 4 not defined in AIS -> we take 1 for GPS + uint8_t fixType = 0; + switch (GNSStype) { + case 0: // GPS + case 3: // GPS+SBAS/WAAS + case 4: // GPS+SBAS/WAAS+GLONASS + fixType = 1; break; + case 1: // GLONASS + fixType = 2; break; + case 2: // GPS+GLONASS + fixType = 3; break; + case 5: // Chayka + fixType = 5; break; + case 6: // integrated + fixType = 6; break; + case 7: // surveyed + fixType = 7; break; + case 8: // Galileo + fixType = 8; break; + default: + fixType = 0; + } + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(fixType, 4) ) return false; + return true; +} + +// ***************************************************************************** +// 8 bit Maxiumum present static draught +// In 1/10 m, 255 = draught 25.5 m or greater, 0 = not available = default; in accordance with IMO Resolution A.851 +bool AddStaticDraught(tNMEA0183AISMsg &NMEA0183AISMsg, double &Draught) { + uint8_t staticDraught; + if ( N2kIsNA(Draught) ) staticDraught = 0; + else if (Draught < 0.0) staticDraught = 0; + else if (Draught>25.5) staticDraught = 255; + else staticDraught = (int) ceil( 10.0 * Draught); + + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(staticDraught, 8) ) return false; + return true; +} + +// ***************************************************************************** +// 20bit Estimated time of arrival; MMDDHHMM UTC +// 4 Bits 19-16: month; 1-12; 0 = not available = default +// 5 Bits 15-11: day; 1-31; 0 = not available = default +// 5 Bits 10-6: hour; 0-23; 24 = not available = default +// 6 Bits 5-0: minute; 0-59; 60 = not available = default +// N2k Field #7: ETA Time - Seconds since midnight Bits: 32 Units: s +// Type: Time Resolution: 0.0001 Signed: false e.g. 36000.00 +// N2k Field #8: ETA Date - Days since January 1, 1970 Bits: 16 +// Units: days Type: Date Resolution: 1 Signed: false e.g. 18184 +bool AddETADateTime(tNMEA0183AISMsg &NMEA0183AISMsg, uint16_t &ETAdate, double &ETAtime) { + + uint8_t month = 0; + uint8_t day = 0; + uint8_t hour = 24; + uint8_t minute = 60; + + if (!N2kIsNA(ETAdate) && ETAdate > 0 ) { + tmElements_t tm; + #ifndef _Time_h + time_t t=NMEA0183AISMsg.daysToTime_t(ETAdate); + #else + time_t t=ETAdate*86400; + #endif + NMEA0183AISMsg.breakTime(t, tm); + month = (uint8_t) NMEA0183AISMsg.GetMonth(tm); + day = (uint8_t) NMEA0183AISMsg.GetDay(tm); + } + if ( !N2kIsNA(ETAtime) && ETAtime >= 0 ) { + double temp = ETAtime / 3600; + hour = (int) temp; + minute = (int) ((temp - hour) * 60); + } else { + hour = 24; + minute = 60; + } + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(month, 4) ) return false; + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(day, 5) ) return false; + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(hour, 5) ) return false; + if ( ! NMEA0183AISMsg.AddIntToPayloadBin(minute, 6) ) return false; + return true; +} diff --git a/lib/nmea2ktoais/NMEA0183AISMessages.h b/lib/nmea2ktoais/NMEA0183AISMessages.h new file mode 100644 index 0000000..0c32ac8 --- /dev/null +++ b/lib/nmea2ktoais/NMEA0183AISMessages.h @@ -0,0 +1,86 @@ +/* +NMEA0183AISMessages.h + +Copyright (c) 2019 Ronnie Zeiller, www.zeiller.eu + +Based on the works of Timo Lappalainen and Eric S. Raymond and Kurt Schwehr https://gpsd.gitlab.io/gpsd/AIVDM.html + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef _tNMEA0183AISMessages_H_ +#define _tNMEA0183AISMessages_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_SHIP_IN_VECTOR 200 +class ship { +public: + uint32_t _userID; + std::string _shipName; + + ship(uint32_t UserID, std::string ShipName) : _userID(UserID), _shipName(ShipName) {} +}; + +extern std::vector vships; + +// Types 1, 2 and 3: Position Report Class A or B +bool SetAISClassABMessage1(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageType, uint8_t Repeat, + uint32_t UserID, double Latitude, double Longitude, bool Accuracy, bool RAIM, uint8_t Seconds, + double COG, double SOG, double Heading, double ROT, uint8_t NavStatus); + +//***************************************************************************** +// AIS Class A Static and Voyage Related Data Message Type 5 +bool SetAISClassAMessage5(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageID, uint8_t Repeat, + uint32_t UserID, uint32_t IMONumber, char *Callsign, char *Name, + uint8_t VesselType, double Length, double Beam, double PosRefStbd, + double PosRefBow, uint16_t ETAdate, double ETAtime, double Draught, + char *Destination, tN2kGNSStype GNSStype, uint8_t DTE ); + +//***************************************************************************** +// AIS position report (class B 129039) -> Standard Class B CS Position Report Message Type 18 Part B +bool SetAISClassBMessage18(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageID, uint8_t Repeat, uint32_t UserID, + double Latitude, double Longitude, bool Accuracy, bool RAIM, + uint8_t Seconds, double COG, double SOG, double Heading, tN2kAISUnit Unit, + bool Display, bool DSC, bool Band, bool Msg22, bool Mode, bool State); + +//***************************************************************************** +// Static Data Report Class B, Message Type 24 +// PGN 129809 Handle AIS Class B "CS" Static Data Report, Part A +bool SetAISClassBMessage24PartA(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageID, uint8_t Repeat, uint32_t UserID, char *Name); + +//***************************************************************************** +// Static Data Report Class B, Message Type 24 +bool SetAISClassBMessage24(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageID, uint8_t Repeat, + uint32_t UserID, uint8_t VesselType, char *VendorID, char *Callsign, + double Length, double Beam, double PosRefStbd, double PosRefBow, uint32_t MothershipID ); + +inline int32_t aRoundToInt(double x) { + return x >= 0 + ? (int32_t) floor(x + 0.5) + : (int32_t) ceil(x - 0.5); +} +#endif diff --git a/lib/nmea2ktoais/NMEA0183AISMsg.cpp b/lib/nmea2ktoais/NMEA0183AISMsg.cpp new file mode 100644 index 0000000..e3d0d78 --- /dev/null +++ b/lib/nmea2ktoais/NMEA0183AISMsg.cpp @@ -0,0 +1,299 @@ +/* +NMEA0183AISMsg.cpp + +Copyright (c) 2019 Ronnie Zeiller, www.zeiller.eu +Based on the works of Timo Lappalainen NMEA2000 and NMEA0183 Library + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "NMEA0183AISMsg.h" +#include +#include +#include +#include +#include +#include +#include + +const char AsciiChar[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !\"#$%&\'()*+,-./0123456789:;<=>?"; +const char *tNMEA0183AISMsg::EmptyAISField = "000000"; + +//***************************************************************************** +tNMEA0183AISMsg::tNMEA0183AISMsg() { + ClearAIS(); +} + +//***************************************************************************** +void tNMEA0183AISMsg::ClearAIS() { + + PayloadBin[0]=0; + Payload[0]=0; + iAddPldBin=0; + iAddPld=0; +} + +//***************************************************************************** +// Add 6bit with no data. +bool tNMEA0183AISMsg::AddEmptyFieldToPayloadBin(uint8_t iBits) { + + if ( (iAddPldBin + iBits * 6) >= AIS_BIN_MAX_LEN ) return false; // Is there room for any data + + for (uint8_t i=0;i= AIS_BIN_MAX_LEN ) return false; // Is there room for any data + + bset = ival; + + PayloadBin[iAddPldBin]=0; + uint16_t iAdd=iAddPldBin; + + char buf[1]; + for(int i = countBits-1; i >= 0 ; i--) { + sprintf(buf, "%d", (int) bset[i]); + PayloadBin[iAdd] = buf[0]; + iAdd++; + } + + iAddPldBin += countBits; + PayloadBin[iAddPldBin]=0; + + return true; +} + +// **************************************************************************** +bool tNMEA0183AISMsg::AddBoolToPayloadBin(bool &bval, uint8_t size) { + int8_t iTemp; + (bval == true)? iTemp = 1 : iTemp = 0; + if ( ! AddIntToPayloadBin(iTemp, size) ) return false; + return true; +} + +// ***************************************************************************** +// converts sval into binary 6-bit AScii encoded string and appends it to PayloadBin +// filled up with "@" == "000000" to given bit-size +bool tNMEA0183AISMsg::AddEncodedCharToPayloadBin(char *sval, size_t countBits) { + + if ( (iAddPldBin + countBits ) >= AIS_BIN_MAX_LEN ) return false; // Is there room for any data + + PayloadBin[iAddPldBin]=0; + std::bitset<6> bs; + char * ptr; + size_t len = strlen(sval); // e.g.: should be 7 for Callsign + if ( len * 6 > countBits ) len = countBits / 6; + + for (int i = 0; i= 0){ + AddIntToPayloadBin(index, 6); + } + } else { + AddIntToPayloadBin(0, 6); + } + } + + PayloadBin[iAddPldBin+1]=0; + + // fill up with "@", also covers empty sval + if ( len * 6 < countBits ) { + for (int i=0;i<(countBits/6-len);i++) { + AddIntToPayloadBin(0, 6); + } + } + PayloadBin[iAddPldBin]=0; + return true; +} + +// ***************************************************************************** +bool tNMEA0183AISMsg::ConvertBinaryAISPayloadBinToAscii(const char *payloadbin) { + uint16_t len; + + len = strlen( payloadbin ) / 6; // 28 + uint32_t offset; + char s[7]; + uint8_t dec; + int i; + for ( i=0; i +#include +#include +#include +#include +#include +#include + + +#ifndef AIS_MSG_MAX_LEN +#define AIS_MSG_MAX_LEN 100 // maximum length of AIS Payload +#endif + +#ifndef AIS_BIN_MAX_LEN +#define AIS_BIN_MAX_LEN 500 // maximum length of AIS Binary Payload (before encoding to Ascii) +#endif + +#define BITSET_LENGTH 120 + +class tNMEA0183AISMsg : public tNMEA0183Msg { + + protected: // AIS-NMEA + std::bitset bset; + static const char *EmptyAISField; // 6bits 0 not used yet..... + static const char *AsciChar; + + uint16_t iAddPldBin; + char Payload[AIS_MSG_MAX_LEN]; + uint8_t iAddPld; + + public: + char PayloadBin[AIS_BIN_MAX_LEN]; + char PayloadBin2[AIS_BIN_MAX_LEN]; + // Clear message + void ClearAIS(); + + public: + tNMEA0183AISMsg(); + const char *GetPayload(); + const char *GetPayloadType5_Part1(); + const char *GetPayloadType5_Part2(); + const char *GetPayloadType24_PartA(); + const char *GetPayloadType24_PartB(); + const char *GetPayloadBin() const { return PayloadBin; } + + const tNMEA0183AISMsg& BuildMsg5Part1(tNMEA0183AISMsg &AISMsg); + const tNMEA0183AISMsg& BuildMsg5Part2(tNMEA0183AISMsg &AISMsg); + const tNMEA0183AISMsg& BuildMsg24PartA(tNMEA0183AISMsg &AISMsg); + const tNMEA0183AISMsg& BuildMsg24PartB(tNMEA0183AISMsg &AISMsg); + + // Generally Used + bool AddIntToPayloadBin(int32_t ival, uint16_t countBits); + bool AddBoolToPayloadBin(bool &bval, uint8_t size); + bool AddEncodedCharToPayloadBin(char *sval, size_t Length); + bool AddEmptyFieldToPayloadBin(uint8_t iBits); + bool ConvertBinaryAISPayloadBinToAscii(const char *payloadbin); + + // AIS Helper functions + protected: + inline int32_t aRoundToInt(double x) { + return (x >= 0) ? (int32_t) floor(x + 0.5) : (int32_t) ceil(x - 0.5); + } +}; +#endif diff --git a/lib/nmea2ktoais/README.md b/lib/nmea2ktoais/README.md new file mode 100644 index 0000000..9d83553 --- /dev/null +++ b/lib/nmea2ktoais/README.md @@ -0,0 +1,49 @@ +# NMEA2000 -> NMEA0183 AIS converter v1.0.0 + +Import from https://github.com/ronzeiller/NMEA0183-AIS + +NMEA0183 AIS library © Ronnie Zeiller, www.zeiller.eu + +Addendum for NMEA2000 and NMEA0183 Library from Timo Lappalainen https://github.com/ttlappalainen + + +## Conversions: + +- NMEA2000 PGN 129038 => AIS CLASS A Position Report (Message Type 1) 1.) 2.) 3.) +- NMEA2000 PGN 129039 => AIS Class B Position Report, Message Type 18 +- NMEA2000 PGN 129794 => AIS Class A Ship Static and Voyage related data, Message Type 5 4.) +- NMEA2000 PGN 129809 => AIS Class B "CS" Static Data Report, making a list of UserID (MMSI) and Ship Names used for Message 24 Part A +- NMEA2000 PGN 129810 => AIS Class B "CS" Static Data Report, Message 24 Part A+B + +### Remarks +1. Message Type could be set to 1 or 3 (identical messages) on demand +2. Maneuver Indicator (not part of NMEA2000 PGN 129038) => will be set to 0 (default) +3. Radio Status (not part of NMEA2000 PGN 129038) => will be set to 0 +4. AIS Version (not part of NMEA2000 PGN 129794) => will be set to 1 + +## Dependencies + +To use this library you need also: + + - NMEA2000 library + + - NMEA0183 library + + - Related CAN libraries. + +## License + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.