天天看點

在Windows Mobile和Wince(Windows Embedded CE)下使用.NET Compact Framework進行GPS NMEA data資料分析的開發

 轉:http://www.cnblogs.com/procoder/archive/2009/05/05/1450219.html

提供GPS功能的Wince和Windows Mobile都需要一個GPS接收器(GPS Receiver)。GPS receiver就像一個收音機,他從太空中各個GPS衛星(Satellites)接收信号,通過自身的算法(一般在Firmware裡面)計算出位置等資訊,然後以NMEA data的格式輸出。GPS receiver就是接收衛星信号轉換成NMEA data的裝置。

進行GPS的開發需要從GPS receiver取出NMEA data,分析出關心的資料。關心的資料包括經度(Longitude),次元(Latitude)和海拔(Altitude)等等。在Windows Mobile 5以上MS提供了GPS Intermediate Driver,開發人員不再需要自己分析NMEA data了。但是Wince5以及以下版本不提供GPS Intermediate Driver,還是需要自己分析NMEA data來取出關心的資訊。本文講述如何使用C#進行NMEA data的分析。第一眼看,分析NMEA有自己做輪子之嫌,其實了解NMEA的分析也是有好處的,由于各個生産GPS receiver的廠商在硬體工藝和算法的不一樣,各個廠商都提供自己擴充的NMEA data,這些資料GPS Intermediate Driver是不支援的,需要自己分析。

NMEA 全稱NMEA 0183,是電子與資料的通信規範,也就是協定。實作該協定的裝置輸出這種規範的資料,其他應用就可以基于這協定分析出相關的資料。NMEA開始用在航海裝置上,現在廣泛用在GPS裝置上,這就是為什麼NMEA的原始速度使用Knot(海裡/小時)表示。下面是一段GPS NMEA data的範例

$GPRMC, 000006 ,A, 3754.6240 ,S, 14509.7720 ,E, 010.8 , 313.1 , 010108 , 011.8 ,E * 6A

$GPGGA, 201033 , 3754.6240 ,S, 14509.7720 ,E, 1 , 05 , 1.7 , 91.1 ,M, - 1.1 ,M,, * 75

$GPGSA,A, 3 ,, 05 , 10 ,,,, 21 ,, 29 , 30 ,,, 2.9 , 1.7 , 1.3 * 32

$GPGSV, 3 , 3 , 12 , 29 , 74 , 163 , 41 , 30 , 53 , 337 , 40 , 31 , 09 , 266 , 00 , 37 , 00 , 000 , 00 * 78

$PGRME, 6.3 ,M, 11.9 ,M, 13.5 ,M * 25

$PGRMB, 0.0 , 200 ,,,,K,,N,W * 28

$PGRMM,WGS  84 * 06 複制代碼

GPS NMEA data有以下特點:

* 每一條NMEA data的資料都是以dollar符号開頭。

* 從第二個字元開始的前2個字元表示發送者(talker)和接着3個字元表示資料(message)。其中上面的talker中,GP表示通用的GPS NMEA data,而PG為特定廠商的NMEA data。

* 所有資料字段(data fields)都是使用逗号隔開(comma-delimited)。

* 最後一個資料段接着一個星号(asterisk)。

* 星号後面是兩位數字的校正碼(checksum),checksum的計算方法是或計算在 '$' 和 '*'之間的所有字元。

* 最後以回車換行(<CR><LF>)結尾。

有了上述規範,開發NMEA的分析器就變得十分簡單,分析流程是:先接收一條NMEA語句(NMEA sentence),然後檢查語句格式,檢查checksum,然後再根據talker和message進行分發,使用不同的算法進行分析。下面為核心分析流程。

         public   bool  Parse( string  sentence)

        {

             string  rawData  =  sentence;

             try

            {

                 if  ( ! IsValid(sentence))

                {

                     return   false ;

                }

                sentence  =  sentence.Substring( 1 , sentence.IndexOf( ' * ' )  -   1 );

                 string [] Words  =  Getwords(sentence);

                 switch  (Words[ 0 ])

                {

                     case   " GPRMC " :

                         return  ParseGPRMC(Words);

                     case   " GPGGA " :

                         return  ParseGPGGA(Words);

                     case   " GPGSA " :

                         return  ParseGPGSA(Words);

                     case   " GPGSV " :

                         return  ParseGPGSV(Words);

                     default :

                         return   false ;

                }

            }

             catch  (Exception e)

            {

                Console.WriteLine(e.Message  +  rawData);

                 return   false ;

            }

        } 複制代碼

