//-------------------------------------------------------------------------------------------------
//
// NMEA AIS Parser
/*********************************************************************************************
* Parts of this parser were imported from free software written by Erik Burrows: Java AIS Parser
*
* Copyright (C) 2010 Ehud Pardo
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*********************************************************************************************/
//
//
// Ehud Pardo
//-------------------------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Linq;
using System.Text;
namespace AISNMEAParser
{
public class Parser
{
private Hashtable _multiMessageQueue = new Hashtable();
#region Parser
public Report Parse(String nmeaSentance, out ParserErrorTypes errorNumber)
{
if ((nmeaSentance.StartsWith("!AIVDM") || nmeaSentance.StartsWith("!AIVDO")))
{
if (Checksum(nmeaSentance))
{
string[] nmeaSentanceParts = nmeaSentance.Split(new string[] { "," }, StringSplitOptions.None);
int message_que = 0;
if (nmeaSentanceParts[3].Length > 0)
{
message_que = Convert.ToInt32(nmeaSentanceParts[3]);
}
int message_num = Convert.ToInt32(nmeaSentanceParts[2]);
int message_max = Convert.ToInt32(nmeaSentanceParts[1]);
string bitstring = BinaryToBitString(nmeaSentanceParts[5]);
// partial messages don't have the mmsi in the place it's expected ...
int mmsi = 1;
// assume consecutive parts ...
if (message_num < message_max)
{
string key = mmsi.ToString() + "." + message_que.ToString() + "." + message_num.ToString();
if (_multiMessageQueue.ContainsKey(key))
{
_multiMessageQueue.Remove(key);
}
// Add to the debug buffer
_multiMessageQueue.Add(key, bitstring);
}
if ((message_num == message_max) && (message_num > 1))
{
for (int i = message_num - 1; i >= 1; --i)
{
string partialNMEAMessageKey = mmsi.ToString() + "." + message_que.ToString() + "." + i.ToString();
string prefix = (string)_multiMessageQueue[partialNMEAMessageKey];
_multiMessageQueue.Remove(partialNMEAMessageKey);
if (prefix == null)
{
// Dont have all the parts of the nmea string just return
errorNumber = ParserErrorTypes.NoErrorPartialPacket;
return null;
}
bitstring = prefix + bitstring;
}
}
if ((bitstring != null) && (bitstring.Length > 0))
{
if (message_num == message_max)
{
int message_type = Convert.ToInt32(bitstring.Substring(0, 6), 2);
switch (message_type)
{
case 1:
case 2:
case 3:
return ProcessDynamicInfo(bitstring, out errorNumber);
case 4:
case 11:
return ProcessStationInfo(bitstring, out errorNumber);
case 5:
return ProcessStaticInfo(bitstring, out errorNumber);
case 18:
return ProcessStandardClassB(bitstring, out errorNumber);
case 19:
case 21:
return ProcessExtendedClassB(bitstring, out errorNumber);
case 24:
return ProcessStaticClassB(bitstring, out errorNumber);
default:
errorNumber = ParserErrorTypes.UnRecognisedMessage;
return null;
}
}
else
{
errorNumber = ParserErrorTypes.NoErrorPartialPacket;
return null;
}
}
else
{
errorNumber = ParserErrorTypes.UnRecognisedMessage;
return null;
}
}
else
{
errorNumber = ParserErrorTypes.ChecksumError;
return null;
}
}
else
{
errorNumber = ParserErrorTypes.UnRecognisedMessage;
return null;
}
}
#endregion
#region AIS Parsing Methods
///
/// type 1, 2, 3
/// (first bit is 1 - remember!)
/// 1) Message ID, 6 bits 1-6
/// 2) Repeat Indicator, 2 bits 7-8
/// 3) User ID, 30 bits 9-38
/// 4) Navigation status, 4 bits 39-42
/// 5) Rate of turn, 8 bits 43-50
/// 6) SOG, 10 bits 51-60
/// 7) Position accuracy, 1 bit 61
/// 8) Longitude, 28 bits 62-89
/// 9) Latitude, 27 bits 90-116
/// 10) COG, 12 bits 117-128
/// 11) True Heading, 9 bits 129-137
/// 12) UTC second when report generated, 6 bits 138-143
/// 13) Regional Application, 4 bits 144-147
/// 14) Spare, 1 bit 148
/// 15) RAIM Flag, 1 bit 149
/// 16) Communications State, 19 bits 150-168
///
///
///
///
/// 1) MMSI
/// 2) Message Type
/// 3) recParams("Class = A");
/// recParams("Longitude");
/// recParams("Latitude");
/// recParams("NavigationalStatus");
/// recParams("Rot");
/// recParams("Sog");
/// recParams("PositionAccuracy");
/// recParams("Cog");
/// recParams("TrueHeading");
/// recParams("Timestamp");
/// recParams("RegApplication");
/// recParams("RaimFlag");
/// recParams("CommState");
///
private Report ProcessDynamicInfo(String bitstring, out ParserErrorTypes errorNumber)
{
int msg = Convert.ToInt32(bitstring.Substring(0, 6), 2);
int repeat = Convert.ToInt32(bitstring.Substring(6, 2), 2);
int userID = Convert.ToInt32(bitstring.Substring(8, 30), 2);
int navStatus = Convert.ToInt32(bitstring.Substring(38, 4), 2);
int rot = Convert.ToInt32(bitstring.Substring(42, 8), 2);
double speed = ((double)Convert.ToInt32(bitstring.Substring(50, 10), 2)) / (double)10;
int posAcc = Convert.ToInt32(bitstring.Substring(61, 1), 2);
double lon = DecodeSignedFloat(bitstring.Substring(61, 28)) / (double)600000;
double lat = DecodeSignedFloat(bitstring.Substring(89, 27)) / (double)600000;
double cog = ((double)Convert.ToInt32(bitstring.Substring(116, 12), 2)) / (double)10;
int trueHeading = Convert.ToInt32(bitstring.Substring(128, 9), 2);
int utcSec = Convert.ToInt32(bitstring.Substring(137, 6), 2);
int regApplication = Convert.ToInt32(bitstring.Substring(143, 4), 2);
int raimFlag = Convert.ToInt32(bitstring.Substring(148, 1), 2);
int commState = Convert.ToInt32(bitstring.Substring(149, 19), 2);
if (lon > 181 || lon < -180 || lat > 91 || lat < -90 || speed > 1023 || userID == 0 || lat == 0 || lon == 0)
{
errorNumber = ParserErrorTypes.InvalidLocation;
return null;
}
else
{
Hashtable recParams = new Hashtable();
recParams.Add("Class", "A");
recParams.Add("Longitude", lon);
recParams.Add("Latitude", lat);
recParams.Add("NavigationalStatus", navStatus);
recParams.Add("Rot", rot);
recParams.Add("Sog", speed);
recParams.Add("PositionAccuracy", posAcc);
recParams.Add("Cog", cog);
recParams.Add("TrueHeading", trueHeading);
recParams.Add("Timestamp", utcSec);
recParams.Add("RegApplication", regApplication);
recParams.Add("RaimFlag", raimFlag);
recParams.Add("CommState", commState);
errorNumber = ParserErrorTypes.NoError;
return new Report(userID, msg, repeat, recParams);
}
}
///
/// type 4 or 11
/// (first bit is 1 - remember!)
/// 1)Message ID, 6 bits 1-6
/// 2)Repeat Indicator, 2 bits 7-8
/// 3)User ID, 30 bits 9-38
/// 4)UTC Year, 14 bits 39-52
/// 5)UTC month, 4 bits 53-56
/// 6)UTC Day, 5 bits 57-61
/// 7)UTC Hour, 5 bits 62-66
/// 8)UTC Minute, 6 bits 67-72
/// 9)UTC Second, 6 bits 73-78
/// 10)Position accuracy, 1 bit 79
/// 11)Longitude, 28 bits 80-107
/// 12)Latitude, 29 bits 108-134
/// 13)Type of Fix Device, 4 bits 135-138
/// 14)Spare, 10 bits 139-148
/// 15)RAIM-Flag, 1 bit 149
/// 16)Comm State, 19 bits 150-168
///
///
///
///
/// 1) MMSI
/// 2) Message Type
/// 3) recParams("UTC");
/// recParams("PositionAccuracy");
/// recParams("FixDevice");
/// recParams("RaimFlag");
/// recParams("CommState");
///
private Report ProcessStationInfo(string bitstring, out ParserErrorTypes errorNumber)
{
int msg = Convert.ToInt32(bitstring.Substring(0, 6), 2);
int repeat = Convert.ToInt32(bitstring.Substring(6, 2), 2);
int userID = Convert.ToInt32(bitstring.Substring(8, 30), 2);
int UTCY = Convert.ToInt32(bitstring.Substring(37, 14), 2);
int UTCM = Convert.ToInt32(bitstring.Substring(51, 4), 2);
int UTCD = Convert.ToInt32(bitstring.Substring(55, 5), 2);
int UTCH = Convert.ToInt32(bitstring.Substring(60, 5), 2);
int UTCm = Convert.ToInt32(bitstring.Substring(65, 6), 2);
int UTCS = Convert.ToInt32(bitstring.Substring(71, 6), 2);
int posAcc = Convert.ToInt32(bitstring.Substring(77, 1), 2);
double lon = DecodeSignedFloat(bitstring.Substring(78, 28)) / (double)600000;
double lat = DecodeSignedFloat(bitstring.Substring(106, 27)) / (double)600000;
int fixDevice = Convert.ToInt32(bitstring.Substring(133, 4), 2);
int raimFlag = Convert.ToInt32(bitstring.Substring(147, 1), 2);
int commState = Convert.ToInt32(bitstring.Substring(148, 19), 2);
string UTC = UTCY + "/" + UTCM + "/" + UTCD + " " + UTCH + ":" + UTCm + ":" + UTCS;
if (lon > 181 || lon < -180 || lat > 91 || lat < -90 || userID == 0 || lat == 0 || lon == 0)
{
errorNumber = ParserErrorTypes.InvalidLocation;
return null;
}
else
{
Hashtable recParams = new Hashtable();
recParams.Add("UTC", UTC);
recParams.Add("PositionAccuracy", posAcc);
recParams.Add("FixDevice", fixDevice);
recParams.Add("RaimFlag", raimFlag);
recParams.Add("CommState", commState);
errorNumber = ParserErrorTypes.NoError;
return new Report(userID, msg, repeat, recParams);
}
}
///
/// type 5
/// (first bit is 1 - remember!)
/// 1)Message ID, 6 bits 1-6
/// 2)Repeat Indicator, 2 bits 7-8
/// 3)User ID, 30 bits 9-38
/// 4)AIS Version, 2 bits 39-40
/// 5)IMO number, 30 bits 41-70
/// 6)Call Sign, 42 bits 71-112
/// 7)Name, 120 bits 113-232
/// 8)Ship/Cargo Type, 8 bits 233-240
/// 9)Overall Dimenisions, 30 bits 241-270 // 9 fore, 9 aft, 6 port, 6 stbd
/// 13)Type of Fix Device, 4 bits 271-274
/// 14)ETA, 20 bits 275-294
/// 15)Max Draught, 8 bits 295-302
/// 16)Destination, 120 bits 303-422
/// 17)DTE, 1 bits 423-423
/// 14)Spare, 1 bits 424-424
///
///
///
///
/// 1) MMSI
/// 2) Message Type
/// 3) recParams("Class");
/// recParams("AisVersion");
/// recParams("ImoNumber");
/// recParams("ShipType");
/// recParams("Draught");
/// recParams("FixDevice");
/// recParams("Dte");
/// recParams("CallSign");
/// recParams("Name");
/// recParams("Destination");
/// recParams("Eta");
/// recParams("Length");
/// recParams("Breadth");
/// recParams("Dimension");
///
private Report ProcessStaticInfo(string bitstring, out ParserErrorTypes errorNumber)
{
if (bitstring.Length < 424)
{
errorNumber = ParserErrorTypes.InvalidPacketLength;
return null;
}
errorNumber = ParserErrorTypes.NoError;
int msg = Convert.ToInt32(bitstring.Substring(0, 6), 2);
int repeat = Convert.ToInt32(bitstring.Substring(6, 2), 2);
int mmsi = Convert.ToInt32(bitstring.Substring(8, 30), 2);
int aisVersion = Convert.ToInt32(bitstring.Substring(38, 2), 2);
int imoNumber = Convert.ToInt32(bitstring.Substring(40, 30), 2);
String callSign = DecodeString(bitstring.Substring(70, 42));
String name = DecodeString(bitstring.Substring(112, 120));
int shipType = Convert.ToInt32(bitstring.Substring(232, 8), 2);
int dimA = Convert.ToInt32(bitstring.Substring(240, 9), 2);
int dimB = Convert.ToInt32(bitstring.Substring(249, 9), 2);
int dimC = Convert.ToInt32(bitstring.Substring(258, 6), 2);
int dimD = Convert.ToInt32(bitstring.Substring(264, 6), 2);
int dimension = Convert.ToInt32(bitstring.Substring(240, 30), 2);
int fixDevice = Convert.ToInt32(bitstring.Substring(270, 4), 2);
int etaMonth = Convert.ToInt32(bitstring.Substring(274, 4), 2);
int etaDay = Convert.ToInt32(bitstring.Substring(278, 5), 2);
int etaHour = Convert.ToInt32(bitstring.Substring(283, 5), 2);
int etaMin = Convert.ToInt32(bitstring.Substring(288, 6), 2);
int draught = Convert.ToInt32(bitstring.Substring(294, 8), 2);
String destination = DecodeString(bitstring.Substring(302, 120));
int dte = Convert.ToInt32(bitstring.Substring(423, 1), 2);
string eta = "";
if (etaMonth > 0 && etaDay > 0)
{
eta = "1970-" + etaMonth.ToString("D2") + "-" + etaDay.ToString("D2") + "T" + etaHour.ToString("D2") + ":" + etaMin.ToString("D2") + ":00";
}
string length = (dimA + dimB).ToString();
string breadth = (dimC + dimD).ToString();
Hashtable recParams = new Hashtable();
recParams.Add("Class", "A");
recParams.Add("AisVersion", aisVersion);
recParams.Add("ImoNumber", imoNumber);
recParams.Add("ShipType", shipType);
recParams.Add("Draught", draught);
recParams.Add("FixDevice", fixDevice);
recParams.Add("Dte", dte);
recParams.Add("CallSign", callSign);
recParams.Add("Name", name);
recParams.Add("Destination", destination);
recParams.Add("Eta", eta);
recParams.Add("Length", length);
recParams.Add("Breadth", breadth);
recParams.Add("Dimension", dimension);
return new Report(mmsi, msg, repeat, recParams);
}
///
/// type 18 - Standard Class B Equipment Report
/// (first bit is 1 - remember!)
/// 1)Message ID, 6 bits 1-6
/// 2)Repeat Indicator, 2 bits 7-8
/// 3)User ID, 30 bits 9-38
/// 4)Reserved for Local app, 8 bits 39-46
/// 5)SOG, 10 bits 47-56
/// 6)Pos Acc, 1 bits 57-57
/// 7)Lon, 28 bits 58-85
/// 8)Lat, 27 bits 86-112
/// 9)COG, 12 bits 113-124
/// 10)True Heading, 9 bits 125-133
/// 11)TimeStamp, 6 bits 134-139
/// 12)Reserved for Local App, 4 bits 140-143
/// 13)spare, 3 bits 144-146
/// 16)AssignedMode, 1 bits 147-147
/// 17)RAIM-flag, 1 bits 148-148
/// 18)CommStateSelect Flag, 1 bits 149-149
/// 19)Comm State, 19 bits 150-168
///
///
///
///
/// 1) MMSI
/// 2) Message Type
/// 3) recParams("Class=B");
/// recParams("LocalApp1");
/// recParams("Sog");
/// recParams("PosAcc");
/// recParams("Longitude");
/// recParams("Latitude");
/// recParams("Cog");
/// recParams("TrueHeading");
/// recParams("Timestamp");
/// recParams("LocalApp2");
/// recParams("AssignedMode");
/// recParams("RaimStatus");
/// recParams("CommStateSelect");
/// recParams("CommState");
///
private Report ProcessStandardClassB(String bitstring, out ParserErrorTypes errorNumber)
{
int msg = Convert.ToInt32(bitstring.Substring(0, 6), 2);
int repeat = Convert.ToInt32(bitstring.Substring(6, 2), 2);
int userID = Convert.ToInt32(bitstring.Substring(8, 30), 2);
int res1 = Convert.ToInt32(bitstring.Substring(38, 8), 2);
double sog = (double)Convert.ToInt32(bitstring.Substring(46, 10), 2) / (double)10;
int posAcc = Convert.ToInt32(bitstring.Substring(56, 1), 2);
double lon = DecodeSignedFloat(bitstring.Substring(57, 28)) / (double)600000;
double lat = DecodeSignedFloat(bitstring.Substring(85, 27)) / (double)600000;
double cog = (double)Convert.ToInt32(bitstring.Substring(112, 12), 2) / (double)10;
int trueHeading = Convert.ToInt32(bitstring.Substring(124, 9), 2);
int tstmp = Convert.ToInt32(bitstring.Substring(133, 6), 2);
int res2 = Convert.ToInt32(bitstring.Substring(139, 4), 2);
int spare = Convert.ToInt32(bitstring.Substring(143, 3), 2);
int assignedMode = Convert.ToInt32(bitstring.Substring(146, 1), 2);
int raimStatus = Convert.ToInt32(bitstring.Substring(147, 1), 2);
int commStateSelect = Convert.ToInt32(bitstring.Substring(148, 1), 2);
int commState = Convert.ToInt32(bitstring.Substring(149, 19), 2);
if (lon > 181 || lon < -180 || lat > 91 || lat < -90 || userID == 0 || lat == 0 || lon == 0)
{
errorNumber = ParserErrorTypes.InvalidLocation;
return null;
}
else
{
Hashtable recParams = new Hashtable();
recParams.Add("Class", "B");
recParams.Add("LocalApp1", res1);
recParams.Add("Sog", sog);
recParams.Add("PosAcc", posAcc);
recParams.Add("Longitude", lon);
recParams.Add("Latitude", lat);
recParams.Add("Cog", cog);
recParams.Add("TrueHeading", trueHeading);
recParams.Add("Timestamp", tstmp);
recParams.Add("LocalApp2", res2);
recParams.Add("AssignedMode", assignedMode);
recParams.Add("RaimStatus", raimStatus);
recParams.Add("CommStateSelect", commStateSelect);
recParams.Add("CommState", commState);
errorNumber = ParserErrorTypes.NoError;
return new Report(userID, msg, repeat, recParams);
}
}
///
/// type 19 - extended class B report
/// (first bit is 1 - remember!)
/// 1)Message ID, 6 bits 1-6
/// 2)Repeat Indicator, 2 bits 7-8
/// 3)User ID, 30 bits 9-38
/// 4)Reserved for Local app, 8 bits 39-46
/// 5)SOG, 10 bits 47-56
/// 6)Pos Acc, 1 bits 57-57
/// 7)Lon, 28 bits 58-85
/// 8)Lat, 27 bits 86-112
/// 9)COG, 12 bits 113-124
/// 10)True Heading, 9 bit 125-133
/// 11)TimeStamp, 6 bits 134-139
/// 12)Reserved for Local App, 4 bits 140-143
/// 13)Name, 120 bits 144-263
/// 14)Type of Ship/Cargo, 8 bits 264-271
/// 15)Dimensions, 30 bits 272-301 // 9 fore, 9 aft, 6 port, 6 stbd
/// 16)Type of Fix Dev, 4 bits 302-305
/// 17)RAIM-flag, 1 bit 306
/// 18)DTE, 1 bit 307
/// 19)Assignend Mode, 1 bit 308
/// 20)Spare, 4 bit 309-312
///
///
///
///
/// 1) MMSI
/// 2) Message Type
/// 3) recParams.Add("Class = B");
/// recParams.Add("LocalApp1");
/// recParams.Add("Sog");
/// recParams.Add("PosAcc");
/// recParams.Add("Longitude");
/// recParams.Add("Latitude");
/// recParams.Add("Cog");
/// recParams.Add("TrueHeading");
/// recParams.Add("Timestamp");
/// recParams.Add("LocalApp2");
/// recParams.Add("Name");
/// recParams("Length");
/// recParams("Breadth");
/// recParams("Dimension");
/// recParams.Add("FixDevice");
/// recParams.Add("RaimStatus");
/// recParams.Add("Dte");
/// recParams.Add("AssignedMode");
///
///
private Report ProcessExtendedClassB(string bitstring, out ParserErrorTypes errorNumber)
{
int msg = Convert.ToInt32(bitstring.Substring(0, 6), 2);
int repeat = Convert.ToInt32(bitstring.Substring(6, 2), 2);
int userID = Convert.ToInt32(bitstring.Substring(8, 30), 2);
int res1 = Convert.ToInt32(bitstring.Substring(38, 8), 2);
double sog = (double)Convert.ToInt32(bitstring.Substring(46, 10), 2) / (double)10;
int posAcc = Convert.ToInt32(bitstring.Substring(56, 1), 2);
double lon = DecodeSignedFloat(bitstring.Substring(57, 28)) / (double)600000;
double lat = DecodeSignedFloat(bitstring.Substring(85, 27)) / (double)600000;
double cog = (double)Convert.ToInt32(bitstring.Substring(112, 12), 2) / (double)10;
int trueHeading = Convert.ToInt32(bitstring.Substring(124, 9), 2);
int tstmp = Convert.ToInt32(bitstring.Substring(133, 6), 2);
int res2 = Convert.ToInt32(bitstring.Substring(139, 4), 2);
string name = DecodeString(bitstring.Substring(143, 120));
int ShipType = Convert.ToInt32(bitstring.Substring(263, 8), 2);
int dimA = Convert.ToInt32(bitstring.Substring(271, 9), 2);
int dimB = Convert.ToInt32(bitstring.Substring(280, 9), 2);
int dimC = Convert.ToInt32(bitstring.Substring(289, 6), 2);
int dimD = Convert.ToInt32(bitstring.Substring(295, 6), 2);
int dimension = Convert.ToInt32(bitstring.Substring(271, 30), 2);
int FixTyp = Convert.ToInt32(bitstring.Substring(301, 4), 2);
int raimStatus = Convert.ToInt32(bitstring.Substring(305, 1), 2);
int DTE = Convert.ToInt32(bitstring.Substring(306, 1), 2);
int assignedMode = Convert.ToInt32(bitstring.Substring(307, 1), 2);
int Spare = Convert.ToInt32(bitstring.Substring(308, 4), 2);
string length = (dimA + dimB).ToString();
string breadth = (dimC + dimD).ToString();
if (lon > 181 || lon < -180 || lat > 91 || lat < -90 || userID == 0 || lat == 0 || lon == 0)
{
errorNumber = ParserErrorTypes.InvalidLocation;
return null;
}
else
{
Hashtable recParams = new Hashtable();
recParams.Add("Class", "B");
recParams.Add("LocalApp1", res1);
recParams.Add("Sog", sog);
recParams.Add("PosAcc", posAcc);
recParams.Add("Longitude", lon);
recParams.Add("Latitude", lat);
recParams.Add("Cog", cog);
recParams.Add("TrueHeading", trueHeading);
recParams.Add("Timestamp", tstmp);
recParams.Add("LocalApp2", res2);
recParams.Add("Name", name);
recParams.Add("Length", length);
recParams.Add("Breadth", breadth);
recParams.Add("Dimension", dimension);
recParams.Add("FixDevice", FixTyp);
recParams.Add("RaimStatus", raimStatus);
recParams.Add("Dte", DTE);
recParams.Add("AssignedMode", assignedMode);
errorNumber = ParserErrorTypes.NoError;
return new Report(userID, msg, repeat, recParams);
}
}
///
/// type 24 - Static Class B Station Info
/// (first bit is 1 - remember!)
/// 1)Message ID, 6 bits 1-6
/// 2)Repeat Indicator, 2 bits 7-8
/// 3)User ID, 30 bits 9-38
/// 4)Part Number 2 bits 39-40 // Part 0=>A, 1=>B
///
/// 5A)Name, 120 bits 41-160 // USCG says should be "US//" then state/fed reg //
///
/// 5B)Ship/Cargo Type, 8 bits 41-48
/// 6B)Vendor ID, 42 bits 49-90
/// 7B)Call Sign, 42 bits 91-132
/// 8B)Overall Dimenisions, 30 bits 133-162 // 9 fore, 9 aft, 6 port, 6 stbd
/// 9B)Spare, 6 bits 163-168
///
///
///
///
/// 1) MMSI
/// 2) Message Type
/// 3) recParams("Part");
/// recParams("Class=B");
/// Part 0:
/// recParams("Name");
/// Part 1:
/// recParams("ShipType");
/// recParams("Vendor");
/// recParams("CallSign");
/// recParams("Length");
/// recParams("Breadth");
/// recParams("Dimension");
///
private Report ProcessStaticClassB(string bitstring, out ParserErrorTypes errorNumber)
{
int msg = Convert.ToInt32(bitstring.Substring(0, 6), 2);
int repeat = Convert.ToInt32(bitstring.Substring(6, 2), 2);
int userID = Convert.ToInt32(bitstring.Substring(8, 30), 2);
int part = Convert.ToInt32(bitstring.Substring(38, 2), 2);
Report report = null;
if (part == 0)
{
string name = DecodeString(bitstring.Substring(40, 120));
Hashtable recParams = new Hashtable();
recParams.Add("Part", part.ToString());
recParams.Add("Class", "B");
recParams.Add("Name", name);
report = new Report(userID, msg, repeat, recParams);
errorNumber = ParserErrorTypes.NoError;
}
else if (part == 1)
{
int shipType = Convert.ToInt32(bitstring.Substring(40, 8), 2);
string vendor = DecodeString(bitstring.Substring(48, 42));
string callSign = DecodeString(bitstring.Substring(90, 42));
int dimA = Convert.ToInt32(bitstring.Substring(132, 9), 2);
int dimB = Convert.ToInt32(bitstring.Substring(141, 9), 2);
int dimC = Convert.ToInt32(bitstring.Substring(150, 6), 2);
int dimD = Convert.ToInt32(bitstring.Substring(156, 6), 2);
int dimension = Convert.ToInt32(bitstring.Substring(132, 30), 2);
string length = (dimA + dimB).ToString();
string breadth = (dimC + dimD).ToString();
Hashtable recParams = new Hashtable();
recParams.Add("Part", part.ToString());
recParams.Add("Class", "B");
recParams.Add("ShipType", shipType);
recParams.Add("Vendor", vendor);
recParams.Add("CallSign", callSign);
recParams.Add("Length", length);
recParams.Add("Breadth", breadth);
recParams.Add("Dimension", dimension);
report = new Report(userID, msg, repeat, recParams);
errorNumber = ParserErrorTypes.NoError;
}
else
{
errorNumber = ParserErrorTypes.UnRecognisedMessage;
}
return report;
}
#endregion
#region private methods
private bool Checksum(string sentance)
{
byte sum = Convert.ToByte(sentance[sentance.IndexOf("!") + 1]);
for (int i = sentance.IndexOf("!") + 2; i < sentance.IndexOf("*"); ++i)
{
sum ^= Convert.ToByte(sentance[i]);
}
int idx = sentance.IndexOf("*");
return sum == Convert.ToByte(sentance.Substring(idx + 1, 2), 16);
}
private double DecodeSignedFloat(string binary)
{
double ret = (double)Convert.ToInt32(binary, 2);
if (binary[0] == '1')
{
ret = ret - Math.Pow(2, binary.Length);
}
return ret;
}
private string DecodeString(string binary)
{
string ret = "";
for (int i = 0; i < binary.Length / 6; ++i)
{
int c = Convert.ToInt32(binary.Substring(i * 6, 6), 2);
if (c < 32 && c >= 0) //convert to 6-bit ASCII - control chars to uppercase latins
{
c = c + 64;
}
if (c != 64)
{
ret += Convert.ToChar(c);
}
}
return ret;
}
private string BinaryToBitString(string binary)
{
string bitString = "";
for (int i = 0; i < binary.Length; ++i)
{
int byteValue = Convert.ToInt32(binary[i]);
if (byteValue >= 48 && byteValue < 88)
{ //40 chars 0 - W
byteValue -= 48;
}
else if (byteValue >= 96 && byteValue < 121)
{ //24 chars `,a - w
byteValue -= 56;
}
else
{
byteValue = 0; //ignore any ather chars
}
for (int j = ToBinaryString(byteValue.ToString()).Length; j < 6; ++j)
{
// pad first with 0s up to 6 bits
bitString = bitString + "0";
}
// add the value of the bits in.
bitString = bitString + ToBinaryString(byteValue.ToString());
}
return bitString;
}
private string ToBinaryString(string strInput)
{
return Convert.ToString(Convert.ToInt32(strInput), 2);
}
#endregion
}
#region enums
public enum ParserErrorTypes
{
NoError = 0,
NoErrorPartialPacket = 1,
UnRecognisedMessage = 2,
ChecksumError = 3,
InvalidTimeValue = 4,
InvalidLocation = 5,
InvalidPacketLength = 6
}
#endregion
}