UCNLNMEA
/
NMEAParser.cs
2238 строк · 98.6 Кб
1using System;2using System.Collections.Generic;3using System.Globalization;4using System.Text;5
6namespace UCNLNMEA7{8/// <summary>9/// Talkers identifiers10/// </summary>11public enum TalkerIdentifiers12{13AG,14AP,15CD,16CR,17CS,18CT,19CV,20CX,21DE,22DF,23EC,24EP,25ER,26GA, // 03-JUN-202027GB, // 02-JUN-202028GL,29GP,30GN,31HC,32HE,33HN,34II,35IN,36LA,37LC,38OM,39P,40RA,41SD,42SN,43TR,44SS,45TI,46VD,47DM,48VW,49WI,50YX,51ZA,52ZC,53ZQ,54ZV,55unknown
56}57
58/// <summary>59/// Sentences identifiers60/// </summary>61public enum SentenceIdentifiers62{63AAM,64ALM,65APA,66APB,67ASD,68BEC,69BOD,70BWC,71BWR,72BWW,73DBK,74DBS,75DBT,76DCN,77DPT,78DSC,79DSE,80DSI,81DSR,82DTM,83FSI,84GBS,85GGA,86GLC,87GLL,88GNS,89GRS,90GST,91GSA,92GSV,93GTD,94GXA,95HDG,96HDM,97HDT,98HEV,99HSC,100LCD,101MSK,102MSS,103MWD,104MTW,105MWV,106OLN,107OSD,108ROO,109RMA,110RMB,111RMC,112ROT,113RPM,114RSA,115RSD,116RTE,117SFI,118STN,119TLL,120TRF,121TTM,122TXT,123VBW,124VDR,125VHW,126VLW,127VPW,128VTG,129VWR,130WCV,131WDC,132WDR,133WNC,134WPL,135XDR,136XTE,137XTR,138ZDA,139ZDL,140ZFO,141ZTG,142unknown
143}144
145/// <summary>146/// Manufacturers codes147/// </summary>148public enum ManufacturerCodes149{150AAR,151ACE,152ACR,153ACS,154ACT,155AGI,156AHA,157AIP,158ALD,159AMR,160AMT,161ANS,162ANX,163ANZ,164APC,165APL,166APN,167APX,168AQC,169AQD,170AQM,171ASP,172ATE,173ATM,174ATR,175ATV,176AVN,177AWA,178BAT,179BBL,180BBR,181BDV,182BEC,183BGS,184BGT,185BHE,186BHR,187BLB,188BME,189BNI,190BNS,191BRM,192BRY,193BTH,194BTK,195BTS,196BXA,197CAT,198CBN,199CCA,200CCC,201CCL,202CCM,203CDC,204CEC,205CHI,206CKM,207CMA,208CMC,209CME,210CMP,211CMS,212CMV,213CNV,214CNX,215CPL,216CPN,217CPS,218CPT,219CRE,220CRO,221CRY,222CSI,223CSM,224CST,225CSV,226CTA,227CTB,228CTC,229CTE,230CTL,231CNI,232CWD,233CWV,234CYZ,235DBG,236DCC,237DEB,238DFI,239DGC,240DME,241DMI,242DNS,243DNT,244DPS,245DRL,246DSC,247DYN,248DYT,249EBC,250ECT,251EEV,252EFC,253ELD,254EMC,255EMS,256ENA,257ENC,258EPM,259EPT,260ERC,261ESA,262FDN,263FEC,264FHE,265FJN,266FMM,267FNT,268FRC,269FTG,270FUJ,271FUG,272FUR,273GAM,274GCA,275GES,276GFC,277GIS,278GPI,279GRM,280GSC,281GTO,282GVE,283GVT,284HAL,285HAR,286HDN,287HIG,288HIT,289HPK,290HRC,291HRT,292HTI,293HUL,294HWM,295ICO,296IFD,297IFI,298IME,299IMI,300IMM,301IMP,302IMT,303INM,304INT,305IRT,306IST,307ITM,308ITR,309JAN,310JFR,311JMT,312JRC,313JRI,314JTC,315JTR,316KBE,317KBM,318KLA,319KMR,320KNG,321KOD,322KRP,323KVH,324KYI,325LAT,326LEC,327LMM,328LNA,329LRD,330LSE,331LSP,332LTF,333LWR,334MCL,335MCP,336MDL,337MEC,338MEG,339MFR,340MFW,341MGN,342MGS,343MIE,344MIM,345MLE,346MLN,347MLP,348MLT,349MMB,350MME,351MMP,352MMS,353MNI,354MNT,355MNX,356MOT,357MPN,358MQS,359MRC,360MRE,361MRP,362MRR,363MRS,364MSB,365MSE,366MSM,367MST,368MTA,369MTG,370MTK,371MTR,372MTS,373MUR,374MVX,375MXX,376MES,377NEF,378NMR,379NGS,380NOM,381NOV,382NSM,383NTK,384NVC,385NVL,386NVS,387NVO,388OAR,389ODE,390ODN,391OIN,392OKI,393OLY,394OMN,395ORE,396OTK,397PCE,398PCH,399PDM,400PLA,401PLI,402PMI,403PMP,404PRK,405PSM,406PTC,407PTG,408PTH,409RAC,410RAE,411RAY,412RCA,413RCH,414RCI,415RDI,416RDM,417REC,418RFP,419RGC,420RGY,421RLF,422RMR,423RPH,424RSL,425RSM,426RWE,427RWI,428RWL,429RME,430RTN,431SAI,432SBR,433SCR,434SEA,435SEC,436SEP,437SFN,438SGC,439SIG,440SIM,441SKA,442SKP,443SLI,444SME,445SMF,446SML,447SMI,448SNV,449SOM,450SOV,451SPL,452SPT,453SRD,454SRF, // added 08 december 2013455SRS,456SRT,457SSI,458STC,459STI,460STM,461SVY,462SWI,463TBB,464TCN,465TDL,466THR,467TLS,468TMT,469TNL,470TNT,471TRC,472TSI,473TTK,474TTS,475TWC,476TXI,477UCN,478UME,479UNI,480UNP,481UNF,482UWV,483VAN,484VAR,485VCM,486VEX,487VIS,488VLB,489VMR,490VSN,491WAL,492WBG,493WEC,494WHA,495WMM,496WMR,497WNG,498WSE,499WTC,500WST,501YAS,502ZMA,503any,504unknown
505}506
507/// <summary>508/// Earth hemisperes cardinals509/// </summary>510[Flags]511public enum Cardinals512{513North,514South,515East,516West,517undefined
518}519
520/// <summary>521/// Structure represents geographical dimension522/// </summary>523public struct GeographicDimension524{525public Cardinals Cardinal;526public double Angle;527
528public override string ToString()529{530int degree = (int)Math.Floor(Angle);531int minutes = (int)Math.Floor((Angle - degree) * 60.0);532double seconds = (Angle - degree) * 3600 - minutes * 60.0;533
534return string.Format(CultureInfo.InvariantCulture, "{0}°{1}\'{2:F04}\" {3}", degree, minutes, seconds, Cardinal);535}536
537public GeographicDimension(double angle, Cardinals cardinal)538{539Angle = angle;540Cardinal = cardinal;541}542
543public static double AsSignedAngle(GeographicDimension dimension)544{545double result = dimension.Angle;546
547if ((dimension.Cardinal == Cardinals.North) || (dimension.Cardinal == Cardinals.West))548result = -dimension.Angle;549
550return result;551}552}553
554/// <summary>555/// Struct represents geographical position556/// </summary>557public struct GeographicPosition558{559public GeographicDimension Latitude;560public GeographicDimension Longitude;561
562public override string ToString()563{564return string.Format("{0}, {1}", Latitude.ToString(), Longitude.ToString());565}566}567
568/// <summary>569/// NMEA0183 2.0 Sentences parser/builder570/// (C) Aleksander Dikarev, 2011-2020571/// </summary>572public static class NMEAParser573{574#region Properties575
576#region Commons577
578#region expression delimiters579
580public static readonly string StandartID = "NMEA 0183 2.0";581public static readonly int MaxFullSentenceLength = 4255;//82;582public static readonly int MaxSentenceLength = 4252;//79;583public static readonly char SentenceStartDelimiter = '$';584public static readonly string SentenceEndDelimiter = "\r\n";585public static readonly char FieldDelimiter = ',';586public static readonly char CheckSumFieldDelimiter = '*';587
588private static char[] arrayBrackets = new char[] { '[', ']' };589public static string arrayOpenBracket = "[";590public static string arrayCloseBracket = "]";591public static char[] formatTokenDelimiters = new char[] { ',' };592public static char[] formatEnumDelimiters = new char[] { '|' };593private static string formatEnumPairDelimiter = "=";594public static char[] formatEnumPairDelimiters = new char[] { '=' };595private static string etcFormat = "...";596
597#endregion598
599#region Talker descriptions600
601private static Dictionary<TalkerIdentifiers, string> TalkerDescriptions =602new Dictionary<TalkerIdentifiers, string>() { { TalkerIdentifiers.AG, "Autopilot - General" },603{ TalkerIdentifiers.AP, "Autopilot - Magnetic" },604{ TalkerIdentifiers.CD, "Communications - Digital Selective Calling (DSC)" },605{ TalkerIdentifiers.CR, "Communications - Receiver / Beacon Receiver" },606{ TalkerIdentifiers.CS, "Communications - Sattelite" },607{ TalkerIdentifiers.CT, "Communications - Radio-Telephone (MF/HF)" },608{ TalkerIdentifiers.CV, "Communications - Radio-Telephone (VHF)" },609{ TalkerIdentifiers.CX, "Communications - Scanning Receiver" },610{ TalkerIdentifiers.DF, "Direction Finder" },611{ TalkerIdentifiers.EC, "Electronic Chart Display & Information System (ECDIS)" },612{ TalkerIdentifiers.EP, "Emergency Position Indicating Beacon (EPIRB)" },613{ TalkerIdentifiers.ER, "Engine Room Monitoring Systems" },614{ TalkerIdentifiers.GP, "Global Positioning System (GPS)" },615{ TalkerIdentifiers.HC, "Heading - Magnetic Compass" },616{ TalkerIdentifiers.HE, "Heading - North Seeking Gyro" },617{ TalkerIdentifiers.HN, "Heading - Non North Seeking Gyro" },618{ TalkerIdentifiers.II, "Integrated instrumentation" },619{ TalkerIdentifiers.IN, "Integrated Navigation" },620{ TalkerIdentifiers.LC, "Loran C" },621{ TalkerIdentifiers.P, "Proprietary Code" },622{ TalkerIdentifiers.RA, "RADAR and/or ARPA" },623{ TalkerIdentifiers.SD, "Sounder, Depth" },624{ TalkerIdentifiers.SN, "Electronic Positioning System, other/general" },625{ TalkerIdentifiers.SS, "Souder, Scanning" },626{ TalkerIdentifiers.TI, "Turn Rate Indicator" },627{ TalkerIdentifiers.VD, "Velocity Sensor, Doppler, other/general" },628{ TalkerIdentifiers.DM, "Velocity Sensor, Speed Log, Water, Magnetic" },629{ TalkerIdentifiers.VW, "Velocity Sensor, Speed Log, Water, Mechanical" },630{ TalkerIdentifiers.WI, "Weather Instruments" },631{ TalkerIdentifiers.YX, "Transduser" },632{ TalkerIdentifiers.ZA, "Timekeeper - Atomic Clock" },633{ TalkerIdentifiers.ZC, "Timekeeper - Chronometer" },634{ TalkerIdentifiers.ZQ, "Timekeeper - Quartz" },635{ TalkerIdentifiers.ZV, "Radio Update, WWV or WWVH" }636};637
638#endregion639
640#region Sentences descriptions641
642private static Dictionary<SentenceIdentifiers, string> SentencesDescriptions =643new Dictionary<SentenceIdentifiers, string>() { { SentenceIdentifiers.AAM, "Waypoint Arrival Alarm" },644{ SentenceIdentifiers.ALM, "GPS Almanac Data" },645{ SentenceIdentifiers.APB, "Autopilot Sentence \"B\"" },646{ SentenceIdentifiers.APA, "Autopilot Sentence \"A\"" },647{ SentenceIdentifiers.ASD, "Autopilot System Data" },648{ SentenceIdentifiers.BEC, "Bearing & Distance to Waypoint, Dead reckoning" },649{ SentenceIdentifiers.BOD, "Bearing, Origin to Destination" },650{ SentenceIdentifiers.BWC, "Bearing & Distance to Waypoint, Great Circle" },651{ SentenceIdentifiers.BWR, "Bearing & Distance to Waypoint, Rhumb Line" },652{ SentenceIdentifiers.BWW, "Bearing, Waypoint to Waypoint" },653{ SentenceIdentifiers.DBK, "Depth Below Keel" },654{ SentenceIdentifiers.DBS, "Depth Below Surface" },655{ SentenceIdentifiers.DBT, "Depth Below Transduser" },656{ SentenceIdentifiers.DCN, "[Obsolete] Decca Position" },657{ SentenceIdentifiers.DPT, "Depth" },658{ SentenceIdentifiers.DSC, "Digital Selective Calling Information" },659{ SentenceIdentifiers.DSE, "Extended DSC" },660{ SentenceIdentifiers.DSI, "DSC Transponder Initiate" },661{ SentenceIdentifiers.DSR, "DSC Transponder Response" },662{ SentenceIdentifiers.DTM, "Datum Reference" },663{ SentenceIdentifiers.FSI, "Frequency Set Information" },664{ SentenceIdentifiers.GBS, "GBS Satellite Fault Detection" },665{ SentenceIdentifiers.GGA, "Global Positioning System Fix Data" },666{ SentenceIdentifiers.GLC, "Geographic Position, Loran-C" },667{ SentenceIdentifiers.GLL, "Geographic Position, Latitude/Longitude" },668{ SentenceIdentifiers.GRS, "GPS Range Residuals" },669{ SentenceIdentifiers.GSA, "GPS DOP and Active Satellites" },670{ SentenceIdentifiers.GST, "GPS Pseudorange Noise Statistics" },671{ SentenceIdentifiers.GSV, "GPS Sattelites in View" },672{ SentenceIdentifiers.GTD, "Geograpic Location in Time Differences" },673{ SentenceIdentifiers.GXA, "Transit Position" },674{ SentenceIdentifiers.HDG, "Heading, Deviation & Variation" },675{ SentenceIdentifiers.HDM, "Heading, Magnetic" },676{ SentenceIdentifiers.HDT, "Heading, True" },677{ SentenceIdentifiers.HSC, "Heading Steering Command" },678{ SentenceIdentifiers.LCD, "Loran-C Signal Data" },679{ SentenceIdentifiers.MSK, "MSK Receiver Interface (for DGPS Beacon Receivers)" },680{ SentenceIdentifiers.MSS, "MSK Receiver Signal Status" },681{ SentenceIdentifiers.MTW, "Water Temperature" },682{ SentenceIdentifiers.MWD, "Wind Direction & Speed" },683{ SentenceIdentifiers.MWV, "Wind Speed & Angle" },684{ SentenceIdentifiers.OLN, "[Obsolete] Omega Line Numbers" },685{ SentenceIdentifiers.OSD, "Own Ship Data" },686{ SentenceIdentifiers.RMA, "Recommend Minimum Specific Loran-C Data" },687{ SentenceIdentifiers.RMB, "Recommend Minimum Navigation Information" },688{ SentenceIdentifiers.RMC, "Recommend Minimum Specific GPS/TRANSIT Data" },689{ SentenceIdentifiers.ROO, "Waypoints in Active Route" },690{ SentenceIdentifiers.ROT, "Rate of Turn" },691{ SentenceIdentifiers.RPM, "Revolutions" },692{ SentenceIdentifiers.RSA, "Rudder Sensor Angle" },693{ SentenceIdentifiers.RSD, "RADAR System Data" },694{ SentenceIdentifiers.RTE, "Routes" },695{ SentenceIdentifiers.SFI, "Scanning Frequency Information" },696{ SentenceIdentifiers.STN, "Multiple Data ID" },697{ SentenceIdentifiers.TLL, "Target Latitude & Longitude" },698{ SentenceIdentifiers.TRF, "[Obsolete] TRANSIT Fix Data" },699{ SentenceIdentifiers.TTM, "Tracked Target Message" },700{ SentenceIdentifiers.VBW, "Dual Ground/Water Speed" },701{ SentenceIdentifiers.VDR, "Set and Drift" },702{ SentenceIdentifiers.VHW, "Water Speed and Heading" },703{ SentenceIdentifiers.VLW, "Distance Traveled through Water" },704{ SentenceIdentifiers.VPW, "Speed, Measured Parallel to Wind" },705{ SentenceIdentifiers.VTG, "Track Made Good and Ground Speed" },706{ SentenceIdentifiers.VWR, "Relative Wind Speed & Angle" },707{ SentenceIdentifiers.WCV, "Waypoint Closure Velocity" },708{ SentenceIdentifiers.WDC, "Distance to Waypoint, Great Circle" },709{ SentenceIdentifiers.WDR, "Distance to Waypoint, Rhumb Line" },710{ SentenceIdentifiers.WNC, "Distance, Waypoint to Waypoint" },711{ SentenceIdentifiers.WPL, "Waypoint Location" },712{ SentenceIdentifiers.XDR, "Transducer Measurement" },713{ SentenceIdentifiers.XTE, "Cross-Track Error, Measured" },714{ SentenceIdentifiers.XTR, "Cross-Track Error, Dead Reckoning" },715{ SentenceIdentifiers.ZDA, "Time & Date" },716{ SentenceIdentifiers.ZDL, "Time & Distance to Variable Point" },717{ SentenceIdentifiers.ZFO, "UTC & Time from Origin Waypoint" },718{ SentenceIdentifiers.ZTG, "UTC & Time to Destination Waypoint" }719};720
721#endregion722
723#region Datum dictionary724
725private static Dictionary<int, string> Datums = new Dictionary<int, string>()726{727{ 0, "WGS1984 (International)" },728{ 1, "Tokyo (Japan)" },729{ 2, "Tokyo (Mean for Japan, South Korea, Okinawa)" },730{ 3, "User setting" },731{ 4, "Adindan (Burkina Faso)" },732{ 5, "Adindan (Cameroon)" },733{ 6, "Adindan (Ethiopia)" },734{ 7, "Adindan (Mali)" },735{ 8, "Adindan (Mean for Ethiopia, Sudan)" },736{ 9, "Adindan (Senegal)" },737{ 10, "Adindan (Sudan)" },738{ 11, "Afgooye (Somalia)" },739{ 12, "Ain El Abd1970 (Bahrain)" },740{ 13, "Ain El Abd1970 (Saudi Arabia)" },741{ 14, "American Samoa1962 (American Samoa islands)" },742{ 15, "Anna 1 Astro1965 (Cocos Island)" },743{ 16, "Antigua Island Astro1943 (Antigua (Leeward Islands))" },744{ 17, "Arc1950 (Botswana)" },745{ 18, "Arc1950 (Burundi)" },746{ 19, "Arc1950 (Lesotho)" },747{ 20, "Arc1950 (Malawi)" },748{ 21, "Arc1950 (Mean for Botswana, Lesotho, Malawi, Swaziland, Zaire, Zambia, Zimbabwe)" },749{ 22, "Arc1950 (Swaziland)" },750{ 23, "Arc1950 (Zaire)" },751{ 24, "Arc1950 (Zambia)" },752{ 25, "Arc1950 (Zimbabwe)" },753{ 26, "Arc1960 (Mean for Kenya Tanzania)" },754{ 27, "Arc1960 (Kenya)" },755{ 28, "Arc1960 (Tanzania)" },756{ 29, "Ascension Island 1958 (Ascension Island)" },757{ 30, "Astro Beacon E 1945 (Iwo Jima)" },758{ 31, "Astro Dos 71/4 (St Helena Island)" },759{ 32, "Astro Tern Island (FRIG) 1961 (Tern Island)" },760{ 33, "Astronomical Station 1952 (Marcus Island)" },761{ 34, "Australian Geodetic 1966 (Mercus Island)" },762{ 35, "Australian Geodetic 1984 (Australia, Tasmania)" },763{ 36, "Ayabelle Lighthouse (Djibouti)" },764{ 37, "Bellevue (IGN) (Efate and Erromango Islands)" },765{ 38, "Bermuda 1957 (Bermuda)" },766{ 39, "Bissau (Guuinea-Bissau)" },767{ 40, "Bogota Observatory (Colombia)" },768{ 41, "Bukit Rimpah (Indonesia (Bangka and Belitung Ids))" },769{ 42, "Camp Area Astro (Antarctica (McMurdi Camp Area))" },770{ 43, "Campo Inchauspe (Argentina)" },771{ 44, "Canton Astro1966 (Phoenix Island)" },772{ 45, "Cape (South Africa)" },773{ 46, "Cape Canaveral (Bahamas, Florida)" },774{ 47, "Carthage (Tunisia)" },775{ 48, "Chatham Island Astro1971 (New Zealand(Chatham Island))" },776{ 49, "Chua Astro (Paraguay)" },777{ 50, "Corrego Alegre (Brazil)" },778{ 51, "Dabola (Guinea)" },779{ 52, "Deception Island (Deception Island, Antarctica)" },780{ 53, "Djakarta (Batavia) (Indonesia (Sumatra))" },781{ 54, "Dos 1968 (New Georgia Islands (Gizo Island)" },782{ 55, "Easter Island 1967 (Easter Island)" },783{ 56, "Estonia Coordinate System 1937 (Estonia)" },784{ 57, "European 1950 (Cyprus)" },785{ 58, "European 1950 (Egypt)" },786{ 59, "European 1950 (England, Channel Isnalds, Scotland, Shetland Islands)" },787{ 60, "European 1950 (England, Ireland, Scotland, Shetland Islands)" },788{ 61, "European 1950 (Finland, Norway" },789{ 62, "European 1950 (Greece)" },790{ 63, "European 1950 (Iran)" },791{ 64, "European 1950 (Italy (Sardinia))" },792{ 65, "European 1950 (Italy (Sicily))" },793{ 66, "European 1950 (Malta)" },794{ 67, "European 1950 (Mean for Austria, Belgium, Danmark, Finland, France, W. Germany, Gibraltar, Greece, Italy, Luxemburg, Netherlands, Norway, Portugal, Spain, Sweden, Switzerland)" },795{ 68, "European 1950 (Mean for Austria, Denmark, France, W. Germany, Netherland, Switzerland)" },796{ 69, "European 1950 (Mean for Iraq, Israel, Jordan, Lebanon, Kuwait, Saudi Arabia, Syria)" },797{ 70, "European 1950 (Portugal, Spain)" },798{ 71, "European 1950 (Tunisia)" },799{ 72, "European 1950 (Mean for Austria, Finland, Netherlands, Norway, Spain, Sweden, Switzerland)" },800{ 73, "Fort Thomas 1955 (Nevis St. Kitts (Leeward Islands))" },801{ 74, "Gan 1970 (Republic of Maldives)" },802{ 75, "Geodetic Datum 1970 (New Zeland)" },803{ 76, "Graciosa Base SW1948 (Azores (Fial, Graciosa, Pico, Sao, Jorge, Terceria))" },804{ 77, "Guam1963 (Guam)" },805{ 78, "Gunung Segara (Indonesia (Kalimantan))" },806{ 79, "Gux I Astro (Guadalcanal Island)" },807{ 80, "Herat North (Afganistan)" },808{ 81, "Hermannskogel Datum (Croatia-Serbia, Bosnia-Herzegovina)" },809{ 82, "Hjorsey 1955 (Iceland)" },810{ 83, "Hongkong 1963 (Hongkong)" },811{ 84, "Hu Tzu Shan (Taiwan)" },812{ 85, "Indian (Bangladesh)" },813{ 86, "Indian (India, Nepal)" },814{ 87, "Indian (Pakistan)" },815{ 88, "Indian 1954 (Thailand)" },816{ 89, "Indian 1960 (Vietnam (Con Son Island)" },817{ 90, "Indian 1960 (Vietnam (Near 16 deg N))" },818{ 91, "Indian 1975 (Thailand)" },819{ 92, "Indonesian 1974 (Indonesian)" },820{ 93, "Ireland 1965 (Ireland)" },821{ 94, "ISTS 061 Astro 1968 (South Georgia Islands)" },822{ 95, "ISTS 073 Astro 1969 (Diego Garcia)" },823{ 96, "Johnston Island 1961 (Johnston Island)" },824{ 97, "Kandawala (Sri Lanka)" },825{ 98, "Kerguelen Island 1949 (Kerguelen Island)" },826{ 99, "Kertau 1948 (West Malaysia and Singapore)" },827{ 100, "Kusaie Astro 1951 (Caroline Island)" },828{ 101, "Korean Geodetic System (South Korea)" },829{ 102, "LC5 Astro 1961 (Cayman Brac Island)" },830{ 103, "Leigon (Ghana)" },831{ 104, "Liberia 1964 (Liberia)" },832{ 105, "Luzon (Philippines (Excluding Mindanao))" },833{ 106, "Luzon (Philippines (Mindanao))" },834{ 107, "M\'Poraloko (Gabon)" },835{ 108, "Mahe 1971 (Mahe Island)" },836{ 109, "Massawa (Ethopia (Eritrea))" },837{ 110, "Merchich (Morocco)" },838{ 111, "Midway Astro 1961 (Midway Islands)" },839{ 112, "Minna (Cameroon)" },840{ 113, "Minna (Nigeria)" },841{ 114, "Montserrat Island Astro 1958 (Monserat (Leeward Island))" },842{ 115, "Nahrwan (Oman (Masirah Island))" },843{ 116, "Nahrwan (Saudi Arabia)" },844{ 117, "Nahrwan (United Arab Emirates)" },845{ 118, "Naparima BWI (Trinidad and Tobago)" },846{ 119, "North American 1927 (Alaska (Excluding Aleutian Islands))" },847{ 120, "North American 1927 (Alaska (Aleutian Islands East of 180 deg. W)" },848{ 121, "North American 1927 (Alaska (Aleutian Islands West of 180 deg. W)" },849{ 122, "North American 1927 (Bahamas (Except San Salvador Islands))" },850{ 123, "North American 1927 (Bahamas (San Salvador Islands))" },851{ 124, "North American 1927 (Canada (Alberta, British Columbia))" },852{ 125, "North American 1927 (Canada (Manitoba, Ontario))" },853{ 126, "North American 1927 (Canada (New Brunswick, Newfoundland, Nova Scotia, Quebec))" },854{ 127, "North American 1927 (Canada (Northwest Territories, Saskatchewan))" },855{ 128, "North American 1927 (Canada (Youkon))" },856{ 129, "North American 1927 (Canal Zone)" },857{ 130, "North American 1927 (Cuba)" },858{ 131, "North American 1927 (Greenland (Hayes Peninsula))" },859{ 132, "North American 1927 (Mean for Antigua, Barbados, Barbuda, Caicos Islands, Cuba, Dominican, Grand Cayman, Jamaica, Turks Islands)" },860{ 133, "North American 1927 (Mean for Belize, Costa Rica, El Salvador, Guatemala, Honduras, Nicaragua)" },861{ 134, "North American 1927 (Mean for Canada)" },862{ 135, "North American 1927 (Mean for Conus)" },863{ 136, "North American 1927 (Mean for Conus (East of Mississippi, River Including Louisiana, Missouri, Minnesota)" },864{ 137, "North American 1927 (Mean for Conus (West of Mississippi, River Excluding Louisiana, Missouri, Minnesota)" },865{ 138, "North American 1927 (Mexico)" },866{ 139, "North American 1983 (Alaska (Excluding Aleutian Islands))" },867{ 140, "North American 1983 (Aleutian Islands)" },868{ 141, "North American 1983 (Canada)" },869{ 142, "North American 1983 (Conus)" },870{ 143, "North American 1983 (Hahawii)" },871{ 144, "North American 1983 (Mexico, Central America)" },872{ 145, "North Sahara 1959 (Algeria)" },873{ 146, "Observatorio Meteorologico 1939 (Azores (Corvo and Flores Islands))" },874{ 147, "Old Egyptian 1907 (Egypt)" },875{ 148, "Old Hawaiian (Hawaii)" },876{ 149, "Old Hawaiian (Kauai)" },877{ 150, "Old Hawaiian (Maui)" },878{ 151, "Old Hawaiian (Mean for Hawaii, Kauai, Maui, Oahu)" },879{ 152, "Old Hawaiian (Oahu)" },880{ 153, "Oman (Oman)" },881{ 154, "Ordnance Survey Great Britain 1936 (England)" },882{ 155, "Ordnance Survey Great Britain 1936 (England, Isle of Man, Wales)" },883{ 156, "Ordnance Survey Great Britain 1936 (Mean for England, Isle of Man, Scotland, Shetland Islands, Wales)" },884{ 157, "Ordnance Survey Great Britain 1936 (Scotland, Shetland Islands)" },885{ 158, "Ordnance Survey Great Britain 1936 (Wales)" },886{ 159, "Pico de las Nieves (Canary Islands)" },887{ 160, "Pitcairn Astro 1967 (Pitcairn Island)" },888{ 161, "Point 58 (Mean for Burkina Faso and Niger)" },889{ 162, "Pointe Noire 1948 (Congo)" },890{ 163, "Porto Santo 1936 (Porto Santo, Maderia Islands)" },891{ 164, "Provisional South American 1956 (Bolivia)" },892{ 165, "Provisional South American 1956 (Chilie (Norther Near 19 deg S))" },893{ 166, "Provisional South American 1956 (Chilie (Southern Near 43 deg S))" },894{ 167, "Provisional South American 1956 (Colombia)" },895{ 168, "Provisional South American 1956 (Ecuador)" },896{ 169, "Provisional South American 1956 (Guyana)" },897{ 170, "Provisional South American 1956 (Mean for Bolivia, Chilie, Colombia, Ecuador, Guyana, Peru, Venezuela)" },898{ 171, "Provisional South American 1956 (Peru)" },899{ 172, "Provisional South American 1956 (Venezuela)" },900{ 173, "Provisional South Chilean 1963 (Chilie (Near 53 deg S) (Hito XVIII))" },901{ 174, "Puerto Rico (Puerto Rico, Virgin Islands)" },902{ 175, "Pulkovo 1942 (Russia)" },903{ 176, "Quatar National (Quatar)" },904{ 177, "Qornoq (Greenland (South))" },905{ 178, "Reunion (Mascarene Island)" },906{ 179, "Rome 1940 (Italy (Sardinia))" },907{ 180, "S-42 (Pulkovo 1942) (Hungary)" },908{ 181, "S-42 (Pulkovo 1942) (Poland)" },909{ 182, "S-42 (Pulkovo 1942) (Czechoslavakia)" },910{ 183, "S-42 (Pulkovo 1942) (Latvia)" },911{ 184, "S-42 (Pulkovo 1942) (Kazakhstan)" },912{ 185, "S-42 (Pulkovo 1942) (Albania)" },913{ 186, "S-42 (Pulkovo 1942) (Romania)" },914{ 187, "S-JTSK (Czechoslavakia (Prior 1 Jan 1993))" },915{ 188, "Santo (Dos) 1965 (Espirito Santo Island)" },916{ 189, "Sao Braz (Azores (San Miguel, Santa Maria Islands))" },917{ 190, "Sapper Hill" },918{ 191, "Schwarzeck (Namibia)" },919{ 192, "Selvagem Grande 1938 (Salvage Islands)" },920{ 193, "Sierra Leone 1960 (Sierra Leone)" },921{ 194, "South American 1969 (Argentina)" },922{ 195, "South American 1969 (Bolivia)" },923{ 196, "South American 1969 (Brazil)" },924{ 197, "South American 1969 (Chilie)" },925{ 198, "South American 1969 (Colombia)" },926{ 199, "South American 1969 (Ecuador)" },927{ 200, "South American 1969 (Ecuador (Baltra, Galapagos))" },928{ 201, "South American 1969 (Guyana)" },929{ 202, "South American 1969 (Mean for Argentina, Bolivia, Brazil, Chilie, Colombia, Ecuador, Guayana, Paraguay, Peru, Trinidad and Tobago, Venezuela)" },930{ 203, "South American 1969 (Paraguay)" },931{ 204, "South American 1969 (Peru)" },932{ 205, "South American 1969 (Trinidad and Tobago)" },933{ 206, "South American 1969 (Venezuela)" },934{ 207, "South Asia (Singapore)" },935{ 208, "Tananarive Observatory 1925 (Madagascar)" },936{ 209, "Timbalai 1948 (Brunei, E. Malaysia (Sabah Sarawak))" },937{ 210, "Tokyo (Japan)" },938{ 211, "Tokyo (Mean for Japan, South Korea, Okinawa)" },939{ 212, "Tokyo (Okinawa)" },940{ 213, "Tokyo (South Korea)" },941{ 214, "Tristan Astro 1968 (Tristam Da Cunha)" },942{ 215, "Viti Levu 1916 (Fiji (Viti Levu Island))" },943{ 216, "Voirol 1960 (Algeria)" },944{ 217, "Wake Island Astro 1952 (Wake Atoll)" },945{ 218, "Wake-Eniwetok 1960 (Marshall Islands)" },946{ 219, "WGS 1972 (Global Definition)" },947{ 220, "WGS 1984 (Global Definition)" },948{ 221, "Yacare (Uruguay)" },949{ 222, "Zanderij (Suriname)" }950};951
952#endregion953
954#region Sentences formats955
956/// <summary>957/// Format string for sentences must be as "param_1_format, param_2_format, ..."958/// valid formats are:959/// x - integer960/// x.x - double961/// c--c - ASCII char string962/// hh - hexadecimal byte963/// hhhh - hexadecimal short int964/// hhhhhhhh - hexadecimal int965/// hhmmss.ss - time: hours, minutes, seconds, fractional part of seconds966/// llll.ll - longitude967/// yyyyy.yy - latitude968/// </summary>969
970#region Standart sentences971
972private static Dictionary<SentenceIdentifiers, string> SentencesFormats =973new Dictionary<SentenceIdentifiers, string>() { { SentenceIdentifiers.AAM, "A=Arrival circled entered|V=V,A=Perpendicular passed at way point|V=V,x.x,N=nm|K=km,c--c" },974{ SentenceIdentifiers.ALM, "x.x,x.x,xx,x.x,hh,hhhh,hh,hhhh,hhhhhh,hhhhhh,hhhhhh,hhhhhh,hhh,hhh" },975{ SentenceIdentifiers.APB, "V=Loran-C Blink or SNR warning|A=general warning flag,V=Loran-C Cycle Lock warning flag|A=OK or not used,x.x,R=Right|L=Left,N=nm|K=km,A=Arrival circled entered|V=Invalid,A=Perpendicular passed at way point|V=Invalid,x.x,M=Magnetic|T=True,c--c,x.x,M=Magnetic|T=True,x.x,M=Magnetic|T=True" },976{ SentenceIdentifiers.APA, "V=Loran-C Blink or SNR warning|A=general warning flag,V=Loran-C Cycle Lock warning flag|A=OK or not used,x.xx,R=Right|L=Left,N=nm|K=km,A=Arrival circled entered|V=Invalid,A=Perpendicular passed at way point|V=Invalid,xxx,M=Magnetic|T=True,c---c" },977{ SentenceIdentifiers.ASD, "" },978{ SentenceIdentifiers.BEC, "hhmmss.ss,llll.ll,N=N|S=S,yyyyy.y,N=N|S=S,x.x,E=E|W=W,x.x,T=True|M=Magnetic,x.x,N=nm|K=km,c--c" },979{ SentenceIdentifiers.BOD, "x.x,T=True|M=Magnetic,x.x,T=True|M=Magnetic,c--c,c--c" },980{ SentenceIdentifiers.BWC, "hhmmss.ss,llll.ll,N=N|S=S,yyyyy.yy,E=E|W=W,x.x,T=True|M=Magnetic,x.x,T=True|M=Magnetic,x.x,N=nm|K=km,c--c" },981{ SentenceIdentifiers.BWR, "hhmmss.ss,llll.ll,N=N|S=S,yyyyy.yy,E=E|W=W,x.x,T=True|M=Magnetic,x.x,T=True|M=Magnetic,x.x,N=nm|K=km,c--c" },982{ SentenceIdentifiers.BWW, "x.x,T=True|M=Magnetic,x.x,T=True|M=Magnetic,c--c,c--c" },983{ SentenceIdentifiers.DBK, "x.x,f=ft|M=m,x.x,f=ft|M=m,x.x,F" },984{ SentenceIdentifiers.DBS, "x.x,f=ft|M=m,x.x,f=ft|M=m,x.x,F" },985{ SentenceIdentifiers.DBT, "x.x,f=ft|M=m,x.x,f=ft|M=m,x.x,F" },986{ SentenceIdentifiers.DCN, "xx,cc,x.x,A,cc,x.x,A,cc,x.x,A,A,A,A,x.x,N=nm|K=km,1=Normal pattern|2=Lane identification pattern|3=Lane identification transmissions" },987{ SentenceIdentifiers.DPT, "x.x,x.x" },988{ SentenceIdentifiers.DSC, "" },989{ SentenceIdentifiers.DSE, "" },990{ SentenceIdentifiers.DSI, "" },991{ SentenceIdentifiers.DSR, "" },992{ SentenceIdentifiers.DTM, "xxx,x,xx.xxxx,x,xx.xxxx,x,c--c,xxx" },993{ SentenceIdentifiers.FSI, "xxxxxx,xxxxxx,c,x" },994{ SentenceIdentifiers.GBS, "hhmmss.ss,x.x,x.x,x.x,x.x,x.x,x.x,x.x" },995{ SentenceIdentifiers.GGA, "hhmmss.ss,llll.ll,a,yyyyy.yy,a,0=Fix not availible|1=GPS fix|2=DGPS fix,xx,x.x,x.x,M,x.x,M,x.x,xxxx" },996{ SentenceIdentifiers.GLC, "xxxx,x.x,a,x.x,a,x.x,a,x.x,a,x.x,a,x.x,B=Blink|C=Cycle|S=SNR|A=Valid" },997{ SentenceIdentifiers.GLL, "llll.ll,N=N|S=S,yyyyy.yy,E=E|W=W,hhmmss.ss,A=Valid|V=Invalid,A=Valid|V=Invalid" },998{ SentenceIdentifiers.GNS, "hhmmss.ss,llll.ll,a,yyyyy.yy,a,с--с,xx,x.x,x.x,x.x,x.x,x,a" },999{ SentenceIdentifiers.GRS, "hhmmss,x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x" },1000
1001/// Missed last paramter in the sentence description, 27 APR 20221002// $GNGSA,A, 3,01,03,04,22,31,09,06, , , , , ,1.57,0.82,1.34,1*051003// GSA,"M=Manual|A=Automatic,x,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,x.x ,x.x ,x.x,x" },1004{ SentenceIdentifiers.GSA, "M=Manual|A=Automatic,x,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,x.x,x.x,x.x,x" }, //1005{ SentenceIdentifiers.GST, "hhmmss.ss,x.x,x.x,x.x,x.x,x.x,x.x,x.x" },1006{ SentenceIdentifiers.GSV, "x,x,xx,xx,xx,xxx,xx,xx,xx,xxx,xx,xx,xx,xxx,xx,xx,xx,xxx,xx," },1007{ SentenceIdentifiers.GTD, "x.x,x.x,x.x,x.x,x.x" },1008{ SentenceIdentifiers.GXA, "hhmmss.ss,llll.ll,a,yyyyy.yy,a,c--c,x" },1009{ SentenceIdentifiers.HDG, "x.x,x.x,a,x.x,a" },1010{ SentenceIdentifiers.HDM, "x.x,M" },1011{ SentenceIdentifiers.HDT, "x.x,T" },1012{ SentenceIdentifiers.HEV, "x.x,A" },1013{ SentenceIdentifiers.HSC, "x.x,T,x.x,M" },1014{ SentenceIdentifiers.LCD, "xxxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx" },1015{ SentenceIdentifiers.MSK, "xxx.x,xx,xxx,xx,N" },1016{ SentenceIdentifiers.MSS, "" },1017{ SentenceIdentifiers.MTW, "x.x,C=C|K=K|F=F" },1018{ SentenceIdentifiers.MWD, "" },1019{ SentenceIdentifiers.MWV, "x.x,a,x.x,a,A" },1020{ SentenceIdentifiers.OLN, "aa,xxx,xxx,aa,xxx,xxx,aa,xxx,xxx" },1021{ SentenceIdentifiers.OSD, "x.x,A,x.x,a,x.x,a,x.x,x.x,a" },1022{ SentenceIdentifiers.RMA, "A,llll.ll,N=N|S=S,yyyyy.yy,E=E|W=W,x.x,x.x,x.x,x.x,x.x,a" },1023{ SentenceIdentifiers.RMB, "A,x.x,a,c--c,c--c,llll.ll,a,yyyyy.yy,a,x.x,x.x,x.x,A,A" },1024{ SentenceIdentifiers.RMC, "hhmmss.ss,A=Valid|V=Invalid,llll.ll,N=N|S=S,yyyyy.yy,E=E|W=W,x.x,x.x,ddmmyy,x.x,a,a,..." },1025{ SentenceIdentifiers.ROO, "c---c,...." }, // TODO: !!!1026{ SentenceIdentifiers.ROT, "x.x,A" },1027{ SentenceIdentifiers.RPM, "a,x,x.x,x.x,A" },1028{ SentenceIdentifiers.RSA, "x.x,A,x.x,A" },1029{ SentenceIdentifiers.RSD, "x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,a,a" },1030{ SentenceIdentifiers.RTE, "x.x,x.x,a,c--c,c--c,..." },1031{ SentenceIdentifiers.SFI, "x.x,x.x,xxxxxx,c,xxxxxx,c" },1032{ SentenceIdentifiers.STN, "xx" },1033{ SentenceIdentifiers.TLL, "xx,llll.lll,a,yyyyy.yyy,a,c--c,hhmmss.ss,a,a" },1034{ SentenceIdentifiers.TRF, "hhmmss.ss,xxxxxx,llll.ll,a,yyyyy.yy,a,x.x,x.x,x.x,x.x,xxx,A" },1035{ SentenceIdentifiers.TTM, "xx,x.x,x.x,T=True|R=Relative,x.x,x.x,T=True|R=Relative,x.x,x.x,K=Km|N=Knots|S=Statue miles,c--c,L=Lost|Q=Query|T=Tracking,a,hhmmss.ss,A=Automatic|M=Manual" },1036{ SentenceIdentifiers.TXT, "x,x,x,c--c" },1037{ SentenceIdentifiers.VBW, "x.x,x.x,A,x.x,x.x,A" },1038{ SentenceIdentifiers.VDR, "x.x,T,x.x,M,x.x,N" },1039{ SentenceIdentifiers.VHW, "x.x,T,x.x,M,x.x,N,x.x,K" },1040{ SentenceIdentifiers.VLW, "x.x,N,x.x,N" },1041{ SentenceIdentifiers.VPW, "x.x,N,x.x,M" },1042{ SentenceIdentifiers.VTG, "x.x,T,x.x,M,x.x,N=nk/h,x.x,K=Km/h,a" },1043{ SentenceIdentifiers.VWR, "x.x,a,x.x,N,x.x,M,x.x,K" },1044{ SentenceIdentifiers.WCV, "x.x,N,c--c" },1045{ SentenceIdentifiers.WDC, "" },1046{ SentenceIdentifiers.WDR, "" },1047{ SentenceIdentifiers.WNC, "x.x,N,x.x,K,c--c,c--c" },1048{ SentenceIdentifiers.WPL, "llll.ll,a,yyyyy.yy,a,c--c" },1049{ SentenceIdentifiers.XDR, "a,x.x,a,c--c,a,x.x,a,c--c" },1050{ SentenceIdentifiers.XTE, "A,A,x.x,a,N" },1051{ SentenceIdentifiers.XTR, "x.x,a,N" },1052{ SentenceIdentifiers.ZDA, "hhmmss.ss,xx,xx,xxxx,xx,xx" },1053{ SentenceIdentifiers.ZDL, "hhmmss.ss,hhmmss.ss,c--c" },1054{ SentenceIdentifiers.ZFO, "hhmmss.ss,hhmmss.ss,c--c" },1055{ SentenceIdentifiers.ZTG, "hhmmss.ss,hhmmss.ss,c--c" }1056};1057
1058#endregion1059
1060#region Known proprietary sentences1061
1062private static Dictionary<ManufacturerCodes, Dictionary<string, string>> ProprietarySentencesFormats =1063new Dictionary<ManufacturerCodes, Dictionary<string, string>>()1064{1065#region Garmin corporation1066
1067{1068ManufacturerCodes.GRM, // Garmin corporation1069new Dictionary<string, string>()1070{1071{"B", "x.x,x,x,x,x.x,a,0=Check Wiring|1=No Signal|2=Tuning|3=Receiving|4=Scanning,R=RTCM|W=WAAS|N=Non DGPS fix,A=Automatic|W=WAAS Only|R=RTCM Only|N=None"},1072{"E", "x.x,a,x.x,a,x.x,a"},1073{"F", "x,x,ddmmyy,hhmmss,x,dddmm.mmmm,N=N|S=S,dddmm.mmmm,E=E|W=W,M=Maual|A=Automatic,0=NoFix|1=2DFix|2=3DFix,x.x,x.x,x.x,x.x"},1074{"M", "c--c"},1075{"T", "c--c,P=Pass|F=Fail,P=Pass|F=Fail,R=Retained|L=Lost,R=Retained|L=Lost,P=Pass|F=Excessive Drift Detected,C=Collecting|Null=Not Collecting,x.x,R=Retained|L=Lost"},1076{"V", "x.x,x.x,x.x"},1077{"Z", "x.x,F=Feet,2=User Altitude|3=GPS Altitude"},1078{"C", "A=Automatic|2=2D Exclusively|3=3D Exclusively,x.x,x,x.x,x,x,x,x,A=Automatic|D=Only Output Differential Fixes,1=1200|2=2400|3=4800|4=9600|5=19200|6=300|7=600,x,1=None|2=1Hz,x,x.x"},1079{"CE", ""},1080{"C1", "x,1=Off|2=On,1=Off|2=On,x.x,x,1=Off|2=On,1=Off|2=On,A=Automatic|W=WAAS Only|R=RTCM Only|N=None,P=Activated|N=Normal"},1081{"C1E", ""},1082{"I", "dddmm.mmm,N=N|S=S,dddmm.mmm,N=N|S=S,ddmmyy,hhmmss,A=Autolocate|R=Unit Reset"},1083{"IE", ""},1084{"O", "c--c,0=Disable|1=Enable|2=Disable All Except PSLIB|3=Enable All Except GPALM|4=Restore Defaults"}1085}1086},1087
1088#endregion1089
1090#region Martech Inc.1091
1092{1093ManufacturerCodes.MTK, // Martech, Inc.1094new Dictionary<string, string>()1095{1096{"001", "c--c,0=Invalid|1=Unsupported|2=Valid, action failed|3=Valid, action succeeded" }, // 001 PMTK ACK (Acknowledge command)1097{"101", "" }, /// Hot restart: use all available data in the NV Store1098{"102", "" }, /// Warm restart: don't use Ephemeris at re-start1099{"103", "" }, /// Cold restart: don't use Time, Position, Almanacs and Ephemeris data at re-start1100{"104", "" }, /// Full cold restart: it's essentially a Cold Restart, but additionaly clear system/user config at re-start1101/// That is, reset the receiver to the factory status1102{"251", "x"}, /// Set NMEA port baudrate, 0 means default, availible values: 4800, 9600, 14400, 19200, 38400, 57600, 1152001103{"300", "x,x,x,x,x"}, /// API_Set_Fix_Ctl, this parameter controls the rate of position fixing activity,1104{"301","0=No DGPS source|1=RTCM|2=WAAS"}, /// API_Set_Dgps_Mode, DGPS correction data source mode.1105{"313","0=Disable|1=Enable"}, /// API_Set_Sbas_Enabled. Enable to search a SBAS stellite or not.1106{"314","x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x"}, /// API_Set_NMEA_Out (NMEA sentence output frequencies)1107{"320","0=False|1=True"}, // API_Set_Pwr_Sav_Mode1108{"390","x,x,x,x,x,x,x,x,x,x,x,0=Disable|1=RTCM|2=SBAS"}, /// API_Set_Flash_User_Option1109{"420",""}, // API_Query_Pwr_Sav_Mode, return: PMTK5201110{"490",""}, // API_Get_Flash_User_Option, return: PMTK5901111{"520","0=False|1=True"}, // Power saving operation mode1112{"590","x,x,x,x,x,x,x,x,x,x,x,x,0=Disable|1=RTCM|2=SBAS,x"},1113{"605",""}, /// Query the firmware release information, return: PMTK7051114{"705","c--c,c--c,c--c"} /// Firmware release version1115}1116},1117
1118#endregion1119
1120#region Trimble Navigation1121
1122{1123ManufacturerCodes.TNL, // Trimble Navigation1124new Dictionary<string, string>()1125{1126{"DG", "x.x,x.x,x.x,x,x,0=Idle|1=Wideband FFT search|2=Searching for signal|3=Channel has acquired signal|4=Channel has locked signal|5=Channel disabled,0=False|1=True,x"}, /// DGPS receiver status1127{"EV", "hhmmss.ss,x"}, /// Event marker1128{",GGK", "hhmmss.ss,ddmmyy,llll.ll,N=North|S=South,dddmm.mm,E=East|W=West,0=Fix unavailible or invalid|1=Autonomous GPS fix|2=RTK float solution|3=RTK fixed solution|4=DGPS,x,x.x,c--c,M=m|f=ft"}, /// Time, Position, Position type, DOP1129{"ID", "c--c,c--c,x,x,dd/mm/yy"}, /// Trimble receiver ID1130{"SM", "xxxx,c--c"}, /// RTCM Special1131{",AVR", "hhmmss.ss,x.x,c--c,x.x,c--c,c--c,c--c,x.x,0=Not availible|1=Autonomous GNSS|2=Dif. carrier phase solution RTK (float)|3=Dif. carrier phase solution RTK (fix)|4=Dif. code-based solution (DGNSS),c--c,x" }, /// Time and attitude info (needs 2 receivers in vactor mode)1132{",BPQ", "hhmmss.ss,ddmmyy,llll.ll,N=North|S=South,yyyyy.yy,E=East|W=West,c--c,M=m|f=ft,0=Not availible|1=Autonomous GNSS fix|2=DGNSS or OmniSTART VBS fix|4=RTK fixed|5=OmniSTAR HP/XP/G2/Float RTK" }, /// Base station position and its quality1133{",PJK", "hhmmss.ss,ddmmyy,x.x,N=North,x.x,E=East,0=Not availible|1=Autonomous GNSS|2=RTK float|3=RTK fix|4=Dif. code-based solution (DGNSS)|5=SBAS|6=RTK float 3D|7=RTK fixed 3D|8=RTK float 2D|9=RTK fix 2D|10=OmniSTAR HP/XP/G2|11=OmniSTAR VBS|12=Location RTK|13=Beacon DGNSS,x,c--c,EHTx.x,M=m|f=ft"}, /// Time, Date and position in ENU format1134{",PJT", "c--c,c--c"}, /// Receiver projection1135{",VGK", "hhmmss.ss,ddmmyy,x.x,x.x,x.x,0=Not availible|1=Autonomous GNSS|2=RTK float|3=RTK fix|4=Dif. code-based solution (DGNSS)|5=SBAS|6=RTK float 3D|7=RTK fixed 3D|8=RTK float 2D|9=RTK fix 2D|10=OmniSTAR HP/XP/G2|11=OmniSTAR VBS|12=Location RTK|13=Beacon DGNSS,x,c--c,M=m|f=ft"}, /// Vector information1136{",VHD", "hhmmss.ss,ddmmyy,yyyyy.yy,x.x,lllll.ll,x.x,x.x,x.x,0=Not availible|1=Autonomous GNSS|2=RTK float|3=RTK fix|4=Dif. code-based solution (DGNSS)|5=SBAS|6=RTK float 3D|7=RTK fixed 3D|8=RTK float 2D|9=RTK fix 2D|10=OmniSTAR HP/XP/G2|11=OmniSTAR VBS|12=Location RTK|13=Beacon DGNSS,x,c--c"}, /// Heading information1137}1138},1139
1140#endregion1141
1142#region Fugro1143
1144{1145ManufacturerCodes.FUG,1146new Dictionary<string, string>()1147{1148{ "DP", "GP=GPS|GL=GLONASS|GN=GNSS,hhmmss.ss,llll.ll,N=North|S=South,yyyyy.yy,E=East|W=West,x,x,x.x,x.x,x.x,x.x" }, /// Positioning system type info1149}1150},1151
1152#endregion1153
1154#region Magellan1155
1156{1157ManufacturerCodes.MGN,1158new Dictionary<string, string>()1159{1160{ "CMD", "c--c,..." }, /// CMD, first parameter "VERSION" or "TRACK", see manufacturer's documentation1161{ "CSM", "hh" }, /// In handshake mode is used to acknowledge succsessful. Parameter - checksum for the last last message received1162{ "DRT", "xx" }, /// Delete route from memory. Paramter - zero based route number1163{ "DWP", "c--c,xx" }, /// Delete waypoint from memory.1164{ "RTE", "xx,xx,c,x,c--c,..." }, /// Route information1165{ "TRK", "llll.ll,N=North|S=South,yyyyy.yy,E=East|W=West,xxxxx,f=Ft|M=m,hhmmss.ss,A=Valid|V=Invalid,c--c,ddmmyy" }, /// Track information1166{ "VER", "x,x,c--c,c--c,c--c"}, /// Version information1167{ "WPL", "llll.ll,N=North|S=South,yyyyy.yy,W=West|E=East,xxxx,M=m|f=ft,c--c,c--c,c--c,xx" }, /// Waypoint info1168{ "ST", "c--c,2=2D|3=3D,T=True|F=False,x,x.x,x,x" }, /// Status information1169}1170},1171
1172#endregion1173
1174#region Motorola1175
1176{1177ManufacturerCodes.MOT,1178new Dictionary<string, string>()1179{1180{"G", "c--c,x"} /// Sentence frequency control1181}1182},1183
1184#endregion1185
1186#region Rockwell International1187
1188{1189ManufacturerCodes.RWI,1190new Dictionary<string, string>()1191{1192{"RID", "x,x.x,c--c,mm/dd/yy,hhhh"}, /// ?1193{"ILOG", "c--c,A=activate|V=deactivate,T=cyclic,x,x"} /// ?1194}1195},1196
1197#endregion1198
1199#region Starlink1200
1201{1202ManufacturerCodes.SLI,1203new Dictionary<string, string>()1204{1205{"B", "x.x,x,c--c,J=Status request|K=Configuration request|=tuning message"}, //1206}1207},1208
1209#endregion1210
1211#region SRF (update 08 dec 2013)1212
1213{1214ManufacturerCodes.SRF,1215new Dictionary<string, string>()1216{1217{"100", "0=BIN|1=NMEA,x,x,x,0=None|1=Even|2=Odd"}, // protocol type: 0 - binary, 1 - nmea1218// baudrate: 1200, 2400, 4800, 9600, 19200, 384001219// data bits: 8, 7 (BIN protocol requires 8)1220// stop bits: 0, 11221// parity: 0 - none, 1 - even, 2 - odd1222{"101", "x,x,x,x,x,x,x,x,x"}, // position X-coordinate1223// position Y-coordinate1224// position Z-coordinate1225// GPS receiver clock offset, Hz: 0 means last save value1226// Time of GPS week1227// Number of GPS week1228// Number of channels to use: 1..121229// Bitmask: 0x01 - data valid, warm start = 11230// 0x02 - clear ephemeris, warm start = 11231// 0x04 - clear memory, cold start = 11232{"102", "x,x,x,x"}, // baudrate: 1200, 2400, 4800, 9600, 19200, 384001233// data bits: 81234// stop bits: 0, 11235// parity: 0 - none, 1 - even, 2 - odd1236{"103", "0=GGA|1=GLL|2=GSA|3=GSV|4=RMC|5=VTG,x,x,x"}, // message type1237// mode: 0 - periodically, 1 - by request1238// rate: 0..255 (0 - means turned off)1239// 0 - checksum disabled, 1 - checksum enabled1240{"104", "x.x,x.x,x.x,x,x,x,x,x"}, // latitude (signed)1241// longitude (signed)1242// altitude (signed)1243// GPS receiver clock offset (0 - means use last saved value)1244// time of GPS week1245// number of GPS week1246// Channels to use 1..121247// bitmask: 0x01 - data valid, warm/hot start = 11248// 0x02 - clear ephemeris, warm start = 11249// 0x04 - clear memory, cold start = 11250{"105", "x"} // 0 - switch debug mode off, 1 - switch debug mode on1251}1252},1253
1254#endregion1255
1256#region Furuno1257
1258{1259ManufacturerCodes.FEC,1260new Dictionary<string, string>()1261{1262// $PFEC,hdcom,N,Er,00101263{ ",hdcom", "c--c,c--c,xxxx" }, // no data for the sentence :(1264}1265},1266
1267#endregion1268};1269
1270#endregion1271
1272#region Formatters1273
1274#if FRAMEWORK_LOWER_351275
1276private delegate object ParserDelegate(string source);1277private static Dictionary<string, ParserDelegate> parsers = new Dictionary<string, ParserDelegate>()1278{1279{ "x", x => int.Parse(x) },1280{ "xx", x => int.Parse(x) },1281{ "xxx", x => int.Parse(x) },1282{ "xxxx", x => int.Parse(x) },1283{ "xxxxx", x => int.Parse(x) },1284{ "xxxxxx", x => int.Parse(x) },1285{ "hh", x => Convert.ToByte(x, 16) },1286{ "hhhh", x => Convert.ToUInt16(x, 16) },1287{ "hhhhhh", x => Convert.ToUInt32(x, 16) },1288{ "hhhhhhhh", x => Convert.ToUInt32(x, 16) },1289{ "h--h", x => ParseByteArray(x) },1290{ "x.x", x => double.Parse(x, CultureInfo.InvariantCulture) },1291{ "c--c", x => x },1292{ "llll.ll", x => ParseLatitude(x) },1293{ "yyyyy.yy", x => ParseLongitude(x) },1294{ "hhmmss", x => ParseCommonTime(x) },1295{ "hhmmss.ss", x => ParseCommonTime(x) },1296{ "ddmmyy", x => ParseCommonDate(x) },1297{ "dd/mm/yy", x => ParseDateSlashes(x) },1298{ "dddmm.mmm", x => ParseCommonDegrees(x) }1299
1300};1301
1302private delegate string FormatterDelegate(object source);1303private static Dictionary<string, FormatterDelegate> formatters = new Dictionary<string, FormatterDelegate>()1304{1305{ "x", x => ((int)(x)).ToString() },1306{ "xx", x => ((int)(x)).ToString("D2") },1307{ "xxx", x => ((int)(x)).ToString("D3") },1308{ "xxxx", x => ((int)(x)).ToString("D4") },1309{ "xxxxx", x => ((int)(x)).ToString("D5") },1310{ "xxxxxx", x => x.ToString() },1311{ "hh", x => ((byte)x).ToString("X2") },1312{ "hhhh", x => ((ushort)x).ToString("X4") },1313{ "hhhhhh", x => ((uint)x).ToString("X6") },1314{ "hhhhhhhh", x => ((uint)x).ToString("X8") },1315{ "h--h", x => FormatByteArray((byte[])x) },1316{ "x.x", x => ((double)x).ToString("F04", CultureInfo.InvariantCulture).TrimEnd(new char[] {'0'}) },1317{ "c--c", x => x.ToString() },1318{ "llll.ll", x => FormatLatitude((double)x) },1319{ "yyyyy.yy", x => FormatLongitude((double)x) },1320{ "hhmmss", x => FormatTime((DateTime)x, false) },1321{ "hhmmss.ss", x => FormatTime((DateTime)x, true) },1322{ "ddmmyy", x => FormatDate((DateTime)x) },1323{ "dd/mm/yy", x => FormatDateSlashes((DateTime)x) },1324{ "dddmm.mmm", x => FormatDegree((double)x) }1325};1326
1327#else1328private static Dictionary<string, Func<string, object>> parsers = new Dictionary<string, Func<string, object>>()1329{1330{ "x", x => int.Parse(x) },1331{ "xx", x => int.Parse(x) },1332{ "xxx", x => int.Parse(x) },1333{ "xxxx", x => int.Parse(x) },1334{ "xxxxx", x => int.Parse(x) },1335{ "xxxxxx", x => int.Parse(x) },1336{ "hh", x => Convert.ToByte(x, 16) },1337{ "hhhh", x => Convert.ToUInt16(x, 16) },1338{ "hhhhhh", x => Convert.ToUInt32(x, 16) },1339{ "hhhhhhhh", x => Convert.ToUInt32(x, 16) },1340{ "h--h", x => ParseByteArray(x) },1341{ "x.x", x => double.Parse(x, CultureInfo.InvariantCulture) },1342{ "c--c", x => x },1343{ "llll.ll", x => ParseLatitude(x) },1344{ "yyyyy.yy", x => ParseLongitude(x) },1345{ "hhmmss", x => ParseCommonTime(x) },1346{ "hhmmss.ss", x => ParseCommonTime(x) },1347{ "ddmmyy", x => ParseCommonDate(x) },1348{ "dddmm.mmmm", x => ParseCommonDegrees(x) }//,1349//{ "ddd.dddd", x => ParseDecDegrees(x) }1350};1351
1352private static Dictionary<string, Func<object, string>> formatters = new Dictionary<string, Func<object, string>>()1353{1354{ "x", x => ((int)(x)).ToString() },1355{ "xx", x => ((int)(x)).ToString("D2") },1356{ "xxx", x => ((int)(x)).ToString("D3") },1357{ "xxxx", x => ((int)(x)).ToString("D4") },1358{ "xxxxx", x => ((int)(x)).ToString("D5") },1359{ "xxxxxx", x => x.ToString() },1360{ "hh", x => ((byte)x).ToString("X2") },1361{ "hhhh", x => ((ushort)x).ToString("X4") },1362{ "hhhhhh", x => ((uint)x).ToString("X6") },1363{ "hhhhhhhh", x => ((uint)x).ToString("X8") },1364{ "h--h", x => FormatByteArray((byte[])x) },1365{ "x.x", x => ((double)x).ToString("F06", CultureInfo.InvariantCulture).TrimEnd(new char[] {'0'}) },1366{ "c--c", x => x.ToString() },1367{ "llll.ll", x => FormatLatitude((double)x) },1368{ "yyyyy.yy", x => FormatLongitude((double)x) },1369{ "hhmmss", x => FormatTime((DateTime)x, false) },1370{ "hhmmss.ss", x => FormatTime((DateTime)x, true) },1371{ "ddmmyy", x => FormatDate((DateTime)x) },1372{ "dd/mm/yy", x => FormatDateSlashes((DateTime)x) },1373{ "dddmm.mmmm", x => FormatDegree((double)x) },1374{ "ddd.dddd", x => FormatDecDegree((double)x) }1375};1376#endif1377
1378#endregion1379
1380#endregion1381
1382#region Checksum1383
1384#if FRAMEWORK_LOWER_351385public delegate byte CheckSumEvaluatorDelegate(string source);1386public static CheckSumEvaluatorDelegate CheckSumEvaluator = new CheckSumEvaluatorDelegate(GetCheckSum);1387#else1388public static Func<string, byte> CheckSumEvaluator = new Func<string, byte>(GetCheckSum);1389#endif1390
1391#endregion1392
1393#endregion1394
1395#endregion1396
1397#region Methods1398
1399#region Sentence building1400
1401/// <summary>1402/// Builds sentence by talker & sentence IDs and parameters, specified as object[]1403/// </summary>1404/// <param name="talkerID">Talker identifier</param>1405/// <param name="sentenceID">Sentence identifier</param>1406/// <param name="parameters">Sentence parameters</param>1407/// <returns>NMEA0183 sentence as string</returns>1408public static string BuildSentence(TalkerIdentifiers talkerID, SentenceIdentifiers sentenceID, object[] parameters)1409{1410var formatter = SentencesFormats[sentenceID];1411
1412StringBuilder sb = new StringBuilder();1413sb.Append(talkerID.ToString());1414sb.Append(sentenceID.ToString());1415sb.Append(FieldDelimiter);1416sb.Append(BuildParametersList(parameters, formatter));1417
1418var checkSum = GetCheckSum(sb.ToString());1419
1420sb.Append(CheckSumFieldDelimiter);1421sb.Append(checkSum.ToString("X2"));1422sb.Append(SentenceEndDelimiter);1423
1424sb.Insert(0, SentenceStartDelimiter);1425
1426return sb.ToString();1427}1428
1429/// <summary>1430/// Builds proprietary sentence1431/// </summary>1432/// <param name="manufacturerID">Manufacturer ID</param>1433/// <param name="sentenceIDString">Sentence ID string</param>1434/// <param name="parameters">Sentence parameters</param>1435/// <returns>NMEA0182 sentence as string</returns>1436public static string BuildProprietarySentence(ManufacturerCodes manufacturerID, string sentenceIDString, object[] parameters)1437{1438var formatter = ProprietarySentencesFormats[manufacturerID][sentenceIDString];1439
1440StringBuilder sb = new StringBuilder();1441sb.Append(TalkerIdentifiers.P.ToString());1442sb.Append(manufacturerID.ToString());1443sb.Append(sentenceIDString);1444sb.Append(FieldDelimiter);1445sb.Append(BuildParametersList(parameters, formatter));1446
1447var checkSum = GetCheckSum(sb.ToString());1448
1449sb.Append(CheckSumFieldDelimiter);1450sb.Append(checkSum.ToString("X2"));1451sb.Append(SentenceEndDelimiter);1452
1453sb.Insert(0, SentenceStartDelimiter);1454
1455return sb.ToString();1456}1457
1458private static string BuildParametersList(object[] items, string formatter)1459{1460var splits = formatter.Split(formatTokenDelimiters);1461
1462if ((splits.Length >= items.Length) || (formatter.Contains(etcFormat)))1463{1464StringBuilder sb = new StringBuilder();1465
1466string previousFormat = "c--c";1467for (int i = 0; i < items.Length; i++)1468{1469if (splits.Length <= i)1470{1471sb.Append(FormatToken(items[i], previousFormat));1472}1473else1474{1475sb.Append(FormatToken(items[i], splits[i]));1476previousFormat = splits[i];1477}1478
1479if (i != items.Length - 1)1480sb.Append(FieldDelimiter);1481}1482
1483return sb.ToString();1484}1485else1486{1487throw new ArgumentException("Specified parameters list and formatting string incompatible");1488}1489}1490
1491private static string FormatToken(object item, string format)1492{1493if (item == null)1494return string.Empty;1495
1496if (format.Contains(formatEnumPairDelimiter))1497{1498var items = format.Split(formatEnumDelimiters);1499Dictionary<string, string> enumDictionary = new Dictionary<string, string>();1500
1501for (int i = 0; i < items.Length; i++)1502{1503var pair = items[i].Split(formatEnumPairDelimiters);1504if (pair.Length == 2)1505{1506enumDictionary.Add(pair[1], pair[0]);1507}1508else1509{1510throw new ArgumentException(string.Format("Error in format token \"{0}\"", format));1511}1512}1513
1514if (enumDictionary.ContainsKey(item.ToString()))1515{1516return enumDictionary[item.ToString()];1517}1518else1519{1520throw new ArgumentException(string.Format("Specified object \"{0}\" cannot be formatter with format string \"{1}\"", item.ToString(), format));1521}1522}1523else1524{1525if (format.StartsWith(arrayOpenBracket) && format.EndsWith(arrayCloseBracket))1526{1527return FormatArray(item, format.Trim(arrayBrackets));1528}1529else1530{1531if (formatters.ContainsKey(format))1532{1533return formatters[format](item);1534}1535else1536{1537return item.ToString();1538}1539}1540}1541}1542
1543#region Heuristic formatters1544
1545private static string FormatArray(object item, string format)1546{1547if (item is Array)1548{1549Array array = item as Array;1550StringBuilder sb = new StringBuilder();1551
1552for (int i = 0; i < array.Length; i++)1553{1554sb.Append(FormatToken(array.GetValue(i), format));1555
1556if (i < array.Length - 1)1557sb.Append(formatEnumDelimiters[0]);1558}1559
1560return sb.ToString();1561}1562else1563{1564throw new ArgumentException(string.Format("Unable cast \"{0}\" to array", item.GetType().Name));1565}1566}1567
1568private static string FormatLatitude(double x)1569{1570StringBuilder sb = new StringBuilder();1571
1572double degrees = Math.Floor(x);1573double minutes = Math.Floor((x - degrees) * 60.0);1574double seconds = (x - degrees) * 3600 - minutes * 60.0;1575
1576minutes += seconds / 60.0;1577
1578sb.Append(degrees.ToString("00"));1579sb.Append(minutes.ToString("00.0000", CultureInfo.InvariantCulture).TrimEnd(new char['0']));1580
1581return sb.ToString();1582}1583
1584private static string FormatLongitude(double x)1585{1586StringBuilder sb = new StringBuilder();1587
1588double degrees = Math.Floor(x);1589double minutes = Math.Floor((x - degrees) * 60.0);1590double seconds = (x - degrees) * 3600 - minutes * 60.0;1591
1592minutes += seconds / 60.0;1593
1594sb.Append(degrees.ToString("000"));1595sb.Append(minutes.ToString("00.0000", CultureInfo.InvariantCulture).TrimEnd(new char['0']));1596
1597return sb.ToString();1598}1599
1600private static string FormatTime(DateTime dt, bool isMilliseconds)1601{1602StringBuilder sb = new StringBuilder();1603sb.Append(dt.Hour.ToString("00"));1604sb.Append(dt.Minute.ToString("00"));1605sb.Append(dt.Second.ToString("00"));1606
1607if (isMilliseconds)1608{1609sb.Append('.');1610sb.Append(dt.Millisecond.ToString());1611}1612
1613return sb.ToString();1614}1615
1616private static string FormatDate(DateTime dt)1617{1618StringBuilder sb = new StringBuilder();1619sb.Append(dt.Day.ToString("00"));1620sb.Append(dt.Month.ToString("00"));1621sb.Append((dt.Year - 2000).ToString("00"));1622return sb.ToString();1623}1624
1625private static string FormatDateSlashes(DateTime dt)1626{1627StringBuilder sb = new StringBuilder();1628
1629sb.Append(dt.Day.ToString("00"));1630sb.Append('/');1631sb.Append(dt.Month.ToString("00"));1632sb.Append('/');1633sb.Append((dt.Year - 2000).ToString("00"));1634
1635return sb.ToString();1636}1637
1638private static string FormatDegree(double x)1639{1640StringBuilder sb = new StringBuilder();1641
1642double degrees = Math.Floor(x);1643double minutes = Math.Floor((x - degrees) * 60.0);1644double seconds = (x - degrees) * 3600 - minutes * 60.0;1645
1646minutes += seconds / 60.0;1647
1648sb.Append(degrees.ToString("000"));1649sb.Append(minutes.ToString("00.0000", CultureInfo.InvariantCulture).TrimEnd(new char['0']));1650
1651return sb.ToString();1652}1653
1654private static string FormatDecDegree(double x)1655{1656return x.ToString("000.0000", CultureInfo.InvariantCulture);1657}1658
1659public static string FormatByteArray(byte[] bytes)1660{1661StringBuilder sb = new StringBuilder();1662sb.Append("0x");1663
1664for (int i = 0; i < bytes.Length; i++)1665sb.Append(bytes[i].ToString("x2"));1666
1667return sb.ToString();1668}1669
1670#endregion1671
1672#endregion1673
1674#region Sentence parsing1675
1676/// <summary>1677/// Parse NMEA0183 sentence1678/// </summary>1679/// <param name="sourceString">string to parse</param>1680/// <returns>Parsed sentence as NMEASentese</returns>1681public static NMEASentence Parse(string sourceString)1682{1683if (!sourceString.EndsWith(SentenceEndDelimiter))1684{1685throw new ArgumentException(string.Format("{0} sentence must ends with valid sentence end delimiter \"{1}\"", StandartID, @"\r\n"));1686}1687
1688var source = sourceString.TrimEnd(SentenceEndDelimiter.ToCharArray());1689
1690if (source.Length > MaxSentenceLength)1691throw new ArgumentException(string.Format("Specified string \"{0}\" length exceeds {1} characters", source.Length, MaxSentenceLength));1692
1693if (!source.StartsWith(SentenceStartDelimiter.ToString()))1694throw new ArgumentException(string.Format("{0} sentence must starts with valid sentence start delimiter \"{1}\"", StandartID, SentenceStartDelimiter));1695
1696try1697{1698string sentence;1699string checkSumString;1700SplitSentence(source, out sentence, out checkSumString);1701
1702if (!string.IsNullOrEmpty(checkSumString))1703{1704byte declaredCheckSum;1705if (byte.TryParse(checkSumString, System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture, out declaredCheckSum))1706{1707byte realCheckSum = GetCheckSum(sentence);1708
1709if (realCheckSum == declaredCheckSum)1710{1711return ParseSentence(sentence);1712}1713else1714{1715throw new ArgumentException(string.Format("Integrity error in sentese \"{0}\" (declared: {1}, real: {2})", sentence, declaredCheckSum, realCheckSum));1716}1717}1718else1719{1720throw new ArgumentException(string.Format("Unable parse chesksum field from \"{0}\"", checkSumString));1721}1722}1723else1724{1725return ParseSentence(sentence);1726}1727}1728catch (Exception ex)1729{1730throw ex;1731}1732}1733
1734private static NMEAProprietarySentence ParseProprietary(string manufacturerIDString, string sentenceID, List<string> parameters)1735{1736ManufacturerCodes manufacturerID = ManufacturerCodes.unknown;1737try1738{1739manufacturerID = (ManufacturerCodes)Enum.Parse(typeof(ManufacturerCodes), manufacturerIDString);1740}1741catch1742{1743throw new ArgumentException(string.Format("Unknown manufacturer \"{0}\" in proprietary sentence", manufacturerIDString));1744}1745
1746if (ProprietarySentencesFormats.ContainsKey(manufacturerID))1747{1748if (ProprietarySentencesFormats[manufacturerID].ContainsKey(sentenceID))1749{1750NMEAProprietarySentence result = new NMEAProprietarySentence();1751result.Manufacturer = manufacturerID;1752result.SentenceIDString = sentenceID;1753
1754var formatter = ProprietarySentencesFormats[manufacturerID][sentenceID];1755result.parameters = ParseParameters(parameters, formatter);1756
1757return result;1758}1759else1760{1761throw new ArgumentException(string.Format("Unknown proprietary sentese \"{0}\", manufacturerID: \"{1}\"", sentenceID, manufacturerID));1762}1763}1764else1765{1766throw new ArgumentException(string.Format("Unknown manufacturerID \"{0}\", sentenceID: \"{1}\"", manufacturerID, sentenceID));1767}1768}1769
1770private static NMEAStandartSentence ParseSentence(TalkerIdentifiers talkerID, string sentenceIDString, List<string> parameters)1771{1772SentenceIdentifiers sentenceID = SentenceIdentifiers.unknown;1773try1774{1775sentenceID = (SentenceIdentifiers)Enum.Parse(typeof(SentenceIdentifiers), sentenceIDString);1776}1777catch1778{1779throw new ArgumentException(string.Format("Undefined sentence ID \"{0}\" from takler \"{1}\"", sentenceIDString, talkerID));1780}1781
1782var formatter = SentencesFormats[sentenceID];1783if (string.IsNullOrEmpty(formatter))1784{1785throw new ArgumentException(string.Format("Specified sentence \"{0}\" from talker \"{1}\" is unknown", sentenceID, talkerID));1786}1787
1788NMEAStandartSentence result = new NMEAStandartSentence();1789result.TalkerID = talkerID;1790result.SentenceID = sentenceID;1791
1792result.parameters = ParseParameters(parameters, formatter);1793
1794return result;1795}1796
1797private static NMEASentence ParseSentence(string source)1798{1799var splits = source.Split(FieldDelimiter.ToString().ToCharArray());1800List<string> parameters = new List<string>();1801
1802if (splits.Length > 1)1803{1804var sentenceDescription = splits[0];1805if (sentenceDescription.Length >= 4)1806{1807string talkerIDString;1808string sentenceIDString;1809
1810if (sentenceDescription.StartsWith(TalkerIdentifiers.P.ToString()))1811{1812// Proprietary code1813string manufacturerIDString = string.Empty;1814manufacturerIDString = sentenceDescription.Substring(1, 3);1815
1816int start = 1;1817if (sentenceDescription.Length == 4)1818{1819sentenceDescription += FieldDelimiter + splits[1];1820start = 2;1821}1822
1823sentenceIDString = sentenceDescription.Substring(4);1824
1825for (int i = start; i < splits.Length; i++)1826{1827parameters.Add(splits[i]);1828}1829
1830return ParseProprietary(manufacturerIDString, sentenceIDString, parameters);1831}1832else1833{1834// Not a proprietary code1835TalkerIdentifiers talkerID = TalkerIdentifiers.unknown;1836talkerIDString = sentenceDescription.Substring(0, 2);1837sentenceIDString = sentenceDescription.Substring(2, 3);1838
1839try1840{1841talkerID = (TalkerIdentifiers)Enum.Parse(typeof(TalkerIdentifiers), talkerIDString);1842}1843catch1844{1845throw new ArgumentException(string.Format("Undefined takler ID \"{0}\"", talkerIDString));1846}1847
1848for (int i = 1; i < splits.Length; i++)1849{1850parameters.Add(splits[i]);1851}1852
1853return ParseSentence(talkerID, sentenceIDString, parameters);1854}1855}1856else1857{1858throw new ArgumentException(string.Format("Wrong sentence description: \"{0}\"", sentenceDescription));1859}1860}1861else1862{1863throw new ArgumentException(string.Format("No field delimiters in specified sentence \"{0}\"", source));1864}1865}1866
1867private static void SplitSentence(string source, out string sentence, out string checkSumString)1868{1869var temp = source.TrimStart(SentenceStartDelimiter.ToString().ToCharArray());1870
1871if (source.Contains(CheckSumFieldDelimiter.ToString()))1872{1873
1874var splits = temp.Split(CheckSumFieldDelimiter.ToString().ToCharArray());1875
1876if (splits.Length == 2)1877{1878sentence = splits[0];1879checkSumString = splits[1];1880}1881else1882{1883throw new ArgumentException(string.Format("Specified string \"{0}\" not in correct {1} format", source, StandartID));1884}1885}1886else1887{1888sentence = temp;1889checkSumString = string.Empty;1890}1891}1892
1893private static object ParseToken(string token, string format)1894{1895if (string.IsNullOrEmpty(token))1896return null;1897
1898if (format.Contains(formatEnumPairDelimiter))1899{1900var items = format.Split(formatEnumDelimiters);1901Dictionary<string, string> enumDictionary = new Dictionary<string, string>();1902
1903for (int i = 0; i < items.Length; i++)1904{1905var pair = items[i].Split(formatEnumPairDelimiters);1906if (pair.Length == 2)1907{1908enumDictionary.Add(pair[0], pair[1]);1909}1910else1911{1912throw new ArgumentException(string.Format("Error in format token \"{0}\"", format));1913}1914}1915
1916if (enumDictionary.ContainsKey(token))1917{1918return enumDictionary[token];1919}1920else1921{1922return token;1923}1924}1925else1926{1927if (format.StartsWith(arrayOpenBracket) && token.EndsWith(arrayCloseBracket))1928{1929return ParseArray(token, format.Trim(arrayBrackets));1930}1931else1932{1933if (parsers.ContainsKey(format))1934{1935return parsers[format](token);1936}1937else1938{1939return token;1940}1941}1942}1943}1944
1945private static object[] ParseParameters(List<string> parameters, string formatString)1946{1947var formatTokens = formatString.Split(formatTokenDelimiters);1948if ((formatTokens.Length >= parameters.Count) || (formatString.Contains(etcFormat)))1949{1950List<object> results = new List<object>();1951
1952string previousFormatter = "c--c";1953for (int i = 0; i < parameters.Count; i++)1954{1955if (formatTokens.Length <= i)1956results.Add(ParseToken(parameters[i], previousFormatter));1957else1958{1959results.Add(ParseToken(parameters[i], formatTokens[i]));1960previousFormatter = formatTokens[i];1961}1962}1963
1964return results.ToArray();1965}1966else1967{1968throw new ArgumentException("Specified parameters list and formatting string incompatible");1969}1970}1971
1972#region Heuristic parsers1973
1974private static object[] ParseArray(string token, string format)1975{1976var splits = token.Split(formatEnumDelimiters);1977List<object> result = new List<object>();1978
1979for (int i = 0; i < splits.Length; i++)1980result.Add(ParseToken(splits[i], format));1981
1982return result.ToArray();1983}1984
1985private static DateTime ParseCommonTime(string token)1986{1987double temp = double.Parse(token, CultureInfo.InvariantCulture);1988
1989int sss = (int)((temp - (int)temp) * 1000.0);1990int hh = (int)((int)temp / 10000.0);1991int mm = (int)(((int)temp - hh * 10000.0) / 100.0);1992int ss = (int)((int)temp - hh * 10000.0 - mm * 100.0);1993
1994DateTime now = DateTime.Now;1995return new DateTime(now.Year, now.Month, now.Day, hh, mm, ss, sss);1996}1997
1998private static DateTime ParseCommonDate(string token)1999{2000if (token.Length == 6)2001{2002int date = Convert.ToInt32(token.Substring(0, 2));2003int month = Convert.ToInt32(token.Substring(2, 2));2004int year = Convert.ToInt32(token.Substring(4, 2)) + 2000;2005
2006return new DateTime(year, month, date);2007}2008else2009{2010throw new ArgumentException(string.Format("Date format incorrect in \"{0}\" (must be ddmmyy)", token));2011}2012}2013
2014private static DateTime ParseDateSlashes(string token)2015{2016var splits = token.Split("/".ToCharArray());2017
2018if (splits.Length == 3)2019{2020DateTime result = DateTime.Now;2021
2022int date = int.Parse(splits[0]);2023int month = int.Parse(splits[1]);2024int year = int.Parse(splits[2]) + 2000;2025
2026return new DateTime(year, month, date);2027}2028else2029{2030throw new ArgumentException(string.Format("Date format incorrect in \"{0}\" (must be dd/mm/yy)", token));2031}2032}2033
2034private static double ParseLatitude(string token)2035{2036double temp = double.Parse(token, CultureInfo.InvariantCulture);2037
2038double degree = (int)((int)temp / 100.0);2039double minutes = ((int)temp - degree * 100.0);2040double seconds = (temp - (int)temp) * 60.0;2041
2042return degree + minutes / 60.0 + seconds / 3600.0;2043}2044
2045private static double ParseLongitude(string token)2046{2047double temp = double.Parse(token, CultureInfo.InvariantCulture);2048
2049double degree = (int)((int)temp / 100.0);2050double minutes = ((int)temp - degree * 100.0);2051double seconds = (temp - (int)temp) * 60.0;2052
2053return degree + minutes / 60.0 + seconds / 3600.0;2054}2055
2056private static double ParseCommonDegrees(string token)2057{2058double temp = double.Parse(token, CultureInfo.InvariantCulture);2059
2060double degree = (int)((int)temp / 100.0);2061double minutes = ((int)temp - degree * 100.0);2062double seconds = (temp - (int)temp) * 60.0;2063
2064return degree + minutes / 60.0 + seconds / 3600.0;2065}2066
2067private static byte[] ParseByteArray(string source)2068{2069if (source.StartsWith("0x"))2070{2071if (source.Length % 2 == 0)2072{2073byte[] result = new byte[(source.Length - 2) / 2];2074
2075for (int i = 1; i < source.Length / 2; i++)2076result[i - 1] = byte.Parse(source.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier);2077
2078return result;2079}2080else2081{2082throw new ArgumentException("Specified string must has even length");2083}2084}2085else2086{2087throw new ArgumentException(string.Format("Specified string \"{0}\" do not starts with \"{1}\"", source, "0x"));2088}2089}2090
2091#endregion2092
2093#endregion2094
2095#region Utils2096
2097#region database related2098
2099/// <summary>2100/// Gets sentence description by specified sentenceID2101/// </summary>2102/// <param name="sentenceID">Sentence identified</param>2103/// <returns>String that reptesents sentence desctiption</returns>2104public static string GetSentenceDescription(SentenceIdentifiers sentenceID)2105{2106if (SentencesDescriptions.ContainsKey(sentenceID))2107{2108return SentencesDescriptions[sentenceID];2109}2110else2111{2112throw new ArgumentException(string.Format("Unknown sentence ID: \"{0}\"", sentenceID));2113}2114}2115
2116/// <summary>2117/// Gets talker description by specified taklerID2118/// </summary>2119/// <param name="talkerID">Talker identidier</param>2120/// <returns>String that represents talker description</returns>2121public static string GetTalkerDescription(TalkerIdentifiers talkerID)2122{2123if (TalkerDescriptions.ContainsKey(talkerID))2124{2125return TalkerDescriptions[talkerID];2126}2127else2128{2129throw new ArgumentException(string.Format("Unknown talker ID: \"{0}\"", talkerID));2130}2131}2132
2133/// <summary>2134/// Adds manufacturer node to base2135/// </summary>2136/// <param name="manufacturer">Manufacturer ID to add</param>2137public static void AddManufacturerToProprietarySentencesBase(ManufacturerCodes manufacturer)2138{2139if (!ProprietarySentencesFormats.ContainsKey(manufacturer))2140{2141ProprietarySentencesFormats.Add(manufacturer, new Dictionary<string, string>());2142}2143else2144{2145throw new ArgumentException(string.Format("Base already contais manufacturer \"{0}\"", manufacturer));2146}2147}2148
2149/// <summary>2150/// Adds proprietary sentence description to base2151/// </summary>2152/// <param name="manufacturer">Manufacturer identifier</param>2153/// <param name="sentenceIDString">proprietary sentence ID string</param>2154/// <param name="formatString">formatting string for specified sentence</param>2155public static void AddProprietarySentenceDescription(ManufacturerCodes manufacturer, string sentenceIDString, string formatString)2156{2157if (ProprietarySentencesFormats.ContainsKey(manufacturer))2158{2159if (!ProprietarySentencesFormats[manufacturer].ContainsKey(sentenceIDString))2160{2161ProprietarySentencesFormats[manufacturer].Add(sentenceIDString, formatString);2162}2163else2164{2165throw new ArgumentException(string.Format("Specified sentence ID \"{0}\" already exists in \"{1}\" sentences list", sentenceIDString, manufacturer));2166}2167}2168else2169{2170throw new ArgumentException(string.Format("Specified manufacturer \"{0}\" not exists in base, add it first", manufacturer));2171}2172}2173
2174public static string GetDatumDescription(int datumKey)2175{2176if (Datums.ContainsKey(datumKey))2177{2178return Datums[datumKey];2179}2180else2181{2182return string.Empty;2183}2184}2185
2186#endregion2187
2188/// <summary>2189/// Calculates NMEA0183 checksum (byte-by-byte XOR) for specified string2190/// </summary>2191/// <param name="source">string to calculate checksum</param>2192/// <returns></returns>2193public static byte GetCheckSum(string source)2194{2195byte result = 0;2196byte[] bytes = Encoding.ASCII.GetBytes(source);2197
2198for (int i = 0; i < bytes.Length; i++)2199result = (byte)(result ^ bytes[i]);2200
2201return result;2202}2203
2204/// <summary>2205/// Converts marine bend to meters per second2206/// </summary>2207/// <param name="speedBends">Speed in bends</param>2208/// <returns>Speed in meters per second</returns>2209public static double Bend2MpS(double speedBends)2210{2211return speedBends * 0.5144;2212}2213
2214/// <summary>2215/// Converts distance in miles to kilometers2216/// </summary>2217/// <param name="nMiles">Distance in miles</param>2218/// <returns>Distance in kilometers</returns>2219public static double NM2Km(double nMiles)2220{2221return nMiles * 1.852;2222}2223
2224/// <summary>2225/// Converts distance in feets to meters2226/// </summary>2227/// <param name="feets">Distance in feets</param>2228/// <returns>Distance in meters</returns>2229public static double Ft2M(double feets)2230{2231return feets * 0.3048;2232}2233
2234#endregion2235
2236#endregion2237}2238}