UCNLNMEA

Форк
0
/
NMEAParser.cs 
2238 строк · 98.6 Кб
1
using System;
2
using System.Collections.Generic;
3
using System.Globalization;
4
using System.Text;
5

6
namespace UCNLNMEA
7
{    
8
    /// <summary>
9
    /// Talkers identifiers
10
    /// </summary>
11
    public enum TalkerIdentifiers
12
    {
13
        AG,
14
        AP,
15
        CD,
16
        CR,
17
        CS,
18
        CT,
19
        CV,
20
        CX,
21
        DE,
22
        DF,       
23
        EC,
24
        EP,
25
        ER,
26
        GA, // 03-JUN-2020
27
        GB, // 02-JUN-2020
28
        GL,
29
        GP,
30
        GN,
31
        HC,
32
        HE,
33
        HN,
34
        II,
35
        IN,
36
        LA,
37
        LC,
38
        OM,
39
        P,
40
        RA,
41
        SD,
42
        SN,
43
        TR,
44
        SS,
45
        TI,
46
        VD,
47
        DM,
48
        VW,
49
        WI,
50
        YX,
51
        ZA,
52
        ZC,
53
        ZQ,
54
        ZV,
55
        unknown
56
    }
57

58
    /// <summary>
59
    /// Sentences identifiers
60
    /// </summary>
61
    public enum SentenceIdentifiers
62
    {
63
        AAM,
64
        ALM,
65
        APA,
66
        APB,
67
        ASD,
68
        BEC,
69
        BOD,
70
        BWC,
71
        BWR,
72
        BWW,
73
        DBK,
74
        DBS,
75
        DBT,
76
        DCN,
77
        DPT,
78
        DSC,
79
        DSE,
80
        DSI,
81
        DSR,
82
        DTM,
83
        FSI,
84
        GBS,
85
        GGA,
86
        GLC,
87
        GLL,
88
        GNS,
89
        GRS,
90
        GST,
91
        GSA,
92
        GSV,
93
        GTD,
94
        GXA,
95
        HDG,
96
        HDM,
97
        HDT,
98
        HEV,
99
        HSC,
100
        LCD,
101
        MSK,
102
        MSS,
103
        MWD,
104
        MTW,
105
        MWV,
106
        OLN,
107
        OSD,
108
        ROO,
109
        RMA,
110
        RMB,
111
        RMC,
112
        ROT,
113
        RPM,
114
        RSA,
115
        RSD,
116
        RTE,
117
        SFI,
118
        STN,
119
        TLL,
120
        TRF,
121
        TTM,
122
        TXT,
123
        VBW,
124
        VDR,
125
        VHW,
126
        VLW,
127
        VPW,
128
        VTG,
129
        VWR,
130
        WCV,
131
        WDC,
132
        WDR,
133
        WNC,
134
        WPL,
135
        XDR,
136
        XTE,
137
        XTR,
138
        ZDA,
139
        ZDL,
140
        ZFO,
141
        ZTG,
142
        unknown
143
    }
144

145
    /// <summary>
146
    /// Manufacturers codes
147
    /// </summary>
148
    public enum ManufacturerCodes
149
    {
150
        AAR,
151
        ACE,
152
        ACR,
153
        ACS,
154
        ACT,
155
        AGI,
156
        AHA,
157
        AIP,
158
        ALD,
159
        AMR,
160
        AMT,
161
        ANS,
162
        ANX,
163
        ANZ,
164
        APC,
165
        APL,
166
        APN,
167
        APX,
168
        AQC,
169
        AQD,
170
        AQM,
171
        ASP,
172
        ATE,
173
        ATM,
174
        ATR,
175
        ATV,
176
        AVN,
177
        AWA,
178
        BAT,
179
        BBL,
180
        BBR,
181
        BDV,
182
        BEC,
183
        BGS,
184
        BGT,
185
        BHE,
186
        BHR,
187
        BLB,
188
        BME,
189
        BNI,
190
        BNS,
191
        BRM,
192
        BRY,
193
        BTH,
194
        BTK,
195
        BTS,
196
        BXA,
197
        CAT,
198
        CBN,
199
        CCA,
200
        CCC,
201
        CCL,
202
        CCM,
203
        CDC,
204
        CEC,
205
        CHI,
206
        CKM,
207
        CMA,
208
        CMC,
209
        CME,
210
        CMP,
211
        CMS,
212
        CMV,
213
        CNV,
214
        CNX,
215
        CPL,
216
        CPN,
217
        CPS,
218
        CPT,
219
        CRE,
220
        CRO,
221
        CRY,
222
        CSI,
223
        CSM,
224
        CST,
225
        CSV,
226
        CTA,
227
        CTB,
228
        CTC,
229
        CTE,
230
        CTL,
231
        CNI,
232
        CWD,
233
        CWV,
234
        CYZ,
235
        DBG,
236
        DCC,
237
        DEB,
238
        DFI,
239
        DGC,
240
        DME,
241
        DMI,
242
        DNS,
243
        DNT,
244
        DPS,
245
        DRL,
246
        DSC,
247
        DYN,
248
        DYT,
249
        EBC,
250
        ECT,
251
        EEV,
252
        EFC,
253
        ELD,
254
        EMC,
255
        EMS,
256
        ENA,
257
        ENC,
258
        EPM,
259
        EPT,
260
        ERC,
261
        ESA,
262
        FDN,
263
        FEC,
264
        FHE,
265
        FJN,
266
        FMM,
267
        FNT,
268
        FRC,
269
        FTG,
270
        FUJ,        
271
        FUG,
272
        FUR,
273
        GAM,
274
        GCA,
275
        GES,
276
        GFC,
277
        GIS,
278
        GPI,
279
        GRM,
280
        GSC,
281
        GTO,
282
        GVE,
283
        GVT,
284
        HAL,
285
        HAR,
286
        HDN,
287
        HIG,
288
        HIT,
289
        HPK,
290
        HRC,
291
        HRT,
292
        HTI,
293
        HUL,
294
        HWM,
295
        ICO,
296
        IFD,
297
        IFI,
298
        IME,
299
        IMI,
300
        IMM,
301
        IMP,
302
        IMT,
303
        INM,
304
        INT,
305
        IRT,
306
        IST,
307
        ITM,
308
        ITR,
309
        JAN,
310
        JFR,
311
        JMT,
312
        JRC,
313
        JRI,
314
        JTC,
315
        JTR,
316
        KBE,
317
        KBM,
318
        KLA,
319
        KMR,
320
        KNG,
321
        KOD,
322
        KRP,
323
        KVH,
324
        KYI,
325
        LAT,
326
        LEC,
327
        LMM,
328
        LNA,
329
        LRD,
330
        LSE,
331
        LSP,
332
        LTF,
333
        LWR,
334
        MCL,
335
        MCP,
336
        MDL,
337
        MEC,
338
        MEG,
339
        MFR,
340
        MFW,
341
        MGN,
342
        MGS,
343
        MIE,
344
        MIM,
345
        MLE,
346
        MLN,
347
        MLP,
348
        MLT,
349
        MMB,
350
        MME,
351
        MMP,
352
        MMS,
353
        MNI,
354
        MNT,
355
        MNX,
356
        MOT,
357
        MPN,
358
        MQS,
359
        MRC,
360
        MRE,
361
        MRP,
362
        MRR,
363
        MRS,
364
        MSB,
365
        MSE,
366
        MSM,
367
        MST,
368
        MTA,
369
        MTG,
370
        MTK,
371
        MTR,
372
        MTS,
373
        MUR,
374
        MVX,
375
        MXX,
376
        MES,        
377
        NEF,
378
        NMR,
379
        NGS,
380
        NOM,
381
        NOV,
382
        NSM,
383
        NTK,
384
        NVC,
385
        NVL,
386
        NVS,
387
        NVO,
388
        OAR,
389
        ODE,
390
        ODN,
391
        OIN,
392
        OKI,
393
        OLY,
394
        OMN,
395
        ORE,
396
        OTK,
397
        PCE,
398
        PCH,
399
        PDM,
400
        PLA,
401
        PLI,
402
        PMI,
403
        PMP,
404
        PRK,
405
        PSM,
406
        PTC,
407
        PTG,
408
        PTH,
409
        RAC,
410
        RAE,
411
        RAY,
412
        RCA,
413
        RCH,
414
        RCI,
415
        RDI,
416
        RDM,
417
        REC,
418
        RFP,
419
        RGC,
420
        RGY,
421
        RLF,
422
        RMR,
423
        RPH,
424
        RSL,
425
        RSM,
426
        RWE,
427
        RWI,
428
        RWL,
429
        RME,
430
        RTN,
431
        SAI,
432
        SBR,
433
        SCR,
434
        SEA,
435
        SEC,
436
        SEP,
437
        SFN,
438
        SGC,        
439
        SIG,
440
        SIM,
441
        SKA,
442
        SKP,
443
        SLI,
444
        SME,
445
        SMF,
446
        SML,
447
        SMI,
448
        SNV,
449
        SOM,
450
        SOV,
451
        SPL,
452
        SPT,
453
        SRD,
454
        SRF, // added 08 december 2013
455
        SRS,
456
        SRT,
457
        SSI,
458
        STC,
459
        STI,
460
        STM,
461
        SVY,
462
        SWI,
463
        TBB,
464
        TCN,
465
        TDL,
466
        THR,
467
        TLS,
468
        TMT,
469
        TNL,
470
        TNT,
471
        TRC,
472
        TSI,
473
        TTK,
474
        TTS,
475
        TWC,
476
        TXI,
477
        UCN,
478
        UME,
479
        UNI,
480
        UNP,
481
        UNF,
482
        UWV,
483
        VAN,
484
        VAR,
485
        VCM,
486
        VEX,
487
        VIS,
488
        VLB,
489
        VMR,
490
        VSN,
491
        WAL,
492
        WBG,
493
        WEC,
494
        WHA,
495
        WMM,
496
        WMR,
497
        WNG,
498
        WSE,
499
        WTC,
500
        WST,
501
        YAS,
502
        ZMA,
503
        any,
504
        unknown
505
    }
506

507
    /// <summary>
508
    /// Earth hemisperes cardinals
509
    /// </summary>
510
    [Flags]
511
    public enum Cardinals
512
    {
513
        North,
514
        South,
515
        East,
516
        West,
517
        undefined
518
    }
519

520
    /// <summary>
521
    /// Structure represents geographical dimension
522
    /// </summary>
523
    public struct GeographicDimension
524
    {
525
        public Cardinals Cardinal;
526
        public double Angle;
527

528
        public override string ToString()
529
        {
530
            int degree = (int)Math.Floor(Angle);
531
            int minutes = (int)Math.Floor((Angle - degree) * 60.0);
532
            double seconds = (Angle - degree) * 3600 - minutes * 60.0;
533

534
            return string.Format(CultureInfo.InvariantCulture, "{0}°{1}\'{2:F04}\" {3}", degree, minutes, seconds, Cardinal);
535
        }
536

537
        public GeographicDimension(double angle, Cardinals cardinal)
538
        {
539
            Angle = angle;
540
            Cardinal = cardinal;
541
        }
542

543
        public static double AsSignedAngle(GeographicDimension dimension)
544
        {
545
            double result = dimension.Angle;
546

547
            if ((dimension.Cardinal == Cardinals.North) || (dimension.Cardinal == Cardinals.West))
548
                result = -dimension.Angle;
549

550
            return result;
551
        }
552
    }
553

554
    /// <summary>
555
    /// Struct represents geographical position
556
    /// </summary>
557
    public struct GeographicPosition
558
    {
559
        public GeographicDimension Latitude;
560
        public GeographicDimension Longitude;
561

562
        public override string ToString()
563
        {
564
            return string.Format("{0}, {1}", Latitude.ToString(), Longitude.ToString());
565
        }
566
    }
567

568
    /// <summary>
569
    /// NMEA0183 2.0 Sentences parser/builder
570
    /// (C) Aleksander Dikarev, 2011-2020
571
    /// </summary>
572
    public static class NMEAParser
573
    {
574
        #region Properties
575

576
        #region Commons
577

578
        #region expression delimiters
579

580
        public static readonly string StandartID = "NMEA 0183 2.0";
581
        public static readonly int MaxFullSentenceLength = 4255;//82;
582
        public static readonly int MaxSentenceLength = 4252;//79;
583
        public static readonly char SentenceStartDelimiter = '$';
584
        public static readonly string SentenceEndDelimiter = "\r\n";
585
        public static readonly char FieldDelimiter = ',';
586
        public static readonly char CheckSumFieldDelimiter = '*';
587

588
        private static char[] arrayBrackets = new char[] { '[', ']' };
589
        public static string arrayOpenBracket = "[";
590
        public static string arrayCloseBracket = "]";
591
        public static char[] formatTokenDelimiters = new char[] { ',' };
592
        public static char[] formatEnumDelimiters = new char[] { '|' };
593
        private static string formatEnumPairDelimiter = "=";
594
        public static char[] formatEnumPairDelimiters = new char[] { '=' };
595
        private static string etcFormat = "...";
596

597
        #endregion
598

599
        #region Talker descriptions
600

601
        private static Dictionary<TalkerIdentifiers, string> TalkerDescriptions = 
602
            new 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
        #endregion
639

640
        #region Sentences descriptions
641

642
        private static Dictionary<SentenceIdentifiers, string> SentencesDescriptions = 
643
            new 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
        #endregion        
722

723
        #region Datum dictionary
724

725
        private 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
        #endregion
953

954
        #region Sentences formats
955

956
        /// <summary>
957
        /// Format string for sentences must be as "param_1_format, param_2_format, ..."
958
        /// valid formats are:
959
        /// x - integer
960
        /// x.x - double
961
        /// c--c - ASCII char string
962
        /// hh - hexadecimal byte
963
        /// hhhh - hexadecimal short int 
964
        /// hhhhhhhh - hexadecimal int
965
        /// hhmmss.ss - time: hours, minutes, seconds, fractional part of seconds
966
        /// llll.ll - longitude
967
        /// yyyyy.yy - latitude
968
        /// </summary>
969

970
        #region Standart sentences
971

972
        private static Dictionary<SentenceIdentifiers, string> SentencesFormats =
973
            new 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 2022
1002
                                                            // $GNGSA,A,                    3,01,03,04,22,31,09,06,  ,  ,  ,  ,  ,1.57,0.82,1.34,1*05
1003
                                                            //    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
        #endregion
1059

1060
        #region Known proprietary sentences
1061

1062
        private static Dictionary<ManufacturerCodes, Dictionary<string, string>> ProprietarySentencesFormats =
1063
            new Dictionary<ManufacturerCodes, Dictionary<string, string>>()
1064
            {
1065
                #region Garmin corporation
1066

1067
                {   
1068
                    ManufacturerCodes.GRM, // Garmin corporation
1069
                    new 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
                #endregion                
1089

1090
                #region Martech Inc.
1091

1092
                {
1093
                    ManufacturerCodes.MTK, // Martech, Inc.
1094
                    new 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 Store
1098
                        {"102", "" }, /// Warm restart: don't use Ephemeris at re-start
1099
                        {"103", "" }, /// Cold restart: don't use Time, Position, Almanacs and Ephemeris data at re-start
1100
                        {"104", "" }, /// Full cold restart: it's essentially a Cold Restart, but additionaly clear system/user config at re-start
1101
                                      /// That is, reset the receiver to the factory status
1102
                        {"251", "x"}, /// Set NMEA port baudrate, 0 means default, availible values: 4800, 9600, 14400, 19200, 38400, 57600, 115200
1103
                        {"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_Mode
1108
                        {"390","x,x,x,x,x,x,x,x,x,x,x,0=Disable|1=RTCM|2=SBAS"}, /// API_Set_Flash_User_Option
1109
                        {"420",""}, // API_Query_Pwr_Sav_Mode, return: PMTK520
1110
                        {"490",""}, // API_Get_Flash_User_Option, return: PMTK590
1111
                        {"520","0=False|1=True"}, // Power saving operation mode 
1112
                        {"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: PMTK705
1114
                        {"705","c--c,c--c,c--c"} /// Firmware release version
1115
                    }
1116
                },
1117

1118
                #endregion
1119

1120
                #region Trimble Navigation
1121

1122
                {
1123
                    ManufacturerCodes.TNL, // Trimble Navigation
1124
                    new 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 status
1127
                        {"EV", "hhmmss.ss,x"}, /// Event marker
1128
                        {",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, DOP                                                                                                                                                                                                                            
1129
                        {"ID", "c--c,c--c,x,x,dd/mm/yy"}, /// Trimble receiver ID                                                         
1130
                        {"SM", "xxxx,c--c"}, /// RTCM Special                                            
1131
                        {",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 quality
1133
                        {",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 format
1134
                        {",PJT", "c--c,c--c"}, /// Receiver projection
1135
                        {",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 information
1136
                        {",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 information
1137
                    }
1138
                },
1139

1140
                #endregion
1141

1142
                #region Fugro
1143

1144
                {
1145
                    ManufacturerCodes.FUG,
1146
                    new 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 info
1149
                    }
1150
                },
1151

1152
                #endregion
1153

1154
                #region Magellan
1155

1156
                {
1157
                    ManufacturerCodes.MGN,
1158
                    new Dictionary<string, string>()
1159
                    {
1160
                        { "CMD", "c--c,..." }, /// CMD, first parameter "VERSION" or "TRACK", see manufacturer's documentation
1161
                        { "CSM", "hh" }, /// In handshake mode is used to acknowledge succsessful. Parameter - checksum for the last last message received
1162
                        { "DRT", "xx" }, /// Delete route from memory. Paramter - zero based route number
1163
                        { "DWP", "c--c,xx" }, /// Delete waypoint from memory. 
1164
                        { "RTE", "xx,xx,c,x,c--c,..." }, /// Route information
1165
                        { "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 information
1166
                        { "VER", "x,x,c--c,c--c,c--c"}, /// Version information
1167
                        { "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 info
1168
                        { "ST", "c--c,2=2D|3=3D,T=True|F=False,x,x.x,x,x" }, /// Status information
1169
                    }
1170
                },
1171

1172
                #endregion
1173

1174
                #region Motorola
1175

1176
                {
1177
                    ManufacturerCodes.MOT,
1178
                    new Dictionary<string, string>()
1179
                    {
1180
                        {"G", "c--c,x"} /// Sentence frequency control
1181
                    }
1182
                },
1183

1184
                #endregion
1185

1186
                #region Rockwell International
1187

1188
                {
1189
                    ManufacturerCodes.RWI,
1190
                    new 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
                #endregion
1198

1199
                #region Starlink
1200

1201
                {
1202
                    ManufacturerCodes.SLI,
1203
                    new Dictionary<string, string>()
1204
                    {
1205
                       {"B", "x.x,x,c--c,J=Status request|K=Configuration request|=tuning message"}, // 
1206
                    }
1207
                },
1208

1209
                #endregion
1210

1211
                #region SRF (update 08 dec 2013)
1212
                
1213
                {
1214
                    ManufacturerCodes.SRF,
1215
                    new Dictionary<string, string>()
1216
                    {
1217
                       {"100", "0=BIN|1=NMEA,x,x,x,0=None|1=Even|2=Odd"}, // protocol type: 0 - binary, 1 - nmea
1218
                                                                          // baudrate: 1200, 2400, 4800, 9600, 19200, 38400
1219
                                                                          // data bits: 8, 7 (BIN protocol requires 8)
1220
                                                                          // stop bits: 0, 1
1221
                                                                          // parity: 0 - none, 1 - even, 2 - odd
1222
                       {"101", "x,x,x,x,x,x,x,x,x"}, // position X-coordinate
1223
                                                     // position Y-coordinate
1224
                                                     // position Z-coordinate
1225
                                                     // GPS receiver clock offset, Hz: 0 means last save value
1226
                                                     // Time of GPS week
1227
                                                     // Number of GPS week
1228
                                                     // Number of channels to use: 1..12
1229
                                                     // Bitmask: 0x01 - data valid, warm start = 1
1230
                                                     //          0x02 - clear ephemeris, warm start = 1
1231
                                                     //          0x04 - clear memory, cold start = 1
1232
                       {"102", "x,x,x,x"}, // baudrate: 1200, 2400, 4800, 9600, 19200, 38400
1233
                                           // data bits: 8
1234
                                           // stop bits: 0, 1
1235
                                           // parity: 0 - none, 1 - even, 2 - odd
1236
                       {"103", "0=GGA|1=GLL|2=GSA|3=GSV|4=RMC|5=VTG,x,x,x"}, // message type
1237
                                                                             // mode: 0 - periodically, 1 - by request
1238
                                                                             // rate: 0..255 (0 - means turned off)
1239
                                                                             // 0 - checksum disabled, 1 - checksum enabled
1240
                       {"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 week
1245
                                                         // number of GPS week
1246
                                                         // Channels to use 1..12
1247
                                                         // bitmask: 0x01 - data valid, warm/hot start = 1
1248
                                                         //          0x02 - clear ephemeris, warm start = 1
1249
                                                         //          0x04 - clear memory, cold start = 1
1250
                       {"105", "x"} // 0 - switch debug mode off, 1 - switch debug mode on
1251
                    }
1252
                },
1253
                
1254
                #endregion
1255

1256
                #region Furuno
1257

1258
                {
1259
                    ManufacturerCodes.FEC,
1260
                    new Dictionary<string, string>()
1261
                    {
1262
                        // $PFEC,hdcom,N,Er,0010
1263
                        { ",hdcom", "c--c,c--c,xxxx" }, // no data for the sentence :(
1264
                    }
1265
                },
1266

1267
                #endregion
1268
            };
1269

1270
        #endregion
1271

1272
        #region Formatters
1273

1274
#if FRAMEWORK_LOWER_35
1275

1276
        private delegate object ParserDelegate(string source);
1277
        private 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

1302
        private delegate string FormatterDelegate(object source);
1303
        private 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
#else
1328
        private 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

1352
        private 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
#endif
1377

1378
        #endregion
1379

1380
        #endregion
1381

1382
        #region Checksum
1383

1384
#if FRAMEWORK_LOWER_35
1385
        public delegate byte CheckSumEvaluatorDelegate(string source);
1386
        public static CheckSumEvaluatorDelegate CheckSumEvaluator = new CheckSumEvaluatorDelegate(GetCheckSum);
1387
#else
1388
        public static Func<string, byte> CheckSumEvaluator = new Func<string, byte>(GetCheckSum);
1389
#endif
1390

1391
        #endregion
1392

1393
        #endregion
1394

1395
        #endregion
1396

1397
        #region Methods
1398

1399
        #region Sentence building
1400

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>
1408
        public static string BuildSentence(TalkerIdentifiers talkerID, SentenceIdentifiers sentenceID, object[] parameters)
1409
        {
1410
            var formatter = SentencesFormats[sentenceID];
1411

1412
            StringBuilder sb = new StringBuilder();
1413
            sb.Append(talkerID.ToString());
1414
            sb.Append(sentenceID.ToString());
1415
            sb.Append(FieldDelimiter);
1416
            sb.Append(BuildParametersList(parameters, formatter));
1417

1418
            var checkSum = GetCheckSum(sb.ToString());
1419

1420
            sb.Append(CheckSumFieldDelimiter);
1421
            sb.Append(checkSum.ToString("X2"));
1422
            sb.Append(SentenceEndDelimiter);
1423

1424
            sb.Insert(0, SentenceStartDelimiter);
1425

1426
            return sb.ToString();            
1427
        }
1428

1429
        /// <summary>
1430
        /// Builds proprietary sentence
1431
        /// </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>
1436
        public static string BuildProprietarySentence(ManufacturerCodes manufacturerID, string sentenceIDString, object[] parameters)
1437
        {
1438
            var formatter = ProprietarySentencesFormats[manufacturerID][sentenceIDString];
1439

1440
            StringBuilder sb = new StringBuilder();
1441
            sb.Append(TalkerIdentifiers.P.ToString());
1442
            sb.Append(manufacturerID.ToString());
1443
            sb.Append(sentenceIDString);
1444
            sb.Append(FieldDelimiter);
1445
            sb.Append(BuildParametersList(parameters, formatter));
1446

1447
            var checkSum = GetCheckSum(sb.ToString());
1448

1449
            sb.Append(CheckSumFieldDelimiter);
1450
            sb.Append(checkSum.ToString("X2"));
1451
            sb.Append(SentenceEndDelimiter);
1452

1453
            sb.Insert(0, SentenceStartDelimiter);
1454

1455
            return sb.ToString();
1456
        }
1457

1458
        private static string BuildParametersList(object[] items, string formatter)
1459
        {
1460
            var splits = formatter.Split(formatTokenDelimiters);
1461

1462
            if ((splits.Length >= items.Length) || (formatter.Contains(etcFormat)))
1463
            {
1464
                StringBuilder sb = new StringBuilder();
1465

1466
                string previousFormat = "c--c";
1467
                for (int i = 0; i < items.Length; i++)
1468
                {
1469
                    if (splits.Length <= i)
1470
                    {
1471
                        sb.Append(FormatToken(items[i], previousFormat));
1472
                    }
1473
                    else
1474
                    {
1475
                        sb.Append(FormatToken(items[i], splits[i]));
1476
                        previousFormat = splits[i];
1477
                    }
1478

1479
                    if (i != items.Length - 1)
1480
                        sb.Append(FieldDelimiter);
1481
                }
1482

1483
                return sb.ToString();
1484
            }
1485
            else
1486
            {
1487
                throw new ArgumentException("Specified parameters list and formatting string incompatible");
1488
            }
1489
        }
1490

1491
        private static string FormatToken(object item, string format)
1492
        {
1493
            if (item == null)
1494
                return string.Empty;
1495

1496
            if (format.Contains(formatEnumPairDelimiter))
1497
            {
1498
                var items = format.Split(formatEnumDelimiters);
1499
                Dictionary<string, string> enumDictionary = new Dictionary<string, string>();
1500

1501
                for (int i = 0; i < items.Length; i++)
1502
                {
1503
                    var pair = items[i].Split(formatEnumPairDelimiters);
1504
                    if (pair.Length == 2)
1505
                    {
1506
                        enumDictionary.Add(pair[1], pair[0]);
1507
                    }
1508
                    else
1509
                    {
1510
                        throw new ArgumentException(string.Format("Error in format token \"{0}\"", format));
1511
                    }
1512
                }
1513

1514
                if (enumDictionary.ContainsKey(item.ToString()))
1515
                {
1516
                    return enumDictionary[item.ToString()];
1517
                }
1518
                else
1519
                {
1520
                    throw new ArgumentException(string.Format("Specified object \"{0}\" cannot be formatter with format string \"{1}\"", item.ToString(), format));
1521
                }
1522
            }
1523
            else
1524
            {
1525
                if (format.StartsWith(arrayOpenBracket) && format.EndsWith(arrayCloseBracket))
1526
                {
1527
                    return FormatArray(item, format.Trim(arrayBrackets));
1528
                }
1529
                else
1530
                {
1531
                    if (formatters.ContainsKey(format))
1532
                    {
1533
                        return formatters[format](item);
1534
                    }
1535
                    else
1536
                    {
1537
                        return item.ToString();
1538
                    }
1539
                }
1540
            }
1541
        }
1542

1543
        #region Heuristic formatters
1544

1545
        private static string FormatArray(object item, string format)
1546
        {
1547
            if (item is Array)
1548
            {
1549
                Array array = item as Array;
1550
                StringBuilder sb = new StringBuilder();
1551

1552
                for (int i = 0; i < array.Length; i++)
1553
                {
1554
                    sb.Append(FormatToken(array.GetValue(i), format));
1555

1556
                    if (i < array.Length - 1)
1557
                        sb.Append(formatEnumDelimiters[0]);
1558
                }
1559

1560
                return sb.ToString();
1561
            }
1562
            else
1563
            {
1564
                throw new ArgumentException(string.Format("Unable cast \"{0}\" to array", item.GetType().Name));
1565
            }
1566
        }
1567

1568
        private static string FormatLatitude(double x)
1569
        {
1570
            StringBuilder sb = new StringBuilder();
1571

1572
            double degrees = Math.Floor(x);
1573
            double minutes = Math.Floor((x - degrees) * 60.0);
1574
            double seconds = (x - degrees) * 3600 - minutes * 60.0;
1575

1576
            minutes += seconds / 60.0;
1577

1578
            sb.Append(degrees.ToString("00"));
1579
            sb.Append(minutes.ToString("00.0000", CultureInfo.InvariantCulture).TrimEnd(new char['0']));
1580

1581
            return sb.ToString();
1582
        }
1583

1584
        private static string FormatLongitude(double x)
1585
        {
1586
            StringBuilder sb = new StringBuilder();
1587

1588
            double degrees = Math.Floor(x);
1589
            double minutes = Math.Floor((x - degrees) * 60.0);
1590
            double seconds = (x - degrees) * 3600 - minutes * 60.0;
1591

1592
            minutes += seconds / 60.0;
1593

1594
            sb.Append(degrees.ToString("000"));
1595
            sb.Append(minutes.ToString("00.0000", CultureInfo.InvariantCulture).TrimEnd(new char['0']));
1596

1597
            return sb.ToString();
1598
        }
1599

1600
        private static string FormatTime(DateTime dt, bool isMilliseconds)
1601
        {
1602
            StringBuilder sb = new StringBuilder();
1603
            sb.Append(dt.Hour.ToString("00"));
1604
            sb.Append(dt.Minute.ToString("00"));
1605
            sb.Append(dt.Second.ToString("00"));
1606

1607
            if (isMilliseconds)
1608
            {
1609
                sb.Append('.');
1610
                sb.Append(dt.Millisecond.ToString());
1611
            }
1612

1613
            return sb.ToString();
1614
        }
1615

1616
        private static string FormatDate(DateTime dt)
1617
        {
1618
            StringBuilder sb = new StringBuilder();
1619
            sb.Append(dt.Day.ToString("00"));
1620
            sb.Append(dt.Month.ToString("00"));
1621
            sb.Append((dt.Year - 2000).ToString("00"));
1622
            return sb.ToString();
1623
        }
1624

1625
        private static string FormatDateSlashes(DateTime dt)
1626
        {
1627
            StringBuilder sb = new StringBuilder();
1628

1629
            sb.Append(dt.Day.ToString("00"));
1630
            sb.Append('/');
1631
            sb.Append(dt.Month.ToString("00"));
1632
            sb.Append('/');
1633
            sb.Append((dt.Year - 2000).ToString("00"));
1634

1635
            return sb.ToString();
1636
        }
1637

1638
        private static string FormatDegree(double x)
1639
        {
1640
            StringBuilder sb = new StringBuilder();
1641

1642
            double degrees = Math.Floor(x);
1643
            double minutes = Math.Floor((x - degrees) * 60.0);
1644
            double seconds = (x - degrees) * 3600 - minutes * 60.0;
1645

1646
            minutes += seconds / 60.0;
1647

1648
            sb.Append(degrees.ToString("000"));
1649
            sb.Append(minutes.ToString("00.0000", CultureInfo.InvariantCulture).TrimEnd(new char['0']));
1650

1651
            return sb.ToString();
1652
        }
1653

1654
        private static string FormatDecDegree(double x)
1655
        {
1656
            return x.ToString("000.0000", CultureInfo.InvariantCulture);
1657
        }
1658

1659
        public static string FormatByteArray(byte[] bytes)
1660
        {
1661
            StringBuilder sb = new StringBuilder();
1662
            sb.Append("0x");
1663

1664
            for (int i = 0; i < bytes.Length; i++)
1665
                sb.Append(bytes[i].ToString("x2"));
1666

1667
            return sb.ToString();
1668
        }
1669

1670
        #endregion
1671

1672
        #endregion
1673

1674
        #region Sentence parsing
1675

1676
        /// <summary>
1677
        /// Parse NMEA0183 sentence
1678
        /// </summary>
1679
        /// <param name="sourceString">string to parse</param>
1680
        /// <returns>Parsed sentence as NMEASentese</returns>
1681
        public static NMEASentence Parse(string sourceString)
1682
        {            
1683
            if (!sourceString.EndsWith(SentenceEndDelimiter))
1684
            {
1685
                throw new ArgumentException(string.Format("{0} sentence must ends with valid sentence end delimiter \"{1}\"", StandartID, @"\r\n"));
1686
            }
1687

1688
            var source = sourceString.TrimEnd(SentenceEndDelimiter.ToCharArray());
1689

1690
            if (source.Length > MaxSentenceLength)
1691
                throw new ArgumentException(string.Format("Specified string \"{0}\" length exceeds {1} characters", source.Length, MaxSentenceLength));
1692

1693
            if (!source.StartsWith(SentenceStartDelimiter.ToString()))
1694
                throw new ArgumentException(string.Format("{0} sentence must starts with valid sentence start delimiter \"{1}\"", StandartID, SentenceStartDelimiter));
1695

1696
            try
1697
            {
1698
                string sentence;
1699
                string checkSumString;
1700
                SplitSentence(source, out sentence, out checkSumString);
1701

1702
                if (!string.IsNullOrEmpty(checkSumString))
1703
                {
1704
                    byte declaredCheckSum;
1705
                    if (byte.TryParse(checkSumString, System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture, out declaredCheckSum))
1706
                    {
1707
                        byte realCheckSum = GetCheckSum(sentence);
1708

1709
                        if (realCheckSum == declaredCheckSum)
1710
                        {
1711
                            return ParseSentence(sentence);
1712
                        }
1713
                        else
1714
                        {
1715
                            throw new ArgumentException(string.Format("Integrity error in sentese \"{0}\" (declared: {1}, real: {2})", sentence, declaredCheckSum, realCheckSum));
1716
                        }
1717
                    }
1718
                    else
1719
                    {
1720
                        throw new ArgumentException(string.Format("Unable parse chesksum field from \"{0}\"", checkSumString));
1721
                    }
1722
                }
1723
                else
1724
                {
1725
                    return ParseSentence(sentence);
1726
                }
1727
            }
1728
            catch (Exception ex)
1729
            {
1730
                throw ex;
1731
            }
1732
        }
1733

1734
        private static NMEAProprietarySentence ParseProprietary(string manufacturerIDString, string sentenceID, List<string> parameters)
1735
        {
1736
            ManufacturerCodes manufacturerID = ManufacturerCodes.unknown;
1737
            try
1738
            {
1739
                manufacturerID = (ManufacturerCodes)Enum.Parse(typeof(ManufacturerCodes), manufacturerIDString);
1740
            }
1741
            catch
1742
            {
1743
                throw new ArgumentException(string.Format("Unknown manufacturer \"{0}\" in proprietary sentence", manufacturerIDString));
1744
            }
1745

1746
            if (ProprietarySentencesFormats.ContainsKey(manufacturerID))
1747
            {
1748
                if (ProprietarySentencesFormats[manufacturerID].ContainsKey(sentenceID))
1749
                {
1750
                    NMEAProprietarySentence result = new NMEAProprietarySentence();
1751
                    result.Manufacturer = manufacturerID;
1752
                    result.SentenceIDString = sentenceID;
1753

1754
                    var formatter = ProprietarySentencesFormats[manufacturerID][sentenceID];
1755
                    result.parameters = ParseParameters(parameters, formatter);
1756

1757
                    return result;
1758
                }
1759
                else
1760
                {
1761
                    throw new ArgumentException(string.Format("Unknown proprietary sentese \"{0}\", manufacturerID: \"{1}\"", sentenceID, manufacturerID));
1762
                }
1763
            }
1764
            else
1765
            {
1766
                throw new ArgumentException(string.Format("Unknown manufacturerID \"{0}\", sentenceID: \"{1}\"", manufacturerID, sentenceID));
1767
            }
1768
        }
1769

1770
        private static NMEAStandartSentence ParseSentence(TalkerIdentifiers talkerID, string sentenceIDString, List<string> parameters)
1771
        {
1772
            SentenceIdentifiers sentenceID = SentenceIdentifiers.unknown;
1773
            try
1774
            {
1775
                sentenceID = (SentenceIdentifiers)Enum.Parse(typeof(SentenceIdentifiers), sentenceIDString);
1776
            }
1777
            catch
1778
            {
1779
                throw new ArgumentException(string.Format("Undefined sentence ID \"{0}\" from takler \"{1}\"", sentenceIDString, talkerID));
1780
            }
1781

1782
            var formatter = SentencesFormats[sentenceID];
1783
            if (string.IsNullOrEmpty(formatter))
1784
            {
1785
                throw new ArgumentException(string.Format("Specified sentence \"{0}\" from talker \"{1}\" is unknown", sentenceID, talkerID));
1786
            }
1787

1788
            NMEAStandartSentence result = new NMEAStandartSentence();
1789
            result.TalkerID = talkerID;
1790
            result.SentenceID = sentenceID;
1791

1792
            result.parameters = ParseParameters(parameters, formatter);
1793

1794
            return result;
1795
        }
1796

1797
        private static NMEASentence ParseSentence(string source)
1798
        {
1799
            var splits = source.Split(FieldDelimiter.ToString().ToCharArray());
1800
            List<string> parameters = new List<string>();
1801

1802
            if (splits.Length > 1)
1803
            {
1804
                var sentenceDescription = splits[0];
1805
                if (sentenceDescription.Length >= 4)
1806
                {
1807
                    string talkerIDString;
1808
                    string sentenceIDString;
1809
                    
1810
                    if (sentenceDescription.StartsWith(TalkerIdentifiers.P.ToString()))
1811
                    {
1812
                        // Proprietary code
1813
                        string manufacturerIDString = string.Empty;
1814
                        manufacturerIDString = sentenceDescription.Substring(1, 3);
1815

1816
                        int start = 1;
1817
                        if (sentenceDescription.Length == 4)
1818
                        {
1819
                            sentenceDescription += FieldDelimiter + splits[1];                                
1820
                            start = 2;
1821
                        }
1822

1823
                        sentenceIDString = sentenceDescription.Substring(4);
1824

1825
                        for (int i = start; i < splits.Length; i++)
1826
                        {
1827
                            parameters.Add(splits[i]);
1828
                        }
1829

1830
                        return ParseProprietary(manufacturerIDString, sentenceIDString, parameters);
1831
                    }
1832
                    else
1833
                    {
1834
                        // Not a proprietary code
1835
                        TalkerIdentifiers talkerID = TalkerIdentifiers.unknown;
1836
                        talkerIDString = sentenceDescription.Substring(0, 2);
1837
                        sentenceIDString = sentenceDescription.Substring(2, 3);
1838

1839
                        try
1840
                        {
1841
                            talkerID = (TalkerIdentifiers)Enum.Parse(typeof(TalkerIdentifiers), talkerIDString);
1842
                        }
1843
                        catch
1844
                        {
1845
                            throw new ArgumentException(string.Format("Undefined takler ID \"{0}\"", talkerIDString));
1846
                        }
1847

1848
                        for (int i = 1; i < splits.Length; i++)
1849
                        {
1850
                            parameters.Add(splits[i]);
1851
                        }
1852

1853
                        return ParseSentence(talkerID, sentenceIDString, parameters);
1854
                    }                                        
1855
                }
1856
                else
1857
                {
1858
                    throw new ArgumentException(string.Format("Wrong sentence description: \"{0}\"", sentenceDescription));
1859
                }
1860
            }
1861
            else
1862
            {
1863
                throw new ArgumentException(string.Format("No field delimiters in specified sentence \"{0}\"", source));
1864
            }            
1865
        }
1866

1867
        private static void SplitSentence(string source, out string sentence, out string checkSumString)
1868
        {
1869
            var temp = source.TrimStart(SentenceStartDelimiter.ToString().ToCharArray());
1870

1871
            if (source.Contains(CheckSumFieldDelimiter.ToString()))
1872
            {
1873

1874
                var splits = temp.Split(CheckSumFieldDelimiter.ToString().ToCharArray());
1875

1876
                if (splits.Length == 2)
1877
                {
1878
                    sentence = splits[0];
1879
                    checkSumString = splits[1];
1880
                }
1881
                else
1882
                {
1883
                    throw new ArgumentException(string.Format("Specified string \"{0}\" not in correct {1} format", source, StandartID));
1884
                }
1885
            }
1886
            else
1887
            {
1888
                sentence = temp;
1889
                checkSumString = string.Empty;
1890
            }
1891
        }
1892

1893
        private static object ParseToken(string token, string format)
1894
        {
1895
            if (string.IsNullOrEmpty(token))
1896
                return null;
1897

1898
            if (format.Contains(formatEnumPairDelimiter))
1899
            {
1900
                var items = format.Split(formatEnumDelimiters);
1901
                Dictionary<string, string> enumDictionary = new Dictionary<string, string>();
1902

1903
                for (int i = 0; i < items.Length; i++)
1904
                {
1905
                    var pair = items[i].Split(formatEnumPairDelimiters);
1906
                    if (pair.Length == 2)
1907
                    {
1908
                        enumDictionary.Add(pair[0], pair[1]);
1909
                    }
1910
                    else
1911
                    {
1912
                        throw new ArgumentException(string.Format("Error in format token \"{0}\"", format));
1913
                    }
1914
                }
1915

1916
                if (enumDictionary.ContainsKey(token))
1917
                {
1918
                    return enumDictionary[token];
1919
                }
1920
                else
1921
                {
1922
                    return token;
1923
                }
1924
            }
1925
            else
1926
            {
1927
                if (format.StartsWith(arrayOpenBracket) && token.EndsWith(arrayCloseBracket))
1928
                {
1929
                    return ParseArray(token, format.Trim(arrayBrackets));
1930
                }
1931
                else
1932
                {
1933
                    if (parsers.ContainsKey(format))
1934
                    {
1935
                        return parsers[format](token);
1936
                    }
1937
                    else
1938
                    {
1939
                        return token;
1940
                    }
1941
                }
1942
            }            
1943
        }
1944

1945
        private static object[] ParseParameters(List<string> parameters, string formatString)
1946
        {
1947
            var formatTokens = formatString.Split(formatTokenDelimiters);
1948
            if ((formatTokens.Length >= parameters.Count) || (formatString.Contains(etcFormat)))
1949
            {
1950
                List<object> results = new List<object>();
1951

1952
                string previousFormatter = "c--c";
1953
                for (int i = 0; i < parameters.Count; i++)
1954
                {
1955
                    if (formatTokens.Length <= i)
1956
                        results.Add(ParseToken(parameters[i], previousFormatter));
1957
                    else
1958
                    {
1959
                        results.Add(ParseToken(parameters[i], formatTokens[i]));
1960
                        previousFormatter = formatTokens[i];
1961
                    }
1962
                }
1963

1964
                return results.ToArray();
1965
            }
1966
            else
1967
            {
1968
                throw new ArgumentException("Specified parameters list and formatting string incompatible");
1969
            }
1970
        }
1971

1972
        #region Heuristic parsers
1973

1974
        private static object[] ParseArray(string token, string format)
1975
        {
1976
            var splits = token.Split(formatEnumDelimiters);
1977
            List<object> result = new List<object>();
1978

1979
            for (int i = 0; i < splits.Length; i++)
1980
                result.Add(ParseToken(splits[i], format));
1981

1982
            return result.ToArray();
1983
        }
1984

1985
        private static DateTime ParseCommonTime(string token)
1986
        {
1987
            double temp = double.Parse(token, CultureInfo.InvariantCulture);
1988

1989
            int sss = (int)((temp - (int)temp) * 1000.0);
1990
            int hh = (int)((int)temp / 10000.0);
1991
            int mm = (int)(((int)temp - hh * 10000.0) / 100.0);
1992
            int ss = (int)((int)temp - hh * 10000.0 - mm * 100.0);
1993

1994
            DateTime now = DateTime.Now;
1995
            return new DateTime(now.Year, now.Month, now.Day, hh, mm, ss, sss);
1996
        }
1997

1998
        private static DateTime ParseCommonDate(string token)
1999
        {
2000
            if (token.Length == 6)
2001
            {
2002
                int date = Convert.ToInt32(token.Substring(0, 2));
2003
                int month = Convert.ToInt32(token.Substring(2, 2));
2004
                int year = Convert.ToInt32(token.Substring(4, 2)) + 2000;
2005

2006
                return new DateTime(year, month, date);
2007
            }
2008
            else
2009
            {
2010
                throw new ArgumentException(string.Format("Date format incorrect in \"{0}\" (must be ddmmyy)", token));
2011
            }
2012
        }
2013

2014
        private static DateTime ParseDateSlashes(string token)
2015
        {
2016
            var splits = token.Split("/".ToCharArray());
2017

2018
            if (splits.Length == 3)
2019
            {
2020
                DateTime result = DateTime.Now;
2021

2022
                int date = int.Parse(splits[0]);
2023
                int month = int.Parse(splits[1]);
2024
                int year = int.Parse(splits[2]) + 2000;
2025

2026
                return new DateTime(year, month, date);
2027
            }
2028
            else
2029
            {
2030
                throw new ArgumentException(string.Format("Date format incorrect in \"{0}\" (must be dd/mm/yy)", token));
2031
            }
2032
        }
2033

2034
        private static double ParseLatitude(string token)
2035
        {
2036
            double temp = double.Parse(token, CultureInfo.InvariantCulture);
2037

2038
            double degree = (int)((int)temp / 100.0);
2039
            double minutes = ((int)temp - degree * 100.0);
2040
            double seconds = (temp - (int)temp) * 60.0;
2041

2042
            return degree + minutes / 60.0 + seconds / 3600.0;
2043
        }
2044

2045
        private static double ParseLongitude(string token)
2046
        {
2047
            double temp = double.Parse(token, CultureInfo.InvariantCulture);
2048

2049
            double degree = (int)((int)temp / 100.0);
2050
            double minutes = ((int)temp - degree * 100.0);
2051
            double seconds = (temp - (int)temp) * 60.0;
2052

2053
            return degree + minutes / 60.0 + seconds / 3600.0;
2054
        }
2055

2056
        private static double ParseCommonDegrees(string token)
2057
        {
2058
            double temp = double.Parse(token, CultureInfo.InvariantCulture);
2059

2060
            double degree = (int)((int)temp / 100.0);
2061
            double minutes = ((int)temp - degree * 100.0);
2062
            double seconds = (temp - (int)temp) * 60.0;
2063

2064
            return degree + minutes / 60.0 + seconds / 3600.0;
2065
        }
2066

2067
        private static byte[] ParseByteArray(string source)
2068
        {
2069
            if (source.StartsWith("0x"))
2070
            {
2071
                if (source.Length % 2 == 0)
2072
                {
2073
                    byte[] result = new byte[(source.Length - 2) / 2];
2074

2075
                    for (int i = 1; i < source.Length / 2; i++)
2076
                        result[i - 1] = byte.Parse(source.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier);
2077

2078
                    return result;
2079
                }
2080
                else
2081
                {
2082
                    throw new ArgumentException("Specified string must has even length");
2083
                }
2084
            }
2085
            else
2086
            {
2087
                throw new ArgumentException(string.Format("Specified string \"{0}\" do not starts with \"{1}\"", source, "0x"));
2088
            }
2089
        }
2090

2091
        #endregion        
2092

2093
        #endregion
2094

2095
        #region Utils
2096

2097
        #region database related
2098

2099
        /// <summary>
2100
        /// Gets sentence description by specified sentenceID
2101
        /// </summary>
2102
        /// <param name="sentenceID">Sentence identified</param>
2103
        /// <returns>String that reptesents sentence desctiption</returns>
2104
        public static string GetSentenceDescription(SentenceIdentifiers sentenceID)
2105
        {
2106
            if (SentencesDescriptions.ContainsKey(sentenceID))
2107
            {
2108
                return SentencesDescriptions[sentenceID];
2109
            }
2110
            else
2111
            {
2112
                throw new ArgumentException(string.Format("Unknown sentence ID: \"{0}\"", sentenceID));
2113
            }
2114
        }
2115

2116
        /// <summary>
2117
        /// Gets talker description by specified taklerID
2118
        /// </summary>
2119
        /// <param name="talkerID">Talker identidier</param>
2120
        /// <returns>String that represents talker description</returns>
2121
        public static string GetTalkerDescription(TalkerIdentifiers talkerID)
2122
        {
2123
            if (TalkerDescriptions.ContainsKey(talkerID))
2124
            {
2125
                return TalkerDescriptions[talkerID];
2126
            }
2127
            else
2128
            {
2129
                throw new ArgumentException(string.Format("Unknown talker ID: \"{0}\"", talkerID));
2130
            }
2131
        }
2132

2133
        /// <summary>
2134
        /// Adds manufacturer node to base
2135
        /// </summary>
2136
        /// <param name="manufacturer">Manufacturer ID to add</param>
2137
        public static void AddManufacturerToProprietarySentencesBase(ManufacturerCodes manufacturer)
2138
        {
2139
            if (!ProprietarySentencesFormats.ContainsKey(manufacturer))
2140
            {
2141
                ProprietarySentencesFormats.Add(manufacturer, new Dictionary<string, string>());
2142
            }
2143
            else
2144
            {
2145
                throw new ArgumentException(string.Format("Base already contais manufacturer \"{0}\"", manufacturer));
2146
            }
2147
        }
2148

2149
        /// <summary>
2150
        /// Adds proprietary sentence description to base
2151
        /// </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>
2155
        public static void AddProprietarySentenceDescription(ManufacturerCodes manufacturer, string sentenceIDString, string formatString)
2156
        {
2157
            if (ProprietarySentencesFormats.ContainsKey(manufacturer))
2158
            {
2159
                if (!ProprietarySentencesFormats[manufacturer].ContainsKey(sentenceIDString))
2160
                {
2161
                    ProprietarySentencesFormats[manufacturer].Add(sentenceIDString, formatString);
2162
                }
2163
                else
2164
                {
2165
                    throw new ArgumentException(string.Format("Specified sentence ID \"{0}\" already exists in \"{1}\" sentences list", sentenceIDString, manufacturer));
2166
                }
2167
            }
2168
            else
2169
            {
2170
                throw new ArgumentException(string.Format("Specified manufacturer \"{0}\" not exists in base, add it first", manufacturer));
2171
            }
2172
        }
2173

2174
        public static string GetDatumDescription(int datumKey)
2175
        {
2176
            if (Datums.ContainsKey(datumKey))
2177
            {
2178
                return Datums[datumKey];
2179
            }
2180
            else
2181
            {
2182
                return string.Empty;
2183
            }
2184
        }
2185

2186
        #endregion
2187

2188
        /// <summary>
2189
        /// Calculates NMEA0183 checksum (byte-by-byte XOR) for specified string
2190
        /// </summary>
2191
        /// <param name="source">string to calculate checksum</param>
2192
        /// <returns></returns>
2193
        public static byte GetCheckSum(string source)
2194
        {
2195
            byte result = 0;
2196
            byte[] bytes = Encoding.ASCII.GetBytes(source);
2197

2198
            for (int i = 0; i < bytes.Length; i++)
2199
                result = (byte)(result ^ bytes[i]);
2200

2201
            return result;
2202
        }
2203

2204
        /// <summary>
2205
        /// Converts marine bend to meters per second
2206
        /// </summary>
2207
        /// <param name="speedBends">Speed in bends</param>
2208
        /// <returns>Speed in meters per second</returns>
2209
        public static double Bend2MpS(double speedBends)
2210
        {
2211
            return speedBends * 0.5144;
2212
        }
2213

2214
        /// <summary>
2215
        /// Converts distance in miles to kilometers
2216
        /// </summary>
2217
        /// <param name="nMiles">Distance in miles</param>
2218
        /// <returns>Distance in kilometers</returns>
2219
        public static double NM2Km(double nMiles)
2220
        {
2221
            return nMiles * 1.852;
2222
        }
2223

2224
        /// <summary>
2225
        /// Converts distance in feets to meters
2226
        /// </summary>
2227
        /// <param name="feets">Distance in feets</param>
2228
        /// <returns>Distance in meters</returns>
2229
        public static double Ft2M(double feets)
2230
        {
2231
            return feets * 0.3048;
2232
        }
2233

2234
        #endregion
2235

2236
        #endregion
2237
    }
2238
}

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.