代碼1

Parse為分析接口,所有從GPS Receiver接收到NMEA data全部調用這個接口進行分析。

IsValid檢驗該NMEA sentence是否有效。

Checksum進行Checksum運算,檢驗校驗碼是否有效。

接下來,講述關鍵語句的分析。進行語句的分析,需要一個NMEA的規範手冊,這個手冊可以從GPS廠商下載下傳,例如從Garmin下載下傳 NMEA手冊

在該手冊的第24頁可以看到GPRMC的協定定義。

在Windows Mobile和Wince(Windows Embedded CE)下使用.NET Compact Framework進行GPS NMEA data資料分析的開發

圖1

根據手冊的規範定義,抽取想要的資訊。如下代碼:

         private   bool  ParseGPRMC( string [] Words)

        {

             if  (Words[ 1 ].Length  >   0   &  Words[ 9 ].Length  >   0 )

            {

                 int  UtcHours  =  Convert.ToInt32(Words[ 1 ].Substring( 0 ,  2 ));

                 int  UtcMinutes  =  Convert.ToInt32(Words[ 1 ].Substring( 2 ,  2 ));

                 int  UtcSeconds  =  Convert.ToInt32(Words[ 1 ].Substring( 4 ,  2 ));

                 int  UtcMilliseconds  =   0 ;

                 //  Extract milliseconds if it is available

                 if  (Words[ 1 ].Length  >   7 )

                {

                    UtcMilliseconds  =  Convert.ToInt32(Words[ 1 ].Substring( 7 ));

                }

                 int  UtcDay  =  Convert.ToInt32(Words[ 9 ].Substring( 0 ,  2 ));

                 int  UtcMonth  =  Convert.ToInt32(Words[ 9 ].Substring( 2 ,  2 ));

                 //  available for this century

                 int  UtcYear  =  Convert.ToInt32(Words[ 9 ].Substring( 4 ,  2 ))  +   2000 ;

                utcDateTime  =   new  DateTime(UtcYear, UtcMonth, UtcDay, UtcHours, UtcMinutes, UtcSeconds, UtcMilliseconds);

            }

            fixStatus  =  (Words[ 2 ][ 0 ]  ==   ' A ' )  ?  FixStatus.Obtained : FixStatus.Lost;

             if  (Words[ 3 ].Length  >   0   &  Words[ 4 ].Length  ==   1   &  Words[ 5 ].Length  >   0   &  Words[ 6 ].Length  ==   1 )

            {

                latitude.Hours  =   int .Parse(Words[ 3 ].Substring( 0 ,  2 ));

                latitude.Minutes  =   int .Parse(Words[ 3 ].Substring( 2 ,  2 ));

                latitude.Seconds  =  Math.Round( double .Parse(Words[ 3 ].Substring( 5 ,  4 ))  *   6   /   1000.0 ,  3 );

                 if  ( " S "   ==  Words[ 4 ])

                {

                    latitude.Hours  =   - latitude.Hours;

                }

                longitude.Hours  =   int .Parse(Words[ 5 ].Substring( 0 ,  3 ));

                longitude.Minutes  =   int .Parse(Words[ 5 ].Substring( 3 ,  2 ));

                longitude.Seconds  =  Math.Round( double .Parse(Words[ 5 ].Substring( 6 ,  4 ))  *   6   /   1000.0 ,  3 );

                 if  ( " W "   ==  Words[ 6 ])

                {

                    longitude.Hours  =   - longitude.Hours;

                }

            }

             if  (Words[ 8 ].Length  >   0 )

            {

                azimuth  =   decimal .Parse(Words[ 8 ], NmeaCultureInfo);

            }

             if  (Words[ 7 ].Length  >   0 )

            {

                velocity  =   decimal .Parse(Words[ 7 ], NmeaCultureInfo)  *  KMpHPerKnot;

            }

             return   true ;

        } 複制代碼

代碼2

從ParseGPRMC看,傳遞的參數是一個string的數組,分析要做的事情就是把數組的元素根據手冊翻譯成需要的資訊,例如字段1為UTC的日期資訊,字段9為UTC的時間資訊。字段2為fix資訊,就是GPS是否完成了初始化的資訊。字段3,4為經度,字段5,6為次元。字段7為速度。字段8為角度。

         private   bool  ParseGPGGA( string [] Words)

        {

             if  (Words[ 6 ].Length  >   0 )

            {

                 switch  (Convert.ToInt32(Words[ 6 ]))

                {

                     case   0 :

                        differentialGpsType  =  DifferentialGpsType.NotSet;

                         break ;

                     case   1 :

                        differentialGpsType  =  DifferentialGpsType.SPS;

                         break ;

                     case   2 :

                        differentialGpsType  =  DifferentialGpsType.DSPS;

                         break ;

                     case   3 :

                        differentialGpsType  =  DifferentialGpsType.PPS;

                         break ;

                     case   4 :

                        differentialGpsType  =  DifferentialGpsType.RTK;

                         break ;

                     default :

                        differentialGpsType  =  DifferentialGpsType.NotSet;

                         break ;

                }

            }

             if  (Words[ 7 ].Length  >   0 )

            {

                satellitesInUsed  =  Convert.ToInt32(Words[ 7 ]);

            }

             if  (Words[ 8 ].Length  >   0 )

            {

                horizontalDilutionOfPrecision  =  Convert.ToDecimal(Words[ 8 ]);

            }

             if  (Words[ 9 ].Length  >   0 )

            {

                altitude  =  Convert.ToDecimal(Words[ 9 ]);

            }

             return   true ;

        } 複制代碼

代碼3

分析ParseGPGGA和分析ParseGPRMC一樣,從數組抽取資訊,字段6為fix類型,這個參數表示使用了那些輔佐衛星或者地面信号站來提高GPS的精度。SPS為普通類型,DSPS使用了DGPS 地面信号站fix,DSPS使用了WAAS位置衛星fix(隻是用在美國),PPS使用了EGNOS位置衛星fix(隻是用在歐洲),RTK使用了MSAS位置衛星fix(隻是用在亞洲)。字段7為使用衛星的數量。字段8為水準精度。字段9為海拔。

         private   bool  ParseGPGSA( string [] Words)

        {

             if  (Words[ 1 ].Length  >   0 )

            {

                fixMode  =  Words[ 1 ][ 0 ]  ==   ' A '   ?  FixMode.Auto : FixMode.Manual;

            }

             if  (Words[ 2 ].Length  >   0 )

            {

                 switch  (Convert.ToInt32(Words[ 2 ]))

                {

                     case   1 :

                        fixMethod  =  FixMethod.NotSet;

                         break ;

                     case   2 :

                        fixMethod  =  FixMethod.Fix2D;

                         break ;

                     case   3 :

                        fixMethod  =  FixMethod.Fix3D;

                         break ;

                     default :

                        fixMethod  =  FixMethod.NotSet;

                         break ;

                }

            }

             foreach  (GpsSatellite s  in  satellites.Values)

            {

                s.InUsed  =   false ;

            }

            satellitesInUsed  =   0 ;

             for  ( int  i  =   0 ; i  <   12 ;  ++ i)

            {

                 string  id  =  Words[ 3   +  i];

                 if  (id.Length  >   0 )

                {

                     int  nId  =  Convert.ToInt32(id);

                     if  ( ! satellites.ContainsKey(nId))

                    {

                        satellites[nId]  =   new  GpsSatellite();

                        satellites[nId].PRC  =  nId;

                    }

                    satellites[nId].InUsed  =   true ;

                     ++ satellitesInUsed;

                }

            }

             if  (Words[ 15 ].Length  >   0 )

            {

                positionDilutionOfPrecision  =  Convert.ToDecimal(Words[ 15 ]);

            }

             if  (Words[ 16 ].Length  >   0 )

            {

                horizontalDilutionOfPrecision  =  Convert.ToDecimal(Words[ 16 ]);

            }

             if  (Words[ 17 ].Length  >   0 )

            {

                verticalDilutionOfPrecision  =  Convert.ToDecimal(Words[ 17 ]);

            }

             return   true ;

        } 複制代碼

代碼4

從ParseGPGSA看,字段1為fix的狀态,手工fix或者自動fix。字段2為fix的方法,2D或者3D。字段3到14共12個字段分别為在使用衛星的資訊。字段15為位置精度資訊。字段16為水準精度資訊。字段17為垂直精度資訊。

         private   bool  ParseGPGSV( string [] Words)

        {

             int  messageNumber  =   0 ;

             if  (Words[ 2 ].Length  >   0 )

            {

                messageNumber  =  Convert.ToInt32(Words[ 2 ]);

            }

             if  (Words[ 3 ].Length  >   0 )

            {

                satellitesInView  =  Convert.ToInt32(Words[ 3 ]);

            }

             if  (messageNumber  ==   0   ||  satellitesInView  ==   0 )

            {

                 return   false ;

            }

             for  ( int  i  =   1 ; i  <=   4 ;  ++ i)

            {

                 if  ((Words.Length  -   1 )  >=  (i  *   4   +   3 ))

                {

                     int  nId  =   0 ;

                     if  (Words[i  *   4 ].Length  >   0 )

                    {

                         string  id  =  Words[i  *   4 ];

                        nId  =  Convert.ToInt32(id);

                         if  ( ! satellites.ContainsKey(nId))

                        {

                            satellites[nId]  =   new  GpsSatellite();

                            satellites[nId].PRC  =  nId;

                        }

                        satellites[nId].InView  =   true ;

                    }

                     if  (Words[i  *   4   +   1 ].Length  >   0 )

                    {

                        satellites[nId].Elevation  =  Convert.ToInt32(Words[i  *   4   +   1 ]);

                    }

                     if  (Words[i  *   4   +   2 ].Length  >   0 )

                    {

                        satellites[nId].Azimuth  =  Convert.ToInt32(Words[i  *   4   +   2 ]);

                    }

                     if  (Words[i  *   4   +   3 ].Length  >   0 )

                    {

                        satellites[nId].SNR  =  Convert.ToInt32(Words[i  *   4   +   3 ]);

                        satellites[nId].NotTracking  =   false ;

                    }

                     else

                    {

                        satellites[nId].NotTracking  =   true ;

                    }

                }

            }

             return   true ;

        } 複制代碼

代碼5

從ParseGPGSA看,這個比較特别,他把在使用的衛星資訊分開多條語句output。如下:

$GPGSV, 3 , 1 , 12 , 03 , 43 , 246 , 46 , 06 , 57 , 263 , 52 , 09 , 10 , 090 , 00 , 14 , 29 , 357 , 41 * 71

$GPGSV, 3 , 2 , 12 , 15 , 12 , 140 , 00 , 16 , 10 , 307 , 00 , 18 , 59 , 140 , 00 , 19 , 20 , 224 , 00 * 75

$GPGSV, 3 , 3 , 12 , 21 , 48 , 089 , 00 , 22 , 69 , 265 , 36 , 24 , 09 , 076 , 00 , 34 , 00 , 000 , 00 * 76

複制代碼

字段1為一共分開多少條語句。字段2為目前語句的序号。字段3為在使用的衛星的數量。後面字段分别表示三個不同衛星的資訊,取其中一個衛星來解釋,字段4為衛星的ID,字段5為太空海拔,字段6為角度,字段7為信号強弱。

對于廠商的私有NMEA data也是同樣的方法進行分析,根據文檔的描述進行分析。下面為整個類的代碼。

在Windows Mobile和Wince(Windows Embedded CE)下使用.NET Compact Framework進行GPS NMEA data資料分析的開發
在Windows Mobile和Wince(Windows Embedded CE)下使用.NET Compact Framework進行GPS NMEA data資料分析的開發

NmeaParser

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/

