mirror of
https://github.com/thooge/esp32-nmea2000-obp60.git
synced 2026-02-11 07:03:07 +01:00
move the nmea2ktoais functions back into our code base.
This commit is contained in:
601
lib/nmea2ktoais/NMEA0183AISMessages.cpp
Normal file
601
lib/nmea2ktoais/NMEA0183AISMessages.cpp
Normal file
@@ -0,0 +1,601 @@
|
||||
/*
|
||||
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]
|
||||
|
||||
|
||||
// ************************ 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) ) 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) ) 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.InitAis()) return false;
|
||||
int padBits=0;
|
||||
if ( !NMEA0183AISMsg.AddStrField( NMEA0183AISMsg.GetPayloadFix(padBits) ) ) return false;
|
||||
if ( !NMEA0183AISMsg.AddUInt32Field(padBits) ) return false;
|
||||
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,
|
||||
tN2kAISVersion AISversion) {
|
||||
|
||||
// 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((uint32_t)AISversion, 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
|
||||
// PGN129039
|
||||
// ParseN2kAISClassBPosition(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, tN2kAISTransceiverInformation &AISTransceiverInformation,
|
||||
// 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)) 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) ) return false; // 143 | 1 If 1, unit is attached to a VHF voice radio with DSC capability.
|
||||
if ( !NMEA0183AISMsg.AddBoolToPayloadBin(Band) ) return false; // 144 | 1 If this flag is 1, the unit can use any part of the marine channel.
|
||||
if ( !NMEA0183AISMsg.AddBoolToPayloadBin(Msg22)) return false; // 145 | 1 If 1, unit can accept a channel assignment via Message Type 22.
|
||||
if ( !NMEA0183AISMsg.AddBoolToPayloadBin(Mode) ) return false; // 146 | 1 Assigned-mode flag: 0 = autonomous mode (default), 1 = assigned mode
|
||||
if ( !NMEA0183AISMsg.AddBoolToPayloadBin(RAIM) ) 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.InitAis()) return false;
|
||||
int padBits=0;
|
||||
if ( !NMEA0183AISMsg.AddStrField( NMEA0183AISMsg.GetPayloadFix(padBits) ) ) return false;
|
||||
if ( !NMEA0183AISMsg.AddUInt32Field(padBits) ) return false;
|
||||
|
||||
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) {
|
||||
// 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(0, 2) ) return false; // 38-39 | 2 Part Number 0-1 ->
|
||||
// Part A: 40 + 128 = len 168
|
||||
if ( !AddText(NMEA0183AISMsg, Name, 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
|
||||
if ( !NMEA0183AISMsg.InitAis() ) return false;
|
||||
int padBits=0;
|
||||
if ( !NMEA0183AISMsg.AddStrField( NMEA0183AISMsg.GetPayloadFix(padBits) ) ) return false;
|
||||
if ( !NMEA0183AISMsg.AddUInt32Field(padBits) ) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ***************************************************************************************************************
|
||||
bool SetAISClassBMessage24PartB(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 ) {
|
||||
|
||||
|
||||
// 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(1, 2) ) return false; // 38-39 | 2 Part Number 0-1 ->
|
||||
|
||||
// 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
|
||||
if ( !NMEA0183AISMsg.InitAis() ) return false;
|
||||
int padBits=0;
|
||||
if ( !NMEA0183AISMsg.AddStrField( NMEA0183AISMsg.GetPayloadFix(padBits) ) ) return false;
|
||||
if ( !NMEA0183AISMsg.AddUInt32Field(padBits) ) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ****************************************************************************
|
||||
// AIS ATON report (129041) -> Type 21: Position and status report for aids-to-navigation
|
||||
// PGN129041
|
||||
|
||||
bool SetAISMessage21(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t Repeat, uint32_t UserID,
|
||||
double Latitude, double Longitude, bool Accuracy, bool RAIM,
|
||||
uint8_t Seconds, double Length, double Beam, double PositionReferenceStarboard,
|
||||
double PositionReferenceTrueNord, tN2kAISAtoNType Type, bool OffPositionIndicator,
|
||||
bool VirtualAtoNFlag, bool AssignedModeFlag, tN2kGNSStype GNSSType, uint8_t AtoNStatus,
|
||||
char * atonName ) {
|
||||
//
|
||||
NMEA0183AISMsg.ClearAIS();
|
||||
if ( !AddMessageType(NMEA0183AISMsg, 21) ) 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(Type,5)) return false; // | 5 aid type
|
||||
//the name must be split:
|
||||
//if it's > 120 bits the rest goes to the last parameter
|
||||
if ( !NMEA0183AISMsg.AddEncodedCharToPayloadBin(atonName,120))
|
||||
return false; // | 120 name
|
||||
if ( !NMEA0183AISMsg.AddBoolToPayloadBin(Accuracy) ) return false; // | 1 accuracy
|
||||
if ( !AddLongitude(NMEA0183AISMsg,Longitude)) return false; // | 28 lon
|
||||
if ( !AddLatitude(NMEA0183AISMsg,Latitude)) return false; // | 27 lat
|
||||
if ( !AddDimensions(NMEA0183AISMsg, Length, Beam,
|
||||
PositionReferenceStarboard, PositionReferenceTrueNord)) return false; // | 30 dim
|
||||
if ( !AddEPFDFixType(NMEA0183AISMsg,GNSSType)) return false; // | 4 fix type
|
||||
if ( !AddSeconds(NMEA0183AISMsg,Seconds)) return false; // | 6 second
|
||||
if ( !NMEA0183AISMsg.AddBoolToPayloadBin(OffPositionIndicator))
|
||||
return false; // | 1 off
|
||||
if ( !NMEA0183AISMsg.AddIntToPayloadBin(0,8)) return false; // | 8 reserverd
|
||||
if ( !NMEA0183AISMsg.AddBoolToPayloadBin(RAIM)) return false; // | 1 raim
|
||||
if ( !NMEA0183AISMsg.AddBoolToPayloadBin(VirtualAtoNFlag))
|
||||
return false; // | 1 virt
|
||||
if ( !NMEA0183AISMsg.AddBoolToPayloadBin(AssignedModeFlag))
|
||||
return false; // | 1 assigned
|
||||
if ( !NMEA0183AISMsg.AddIntToPayloadBin(0,1)) return false; // | 1 spare
|
||||
size_t l=strlen(atonName);
|
||||
if (l >=20){
|
||||
uint8_t bitlen=(l-20)*6;
|
||||
if (bitlen > 88) bitlen=88;
|
||||
if ( !NMEA0183AISMsg.AddEncodedCharToPayloadBin(atonName+20,bitlen)) return false; // | name
|
||||
}
|
||||
if ( !NMEA0183AISMsg.InitAis() ) return false;
|
||||
int padBits=0;
|
||||
if ( !NMEA0183AISMsg.AddStrField( NMEA0183AISMsg.GetPayload(padBits) ) ) return false;
|
||||
if ( !NMEA0183AISMsg.AddUInt32Field(padBits) ) return false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
87
lib/nmea2ktoais/NMEA0183AISMessages.h
Normal file
87
lib/nmea2ktoais/NMEA0183AISMessages.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
|
||||
// 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,
|
||||
tN2kAISVersion AISversion);
|
||||
|
||||
//*****************************************************************************
|
||||
// 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 SetAISClassBMessage24PartB(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 );
|
||||
|
||||
//*****************************************************************************
|
||||
// Aton class 21
|
||||
bool SetAISMessage21(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t Repeat, uint32_t UserID,
|
||||
double Latitude, double Longitude, bool Accuracy, bool RAIM,
|
||||
uint8_t Seconds, double Length, double Beam, double PositionReferenceStarboard,
|
||||
double PositionReferenceTrueNord, tN2kAISAtoNType Type, bool OffPositionIndicator,
|
||||
bool VirtualAtoNFlag, bool AssignedModeFlag, tN2kGNSStype GNSSType, uint8_t AtoNStatus,
|
||||
char * atonName );
|
||||
|
||||
inline int32_t aRoundToInt(double x) {
|
||||
return x >= 0
|
||||
? (int32_t) floor(x + 0.5)
|
||||
: (int32_t) ceil(x - 0.5);
|
||||
}
|
||||
#endif
|
||||
201
lib/nmea2ktoais/NMEA0183AISMsg.cpp
Normal file
201
lib/nmea2ktoais/NMEA0183AISMsg.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
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() {
|
||||
|
||||
Payload[0]=0;
|
||||
PayloadBin.reset();
|
||||
iAddPldBin=0;
|
||||
iAddPld=0;
|
||||
}
|
||||
|
||||
|
||||
//*****************************************************************************
|
||||
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;
|
||||
|
||||
uint16_t iAdd=iAddPldBin;
|
||||
|
||||
for(int i = countBits-1; i >= 0 ; i--) {
|
||||
PayloadBin[iAdd]=bset [i];
|
||||
iAdd++;
|
||||
}
|
||||
|
||||
iAddPldBin += countBits;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//****************************************************************************
|
||||
bool tNMEA0183AISMsg::AddBoolToPayloadBin(bool &bval) {
|
||||
if ( (iAddPldBin + 1 ) >= AIS_BIN_MAX_LEN ) return false;
|
||||
PayloadBin[iAddPldBin]=bval;
|
||||
iAddPldBin++;
|
||||
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
|
||||
|
||||
const char * ptr;
|
||||
size_t len = strlen(sval); // e.g.: should be 7 for Callsign
|
||||
if ( len * 6 > countBits ) len = countBits / 6;
|
||||
|
||||
for (size_t 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);
|
||||
}
|
||||
}
|
||||
// fill up with "@", also covers empty sval
|
||||
if ( len * 6 < countBits ) {
|
||||
for (size_t i=0;i<(countBits/6-len);i++) {
|
||||
AddIntToPayloadBin(0, 6);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
template <unsigned int S>
|
||||
int tNMEA0183AISMsg::ConvertBinaryAISPayloadBinToAscii(std::bitset<S> &src,uint16_t maxSize,uint16_t bitSize,uint16_t stoffset) {
|
||||
Payload[0]='\0';
|
||||
uint16_t slen=maxSize;
|
||||
if (stoffset >= slen) return 0;
|
||||
slen-=stoffset;
|
||||
uint16_t bitLen=bitSize > 0?bitSize:slen;
|
||||
uint16_t len= bitLen / 6;
|
||||
if ((len * 6) < bitLen) len+=1;
|
||||
uint16_t padBits=0;
|
||||
uint32_t offset;
|
||||
std::bitset<6> s;
|
||||
uint8_t dec;
|
||||
int i;
|
||||
for ( i=0; i<len; i++ ) {
|
||||
offset = i * 6;
|
||||
int k = 5;
|
||||
for (uint32_t j=offset; j<offset+6; j++ ) {
|
||||
if (j < slen){
|
||||
s[k] = src[stoffset+j];
|
||||
}
|
||||
else{
|
||||
s[k] = 0;
|
||||
padBits++;
|
||||
}
|
||||
k--;
|
||||
}
|
||||
dec = s.to_ulong();
|
||||
|
||||
if (dec < 40 ) dec += 48;
|
||||
else dec += 56;
|
||||
char c = dec;
|
||||
Payload[i] = c;
|
||||
}
|
||||
Payload[i]=0;
|
||||
|
||||
return padBits;
|
||||
}
|
||||
|
||||
void tNMEA0183AISMsg::SetChannelAndTalker(bool channelA,bool own){
|
||||
channel[0]=channelA?'A':'B';
|
||||
strcpy(talker,own?"VDO":"VDM");
|
||||
}
|
||||
|
||||
//********************** BUILD 2-parted AIS Sentences ************************
|
||||
bool tNMEA0183AISMsg::InitAis(int max,int number,int sequence){
|
||||
if ( !Init(talker,"AI", '!') ) return false;
|
||||
if ( !AddUInt32Field(max) ) return false;
|
||||
if ( !AddUInt32Field(number) ) return false;
|
||||
if (sequence >= 0){
|
||||
if ( !AddUInt32Field(sequence) ) return false;
|
||||
}
|
||||
else{
|
||||
if ( !AddEmptyField() ) return false;
|
||||
}
|
||||
if ( !AddStrField(channel) ) return false;
|
||||
return true;
|
||||
}
|
||||
bool tNMEA0183AISMsg::BuildMsg5Part1() {
|
||||
if ( iAddPldBin != 424 ) return false;
|
||||
InitAis(2,1,5);
|
||||
int padBits=0;
|
||||
AddStrField( GetPayload(padBits,0,336));
|
||||
AddUInt32Field(padBits);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tNMEA0183AISMsg::BuildMsg5Part2() {
|
||||
if ( iAddPldBin != 424 ) return false;
|
||||
InitAis(2,2,5);
|
||||
int padBits=0;
|
||||
AddStrField( GetPayload(padBits,336,88) );
|
||||
AddUInt32Field(padBits);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//******************************* AIS PAYLOADS *********************************
|
||||
// get converted Payload for Message 1, 2, 3 & 18, always Length 168
|
||||
const char *tNMEA0183AISMsg::GetPayloadFix(int &padBits,uint16_t fixLen){
|
||||
uint16_t lenbin = iAddPldBin;
|
||||
if ( lenbin != fixLen ) return nullptr;
|
||||
return GetPayload(padBits,0,0);
|
||||
}
|
||||
const char *tNMEA0183AISMsg::GetPayload(int &padBits,uint16_t offset,uint16_t bitLen) {
|
||||
padBits=ConvertBinaryAISPayloadBinToAscii<AIS_BIN_MAX_LEN>(PayloadBin,iAddPldBin, bitLen,offset );
|
||||
return Payload;
|
||||
}
|
||||
|
||||
97
lib/nmea2ktoais/NMEA0183AISMsg.h
Normal file
97
lib/nmea2ktoais/NMEA0183AISMsg.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
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;
|
||||
char talker[4]="VDM";
|
||||
char channel[2]="A";
|
||||
std::bitset<AIS_BIN_MAX_LEN> PayloadBin;
|
||||
public:
|
||||
// Clear message
|
||||
void ClearAIS();
|
||||
|
||||
public:
|
||||
tNMEA0183AISMsg();
|
||||
const char *GetPayloadFix(int &padBits,uint16_t fixLen=168);
|
||||
const char *GetPayload(int &padBits,uint16_t offset=0,uint16_t bitLen=0);
|
||||
|
||||
bool BuildMsg5Part1();
|
||||
bool BuildMsg5Part2();
|
||||
bool InitAis(int max=1,int number=1,int sequence=-1);
|
||||
|
||||
// Generally Used
|
||||
bool AddIntToPayloadBin(int32_t ival, uint16_t countBits);
|
||||
bool AddBoolToPayloadBin(bool &bval);
|
||||
bool AddEncodedCharToPayloadBin(char *sval, size_t Length);
|
||||
/**
|
||||
* @param channelA - if set A, otherwise B
|
||||
* @param own - if set VDO, else VDM
|
||||
*/
|
||||
void SetChannelAndTalker(bool channelA,bool own=false);
|
||||
/**
|
||||
* convert the payload to ascii
|
||||
* return the number of padding bits
|
||||
* @param bitSize the number of bits to be used, 0 - use all bits
|
||||
*/
|
||||
template <unsigned int SZ>
|
||||
int ConvertBinaryAISPayloadBinToAscii(std::bitset<SZ> &src,uint16_t maxSize, uint16_t bitSize,uint16_t offset=0);
|
||||
|
||||
// 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
|
||||
73
lib/nmea2ktoais/README.md
Normal file
73
lib/nmea2ktoais/README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# NMEA2000 to NMEA0183 AIS Converter
|
||||
|
||||
|
||||
NMEA0183 AIS library © Ronnie Zeiller, www.zeiller.eu
|
||||
|
||||
Addendum for NMEA2000 and NMEA0183 Library from Timo Lappalainen https://github.com/ttlappalainen
|
||||
|
||||
to get NMEA0183 AIS data from N2k-bus
|
||||
|
||||
## 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
|
||||
|
||||
### Versions
|
||||
1.0.6 2024-03-25
|
||||
- fixed to work with Timo´s NMEA2000 v4.21.3
|
||||
|
||||
1.0.5 2023-12-02
|
||||
- removed VDO remote print statements
|
||||
|
||||
1.0.4 2023-12-02
|
||||
- merged @Isoltero master with fixed memory over run, added VDO remote print statements Thanks to Luis Soltero
|
||||
- fixed example, thanks to @arduinomnomnom
|
||||
|
||||
1.0.3 2022-05-01
|
||||
- Update Examples: AISTransceiverInformation in ParseN2kPGN129039 for changes in NMEA2000 library: https://github.com/ttlappalainen/NMEA2000
|
||||
|
||||
|
||||
1.0.2 2022-04-30
|
||||
- bugfix: malloc without free. Thanks to Luis Soltero (Issue https://github.com/ronzeiller/NMEA0183-AIS/issues/3)
|
||||
|
||||
1.0.1 2022-03-15
|
||||
- bugfix: buffer overrun missing space for termination. Thanks to Luis Soltero (Issue https://github.com/ronzeiller/NMEA0183-AIS/issues/2)
|
||||
|
||||
2020-12-25
|
||||
- corrected Navigational Status 0. Thanks to Li-Ren (Issue https://github.com/ronzeiller/NMEA0183-AIS/issues/1)
|
||||
|
||||
1.0.0 2019-11-24
|
||||
- initial upload
|
||||
|
||||
### 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
|
||||
|
||||
MIT license
|
||||
|
||||
Copyright (c) 2019-2022 Ronnie Zeiller, www.zeiller.eu
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user