//------------------------------------------------------------------------------------------------- // // 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 }