initial import of ais stuff

This commit is contained in:
andreas 2021-10-21 19:38:57 +02:00
parent 63eb317816
commit c63a9bb689
6 changed files with 1098 additions and 0 deletions

View File

@ -11,6 +11,7 @@
#include <math.h>
#include "N2kDataToNMEA0183.h"
#include <map>
#include "NMEA0183AISMessages.h"
class N2kToNMEA0183Functions : public N2kDataToNMEA0183
{
typedef void (N2kToNMEA0183Functions::*N2KConverter)(const tN2kMsg &N2kMsg);

View File

@ -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 <NMEA0183AISMessages.h>
#include <N2kTypes.h>
#include <N2kMsg.h>
#include <string.h>
//#include <bitset>
//#include <unordered_map>
#include <sstream>
#include <math.h>
#include <NMEA0183AISMsg.h>
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<ship *> 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<char*>( 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;
}

View File

@ -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 <stdio.h>
#include <time.h>
#include <string.h>
#include <N2kTypes.h>
#include <NMEA0183AISMsg.h>
#include <stddef.h>
#include <vector>
#include <string>
#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<ship *> 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

View File

@ -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 <NMEA0183Msg.h>
#include <Arduino.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <cstdio>
#include <sstream>
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<iBits;i++) {
strncpy(PayloadBin+iAddPldBin, EmptyAISField, 6);
iAddPldBin+=6;
}
return true;
}
//*****************************************************************************
bool tNMEA0183AISMsg::AddIntToPayloadBin(int32_t ival, uint16_t countBits) {
if ( (iAddPldBin + countBits ) >= 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<len; i++) {
ptr = strchr(AsciiChar, sval[i]);
if ( ptr ) {
int16_t index = ptr - AsciiChar;
if (index >= 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<len; i++ ) {
offset = i * 6;
int k = 0;
for (int j=offset; j<offset+6; j++ ) {
s[k] = payloadbin[j];
k++;
}
s[k]=0;
dec = strtoull (s, NULL, 2); //binToDec
if (dec < 40 ) dec += 48;
else dec += 56;
char c = dec;
Payload[i] = c;
}
Payload[i]=0;
return true;
}
//********************** BUILD 2-parted AIS Sentences ************************
const tNMEA0183AISMsg& tNMEA0183AISMsg::BuildMsg5Part1(tNMEA0183AISMsg &AISMsg) {
Init("VDM", "AI", '!');
AddStrField("2");
AddStrField("1");
AddStrField("5");
AddStrField("A");
AddStrField( GetPayloadType5_Part1() );
AddStrField("0");
return AISMsg;
}
const tNMEA0183AISMsg& tNMEA0183AISMsg::BuildMsg5Part2(tNMEA0183AISMsg &AISMsg) {
Init("VDM", "AI", '!');
AddStrField("2");
AddStrField("2");
AddStrField("5");
AddStrField("A");
AddStrField( GetPayloadType5_Part2() );
AddStrField("2"); // Message 5, Part 2 has always 2 Padding Zeros
return AISMsg;
}
const tNMEA0183AISMsg& tNMEA0183AISMsg::BuildMsg24PartA(tNMEA0183AISMsg &AISMsg) {
Init("VDM", "AI", '!');
AddStrField("1");
AddStrField("1");
AddEmptyField();
AddStrField("A");
AddStrField( GetPayloadType24_PartA() );
AddStrField("0");
return AISMsg;
}
const tNMEA0183AISMsg& tNMEA0183AISMsg::BuildMsg24PartB(tNMEA0183AISMsg &AISMsg) {
Init("VDM", "AI", '!');
AddStrField("1");
AddStrField("1");
AddEmptyField();
AddStrField("A");
AddStrField( GetPayloadType24_PartB() );
AddStrField("0"); // Message 24, both parts have always Zero Padding
return AISMsg;
}
//******************************* AIS PAYLOADS *********************************
//******************************************************************************
// get converted Payload for Message 1, 2, 3 & 18, always Length 168
const char *tNMEA0183AISMsg::GetPayload() {
uint16_t lenbin = strlen( PayloadBin);
if ( lenbin != 168 ) return nullptr;
if ( !ConvertBinaryAISPayloadBinToAscii( PayloadBin ) ) return nullptr;
return Payload;
}
//******************************************************************************
// get converted Part 1 of Payload for Message 5
const char *tNMEA0183AISMsg::GetPayloadType5_Part1() {
uint16_t lenbin = strlen( PayloadBin);
if ( lenbin != 424 ) return nullptr;
char *to = (char*) malloc(337);
strncpy(to, PayloadBin, 336); // First Part is always 336 Length
to[336]=0;
if ( !ConvertBinaryAISPayloadBinToAscii( to ) ) return nullptr;
return Payload;
}
//******************************************************************************
// get converted Part 2 of Payload for Message 5
const char *tNMEA0183AISMsg::GetPayloadType5_Part2() {
uint16_t lenbin = strlen( PayloadBin);
if ( lenbin != 424 ) return nullptr;
lenbin = 88; // Second Part is always 424 - 336 + 2 padding Zeros in Length
char *to = (char*) malloc(91);
strncpy(to, PayloadBin + 336, lenbin);
to[88]='0'; to[89]='0'; to[90]=0;
if ( !ConvertBinaryAISPayloadBinToAscii( to ) ) return nullptr;
return Payload;
}
//******************************************************************************
// get converted Part A of Payload for Message 24
// Bit 0.....167, len 168
// In PayloadBin is Part A and Part B chained together with Length 296
const char *tNMEA0183AISMsg::GetPayloadType24_PartA() {
uint16_t lenbin = strlen( PayloadBin);
if ( lenbin != 296 ) return nullptr; // too short for Part A
char *to = (char*) malloc(169); // Part A has Length 168
*to = '\0';
for (int i=0; i<168; i++){
to[i] = PayloadBin[i];
}
to[168]=0;
if ( !ConvertBinaryAISPayloadBinToAscii( to ) ) return nullptr;
return Payload;
}
//******************************************************************************
// get converted Part B of Payload for Message 24
// Bit 0.....38 + bit39='1' (part number) + bit 168........295 296='\0' of total PayloadBin
// binary part B: len 40 + 128 = len 168
const char *tNMEA0183AISMsg::GetPayloadType24_PartB() {
uint16_t lenbin = strlen( PayloadBin);
if ( lenbin != 296 ) return nullptr; // too short for Part B
char *to = (char*) malloc(169); // Part B has Length 168
*to = '\0';
for (int i=0; i<39; i++){
to[i] = PayloadBin[i];
}
to[39] = 49; // part number 1
for (int i=40; i<168; i++) {
to[i] = PayloadBin[i+128];
}
to[168]=0;
if ( !ConvertBinaryAISPayloadBinToAscii( to ) ) return nullptr;
return Payload;
}

View File

@ -0,0 +1,92 @@
/*
NMEA0183AISMsg.h
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.
*/
#ifndef _tNMEA0183AISMsg_H_
#define _tNMEA0183AISMsg_H_
#include <NMEA0183Msg.h>
#include <string.h>
#include <time.h>
#include <bitset>
#include <stdint.h>
#include <math.h>
#include <string>
#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<BITSET_LENGTH> 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

49
lib/nmea2ktoais/README.md Normal file
View File

@ -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.