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

C 語言實戰:GPS NMEA-0183 協議解析與北京時間提取

     編輯:C 語言入門

本文記錄一道來自中國大學 MOOC《C 語言程序設計》第九周作業的實戰題目:解析 GPS 接收機輸出的 NMEA-0183 協議數據流,提取有效的 $GPRMC 語句,驗證校驗和,篩選定位狀態,最終將 UTC 時間轉換為北京時間輸出。涉及字符串處理、指針操作、位運算、時間計算等核心技能。

題目背景與數據格式

NMEA-0183 是海事電子設備通信標準。$GPRMC(推薦最小定位信息)語句格式:

$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50
字段含義示例
0語句 ID$GPRMC
1UTC 時間 (hhmmss.sss)024813.640
2狀態:A=定位,V=未定位A
3-4緯度/半球3158.4608,N
5-6經度/半球11848.3737,E
7速度(節)10.05
8方位角(度)324.27
9UTC 日期 (DDMMYY)150706
10-11磁偏角/方向(空)
12校驗和標識 + 十六進制值*50

校驗規則:$ 與 * 之間所有字符異或 (XOR) 後對 65536 取模,結果應等於 * 後兩位十六進制數(大寫 A-F)。

解題思路

  1. 逐行讀入,遇到 END 終止
  2. 篩選包含 $GPRMC 且狀態字段為 ,A, 的行
  3. 計算校驗和:從 $ 後第一字符開始,逐字符 ^= 直到 * 前
  4. 將計算值格式化為兩位大寫十六進制 %02X,與 * 後字符比對
  5. 校驗通過後,提取字段 1(UTC 時間),解析時分秒
  6. UTC+8 轉北京時間:hour = (utc_hour + 8) % 24
  7. 保留最後一條有效語句的結果,格式化輸出 hh:mm:ss

關鍵技術點代碼詳解

1. 字符串讀取與結束判斷

char line[200];
while (scanf("%199s", line) == 1) {  // %s 自動跳過空白,遇換行停止
    if (strcmp(line, "END") == 0) break;
    // 處理 line...
}

注意:scanf("%s") 以空白符分隔,NMEA 語句無內部空格,完美適配。若語句可能含空格,需用 fgets()。

2. 多條件篩選:strstr() 組合

if (strstr(line, "$GPRMC") && strstr(line, ",A,")) {
    // 同時滿足語句類型與定位狀態
}

strstr(haystack, needle) 返回子串首次出現地址,非 NULL 即真。,A, 精確匹配狀態字段,避免誤匹配其他字段中的 A。

3. 校驗和計算:異或累積

unsigned char checksum = 0;
for (char *p = line + 1; *p != '*'; ++p)  // 跳過 '$'
    checksum ^= *p;
checksum %= 65536;  // 實際上 unsigned char 自動模 256,題目要求模 65536 此處無影響

原理:XOR 滿足結合律/交換律,順序無關。unsigned char 保證 8 位無符號,最終值 0-255。

4. 十六進制格式化比對:sprintf() + strcmp()

char calc[3];
sprintf(calc, "%02X", checksum);  // 大寫,不足兩位前補 0
char *star = strchr(line, '*');
if (star && strcmp(calc, star + 1) == 0) {
    // 校驗通過
}

%02X 確保單個十六進制數字(如 0x0A)輸出 0A 而非 A,與協議格式一致。

5. 時間字段提取:strchr() 定位 + strncpy() + atoi()

char *comma = strchr(line, ',');  // 定位第一個逗號(字段 0 結束)
comma++;  // 指向字段 1 首字符
char time_str[7];
strncpy(time_str, comma, 6);  // 取 hhmmss
time_str[6] = '\0';
int utc_time = atoi(time_str);  // 整數 24813
int hour = utc_time / 10000;
int minute = (utc_time % 10000) / 100;
int second = utc_time % 100;

6. 時區轉換與格式化輸出

hour = (hour + 8) % 24;  // UTC+8
printf("%02d:%02d:%02d\n", hour, minute, second);  // %02d 雙位補零

完整可編譯代碼

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main() {
    char line[200];
    int final_hour = 0, final_min = 0, final_sec = 0;
    int has_valid = 0;

    while (scanf("%199s", line) == 1) {
        if (strcmp(line, "END") == 0) break;

        // 1. 篩選 $GPRMC 且定位有效
        if (!strstr(line, "$GPRMC") || !strstr(line, ",A,")) continue;

        // 2. 計算校驗和
        unsigned char sum = 0;
        char *p = line + 1;  // 跳過 '$'
        while (*p && *p != '*') sum ^= *p++;

        // 3. 校驗比對
        char expected[3];
        sprintf(expected, "%02X", sum);
        char *star = strchr(line, '*');
        if (!star || strcmp(expected, star + 1) != 0) continue;

        // 4. 提取 UTC 時間(字段 1)
        char *comma = strchr(line, ',');
        if (!comma) continue;
        comma++;  // 指向時間字段
        char time_buf[7];
        strncpy(time_buf, comma, 6);
        time_buf[6] = '\0';
        int utc = atoi(time_buf);
        int h = utc / 10000;
        int m = (utc % 10000) / 100;
        int s = utc % 100;

        // 5. 轉北京時間(保留最後一條有效)
        final_hour = (h + 8) % 24;
        final_min = m;
        final_sec = s;
        has_valid = 1;
    }

    if (has_valid)
        printf("%02d:%02d:%02d\n", final_hour, final_min, final_sec);
    return 0;
}

測試用例

輸入:
$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50
END

輸出:
10:48:13

驗證:02:48:13 UTC + 8h = 10:48:13 北京時間。校驗和手算:$ 後到 * 前所有字符 XOR = 0x50,匹配。

避坑指南與擴展思考

陷阱後果修正
scanf("%s") 遇空格截斷語句含空格時解析不全改用 fgets(line, sizeof(line), stdin) 讀整行,去尾部 \n
校驗和用 int 累加而非 XOR算法錯誤,永不通過嚴格用 ^= 逐字符異或
%X 不補零0x0A 輸出 A,比對失敗用 %02X 強制兩位大寫
時間字段直接 atoi(line+7)假設字段固定位置,實際長度可變用 strchr 定位分隔符動態提取
時區跨日未處理23:00 UTC + 8 = 07:00 次日(h+8)%24 正確處理跨日

進階擴展方向

  1. 健壯解析器:實現通用 NMEA 解析器,支持 $GPGGA、$GPGSA 等語句,輸出結構體
  2. 流式處理:處理串口/TCP 流中不完整/粘包語句,狀態機解析
  3. 坐標轉換:度分格式 (DDMM.MMMM) → 十進制度 (DD.DDDDDD),支持地圖顯示
  4. 異常檢測:校驗失敗統計、信號質量評估、多源融合定位

這道題目將 C 語言「指針、字符串、位運算、標準庫」四大基礎能力串聯在真實工程場景中:從原始字節流提取語義信息,經過校驗保證可信度,再經單位轉換產出業務價值。這正是嵌入式/物聯網/車聯網開發的日常核心功夫。

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