-->    public class NmeaParser

    {

        public struct Coordinate

        {

            public int Hours;

            public int Minutes;

            public double Seconds;

        }

        public enum FixStatus

        {

            NotSet,

            Obtained, //A

            Lost //V

        }

        public enum FixMode

        {

            Auto,   //A

            Manual

        }

        public enum FixMethod

        {

            NotSet,

            Fix2D,

            Fix3D

        }

        public enum DifferentialGpsType

        {

            NotSet, 

            SPS,    

            DSPS,   

            PPS,    

            RTK     

        }

        public class GpsSatellite

        {

            public int PRC { get; set; }

            public int Elevation { get; set; }

            public int Azimuth { get; set; }

            public int SNR { get; set; }

            public bool InUsed { get; set; }

            public bool InView { get; set; }

            public bool NotTracking { get; set; }

        }

        private static readonly CultureInfo NmeaCultureInfo = new CultureInfo("en-US");

        private static readonly decimal KMpHPerKnot = decimal.Parse("1.852", NmeaCultureInfo);

        private Coordinate latitude;

        private Coordinate longitude;

        private decimal altitude = 0;

        private DateTime utcDateTime;

        private decimal velocity = 0;

        private decimal azimuth = 0;

        private FixStatus fixStatus;

        private DifferentialGpsType differentialGpsType;

        private FixMode fixMode;

        private FixMethod fixMethod;

        private int satellitesInView;

        private int satellitesInUsed;

        private readonly Dictionary<int, GpsSatellite> satellites;

        private decimal horizontalDilutionOfPrecision = 50;

        private decimal positionDilutionOfPrecision = 50;

        private decimal verticalDilutionOfPrecision = 50;

        public NmeaParser()

        {

            satellites = new Dictionary<int, GpsSatellite>();

        }

        public bool Parse(string sentence)

        {

            string rawData = sentence;

            try

            {

                if (!IsValid(sentence))

                {

                    return false;

                }

                sentence = sentence.Substring(1, sentence.IndexOf('*') - 1);

                string[] Words = Getwords(sentence);

                switch (Words[0])

                {

                    case "GPRMC":

                        return ParseGPRMC(Words);

                    case "GPGGA":

                        return ParseGPGGA(Words);

                    case "GPGSA":

                        return ParseGPGSA(Words);

                    case "GPGSV":

                        return ParseGPGSV(Words);

                    default:

                        return false;

                }

            }

            catch (Exception e)

            {

                Console.WriteLine(e.Message + rawData);

                return false;

            }

        }

        private bool IsValid(string sentence)

        {

            // GPS data can't be zero length

            if (sentence.Length == 0)

            {

                return false;

            }

            // first character must be a $

            if (sentence[0] != '$')

            {

                return false;

            }

            // GPS data can't be longer than 82 character

            if (sentence.Length > 82)

            {

                return false;

            }

            try

            {

                string checksum = sentence.Substring(sentence.IndexOf('*') + 1);

                return Checksum(sentence, checksum);

            }

            catch (Exception e)

            {

                Console.WriteLine("Checksum failure. " + e.Message);

                return false;

            }

        }

        private bool Checksum(string sentence, string checksumStr)

        {

            int checksum = 0;

            int length = sentence.IndexOf('*') - 1;

            // go from first character upto last *

            for (int i = 1; i <= length; ++i)

            {

                checksum = checksum ^ Convert.ToByte(sentence[i]);

            }

            return (checksum.ToString("X2") == checksumStr);

        }

        // Divides a sentence into individual Words

        private static string[] Getwords(string sentence)

        {

            return sentence.Split(',');

        }

        private bool ParseGPRMC(string[] Words)

        {

            if (Words[1].Length > 0 & Words[9].Length > 0)

            {

                int UtcHours = Convert.ToInt32(Words[1].Substring(0, 2));

                int UtcMinutes = Convert.ToInt32(Words[1].Substring(2, 2));

                int UtcSeconds = Convert.ToInt32(Words[1].Substring(4, 2));

                int UtcMilliseconds = 0;

                // Extract milliseconds if it is available

                if (Words[1].Length > 7)

                {

                    UtcMilliseconds = Convert.ToInt32(Words[1].Substring(7));

                }

                int UtcDay = Convert.ToInt32(Words[9].Substring(0, 2));

                int UtcMonth = Convert.ToInt32(Words[9].Substring(2, 2));

                // available for this century

                int UtcYear = Convert.ToInt32(Words[9].Substring(4, 2)) + 2000;

                utcDateTime = new DateTime(UtcYear, UtcMonth, UtcDay, UtcHours, UtcMinutes, UtcSeconds, UtcMilliseconds);

            }

            fixStatus = (Words[2][0] == 'A') ? FixStatus.Obtained : FixStatus.Lost;

            if (Words[3].Length > 0 & Words[4].Length == 1 & Words[5].Length > 0 & Words[6].Length == 1)

            {

                latitude.Hours = int.Parse(Words[3].Substring(0, 2));

                latitude.Minutes = int.Parse(Words[3].Substring(2, 2));

                latitude.Seconds = Math.Round(double.Parse(Words[3].Substring(5, 4)) * 6 / 1000.0, 3);

                if ("S" == Words[4])

                {

                    latitude.Hours = -latitude.Hours;

                }

                longitude.Hours = int.Parse(Words[5].Substring(0, 3));

                longitude.Minutes = int.Parse(Words[5].Substring(3, 2));

                longitude.Seconds = Math.Round(double.Parse(Words[5].Substring(6, 4)) * 6 / 1000.0, 3);

                if ("W" == Words[6])

                {

                    longitude.Hours = -longitude.Hours;

                }

            }

            if (Words[8].Length > 0)

            {

                azimuth = decimal.Parse(Words[8], NmeaCultureInfo);

            }

            if (Words[7].Length > 0)

            {

                velocity = decimal.Parse(Words[7], NmeaCultureInfo) * KMpHPerKnot;

            }

            return true;

        }

        private bool ParseGPGGA(string[] Words)

        {

            if (Words[6].Length > 0)

            {

                switch (Convert.ToInt32(Words[6]))

                {

                    case 0:

                        differentialGpsType = DifferentialGpsType.NotSet;

                        break;

                    case 1:

                        differentialGpsType = DifferentialGpsType.SPS;

                        break;

                    case 2:

                        differentialGpsType = DifferentialGpsType.DSPS;

                        break;

                    case 3:

                        differentialGpsType = DifferentialGpsType.PPS;

                        break;

                    case 4:

                        differentialGpsType = DifferentialGpsType.RTK;

                        break;

                    default:

                        differentialGpsType = DifferentialGpsType.NotSet;

                        break;

                }

            }

            if (Words[7].Length > 0)

            {

                satellitesInUsed = Convert.ToInt32(Words[7]);

            }

            if (Words[8].Length > 0)

            {

                horizontalDilutionOfPrecision = Convert.ToDecimal(Words[8]);

            }

            if (Words[9].Length > 0)

            {

                altitude = Convert.ToDecimal(Words[9]);

            }

            return true;

        }

        private bool ParseGPGSA(string[] Words)

        {

            if (Words[1].Length > 0)

            {

                fixMode = Words[1][0] == 'A' ? FixMode.Auto : FixMode.Manual;

            }

            if (Words[2].Length > 0)

            {

                switch (Convert.ToInt32(Words[2]))

                {

                    case 1:

                        fixMethod = FixMethod.NotSet;

                        break;

                    case 2:

                        fixMethod = FixMethod.Fix2D;

                        break;

                    case 3:

                        fixMethod = FixMethod.Fix3D;

                        break;

                    default:

                        fixMethod = FixMethod.NotSet;

                        break;

                }

            }

            foreach (GpsSatellite s in satellites.Values)

            {

                s.InUsed = false;

            }

            satellitesInUsed = 0;

            for (int i = 0; i < 12; ++i)

            {

                string id = Words[3 + i];

                if (id.Length > 0)

                {

                    int nId = Convert.ToInt32(id);

                    if (!satellites.ContainsKey(nId))

                    {

                        satellites[nId] = new GpsSatellite();

                        satellites[nId].PRC = nId;

                    }

                    satellites[nId].InUsed = true;

                    ++satellitesInUsed;

                }

            }

            if (Words[15].Length > 0)

            {

                positionDilutionOfPrecision = Convert.ToDecimal(Words[15]);

            }

            if (Words[16].Length > 0)

            {

                horizontalDilutionOfPrecision = Convert.ToDecimal(Words[16]);

            }

            if (Words[17].Length > 0)

            {

                verticalDilutionOfPrecision = Convert.ToDecimal(Words[17]);

            }

            return true;

        }

        private bool ParseGPGSV(string[] Words)

        {

            int messageNumber = 0;

            if (Words[2].Length > 0)

            {

                messageNumber = Convert.ToInt32(Words[2]);

            }

            if (Words[3].Length > 0)

            {

                satellitesInView = Convert.ToInt32(Words[3]);

            }

            if (messageNumber == 0 || satellitesInView == 0)

            {

                return false;

            }

            for (int i = 1; i <= 4; ++i)

            {

                if ((Words.Length - 1) >= (i * 4 + 3))

                {

                    int nId = 0;

                    if (Words[i * 4].Length > 0)

                    {

                        string id = Words[i * 4];

                        nId = Convert.ToInt32(id);

                        if (!satellites.ContainsKey(nId))

                        {

                            satellites[nId] = new GpsSatellite();

                            satellites[nId].PRC = nId;

                        }

                        satellites[nId].InView = true;

                    }

                    if (Words[i * 4 + 1].Length > 0)

                    {

                        satellites[nId].Elevation = Convert.ToInt32(Words[i * 4 + 1]);

                    }

                    if (Words[i * 4 + 2].Length > 0)

                    {

                        satellites[nId].Azimuth = Convert.ToInt32(Words[i * 4 + 2]);

                    }

                    if (Words[i * 4 + 3].Length > 0)

                    {

                        satellites[nId].SNR = Convert.ToInt32(Words[i * 4 + 3]);

                        satellites[nId].NotTracking = false;

                    }

                    else

                    {

                        satellites[nId].NotTracking = true;

                    }

                }

            }

            return true;

        }

    }

繼續閱讀