首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET Compact Framework 解析 GPS NMEA 數據完整指南(上)

     編輯:關於.NET

在 Windows CE 與 Windows Mobile 裝置上實作 GPS 功能,核心在於 GPS 接收器。它如同收音機般從多顆衛星接收訊號,經由內建韌體演算法計算位置、速度等資訊,再依照 NMEA 0183 協定格式輸出。NMEA 全稱「National Marine Electronics Association」,早期用於航海設備,現廣泛應用於 GPS 裝置,因此速度單位仍採用「節」(Knot,海浬/小時)。

NMEA 資料結構特徵

每筆 NMEA 句子具有以下特徵:

  • 以 $ 符號開頭
  • 第 2-3 字元為 Talker ID(如 GP 代表通用 GPS,P 開頭為廠商擴充)
  • 第 4-6 字元為 Sentence Type(如 RMC、GGA)
  • 欄位以逗號分隔
  • 以星號 * 結尾,後接兩位十六進位 Checksum($ 到 * 之間所有字元的 XOR 運算)
  • 以 <CR><LF> 換行結束

範例資料:

$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

解析器核心流程

解析流程:接收句子 → 驗證格式與 Checksum → 依 Talker+Type 分派 → 逐欄位提取所需資訊。以下為 C# 核心實作:

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

關鍵語句解析實作

GPRMC - 推薦最小定位資訊

包含 UTC 時間、定位狀態、經緯度、速度、方向、日期等。解析時需注意經緯度格式為「度分.秒」,需轉換為十進位度數;南緯/西經以負號表示。

private bool ParseGPRMC(string[] words)
{
    // UTC 時間 (欄位1) 與日期 (欄位9)
    if (words[1].Length > 0 && words[9].Length > 0)
    {
        int utcHours = int.Parse(words[1].Substring(0, 2));
        int utcMinutes = int.Parse(words[1].Substring(2, 2));
        int utcSeconds = int.Parse(words[1].Substring(4, 2));
        int utcMilliseconds = words[1].Length > 7 ? int.Parse(words[1].Substring(7)) : 0;

        int utcDay = int.Parse(words[9].Substring(0, 2));
        int utcMonth = int.Parse(words[9].Substring(2, 2));
        int utcYear = int.Parse(words[9].Substring(4, 2)) + 2000;

        utcDateTime = new DateTime(utcYear, utcMonth, utcDay,
            utcHours, utcMinutes, utcSeconds, utcMilliseconds);
    }

    // 定位狀態: A=有效, V=無效
    fixStatus = (words[2][0] == 'A') ? FixStatus.Obtained : FixStatus.Lost;

    // 經緯度 (欄位3-6)
    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;
    }

    // 速度 (欄位7, 節) 轉 km/h
    if (words[7].Length > 0)
        velocity = decimal.Parse(words[7], NmeaCultureInfo) * KMpHPerKnot;

    // 方向角 (欄位8)
    if (words[8].Length > 0)
        azimuth = decimal.Parse(words[8], NmeaCultureInfo);

    return true;
}

GPGGA - 全球定位系統固定資料

提供固定品質、使用衛星數、水平精度因子 (HDOP)、海拔高度等關鍵資訊。

private bool ParseGPGGA(string[] words)
{
    // 固定品質 (欄位6)
    if (words[6].Length > 0)
    {
        switch (int.Parse(words[6]))
        {
            case 0: differentialGpsType = DifferentialGpsType.NotSet; break;
            case 1: differentialGpsType = DifferentialGpsType.SPS; break;      // 標準定位
            case 2: differentialGpsType = DifferentialGpsType.DSPS; break;     // DGPS 地面站
            case 3: differentialGpsType = DifferentialGpsType.PPS; break;      // 精密定位
            case 4: differentialGpsType = DifferentialGpsType.RTK; break;      // 即時動態
            default: differentialGpsType = DifferentialGpsType.NotSet; break;
        }
    }

    // 使用衛星數 (欄位7)
    if (words[7].Length > 0)
        satellitesInUsed = int.Parse(words[7]);

    // HDOP (欄位8)
    if (words[8].Length > 0)
        horizontalDilutionOfPrecision = decimal.Parse(words[8]);

    // 海拔 (欄位9, 公尺)
    if (words[9].Length > 0)
        altitude = decimal.Parse(words[9]);

    return true;
}

GPGSA - GPS DOP 與作用衛星

描述定位模式 (2D/3D)、定位方法、參與定位的衛星 PRN 號、位置/水平/垂直精度因子 (PDOP/HDOP/VDOP)。

GPGSV - 可見衛星資訊

多句子分段傳輸,每句最多 4 顆衛星,包含衛星 PRN、仰角、方位角、訊雜比 (SNR)。

NMEA 協定手冊 GPRMC 定義

理解 NMEA 解析雖看似「重造輪子」,但廠商擴充句子 (如 $PGRMx) 往往不被 GPS Intermediate Driver 支援,自行解析仍具必要性。下篇將探討廠商擴充句子處理與效能優化。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved