You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于树莓派的DeLORME Earthmate GPS LT-20数据解析方案咨询(C语言)

用C语言解析NMEA GPS数据实现屏幕显示的思路

首先,咱们先明确核心:你拿到的这些是NMEA 0183协议的标准GPS报文,这是绝大多数民用GPS模块的通用输出格式,用C语言完全能搞定解析和显示的需求。

第一步:先搞懂你拿到的NMEA报文类型

你给出的采样数据里有几种高频实用的报文,先逐个理清重点字段:

  • $GPRMC:最小核心定位报文,包含UTC时间、定位有效性、经纬度、地面速度、日期这些关键信息
  • $GPGGA:高精度定位数据,比RMC多了卫星数量、海拔高度、差分定位状态
  • $GPVTG:地面速度与航向信息
  • $GPGSV:可见卫星的状态明细,能看到卫星编号、方位角、信号强度
  • $PSTMECH:这是DeLORME的私有报文,记录模块硬件状态,如果你只需要基础定位显示,暂时可以先忽略

第二步:C语言解析的核心思路

1. 串口数据读取优化

你已经能读取原始串口数据了,接下来要做的是按行读取——NMEA报文都是以换行(\r\n或\n)结尾的,所以每次读一行再处理,比逐字节读更高效。

  • 可以用fgets()从串口对应的文件描述符(比如树莓派上的/dev/ttyUSB0)读取,设置256字节的缓冲区足够,NMEA单条报文不会太长。

2. 报文校验与拆分

每一条NMEA末尾的*XX是校验和,建议先做校验确保数据没出错,再拆分字段:

  • 校验方法:从$后面到*前面的所有字符做异或运算,把结果转成两位十六进制,和*后的字符对比,一致再处理。
  • 拆分字段用strtok()函数,以逗号,为分隔符,逐个提取每个字段就行。

3. 字段格式转换

拿到字符串字段后,需要转成可用的数值,比如经纬度的转换是重点:

  • NMEA纬度格式是DDMM.MMMMM(度分格式),比如3XX8.64662,N,转十进制度数:DD + MM.MMMMM/60,南纬要加负号。
  • 经度是DDDMM.MMMMM,比如11XX1.28205,W,转十进制:DDD + MM.MMMMM/60,西经加负号。

4. 屏幕显示实现

如果是树莓派终端显示,用printf()格式化输出就行,还可以用\033[H\033[J实现清屏刷新,做一个实时更新的定位界面;如果是LCD屏幕,后续再对接对应的驱动函数即可。

第三步:极简代码示例框架

给你一个可直接扩展的基础框架:

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

// 计算NMEA报文校验和
int nmea_verify_checksum(char *sentence) {
    int calculated = 0;
    char *ptr = sentence + 1; // 跳过开头的$
    while (*ptr != '*' && *ptr != '\0') {
        calculated ^= *ptr++;
    }
    if (*ptr != '*') return 0;
    // 转换报文里的十六进制校验和为整数
    char hex_str[3] = {ptr[1], ptr[2], '\0'};
    int given = strtol(hex_str, NULL, 16);
    return calculated == given ? 1 : 0;
}

// 解析GPRMC核心定位报文
void parse_gprmc(char *sentence) {
    char *token = strtok(sentence, ",");
    int field_idx = 0;
    char utc_time[10], status[2], lat[12], lat_dir[2], lon[12], lon_dir[2], speed[6], date[7];

    while (token != NULL) {
        switch(field_idx) {
            case 1: strncpy(utc_time, token, sizeof(utc_time)-1); break;
            case 2: strncpy(status, token, sizeof(status)-1); break;
            case 3: strncpy(lat, token, sizeof(lat)-1); break;
            case 4: strncpy(lat_dir, token, sizeof(lat_dir)-1); break;
            case 5: strncpy(lon, token, sizeof(lon)-1); break;
            case 6: strncpy(lon_dir, token, sizeof(lon_dir)-1); break;
            case 7: strncpy(speed, token, sizeof(speed)-1); break;
            case 9: strncpy(date, token, sizeof(date)-1); break;
            default: break;
        }
        token = strtok(NULL, ",");
        field_idx++;
    }

    // 仅处理有效定位
    if (status[0] == 'A') {
        // 转换纬度为十进制
        float lat_deg = atof(lat) / 100;
        int lat_int = (int)lat_deg;
        float lat_dec = lat_int + (lat_deg - lat_int)*100 / 60;
        if (!strcmp(lat_dir, "S")) lat_dec *= -1;

        // 转换经度为十进制
        float lon_deg = atof(lon) / 100;
        int lon_int = (int)lon_deg;
        float lon_dec = lon_int + (lon_deg - lon_int)*100 / 60;
        if (!strcmp(lon_dir, "W")) lon_dec *= -1;

        // 清屏并格式化输出
        printf("\033[H\033[J");
        printf("=== 树莓派GPS定位单元 ===\n");
        printf("UTC时间:%02d:%02d:%02d.%03d\n",
            atoi(utc_time)/10000, (atoi(utc_time)/100)%100, atoi(utc_time)%100, atoi(utc_time+6));
        printf("本地日期:20%02d-%02d-%02d\n",
            atoi(date+4), atoi(date+2), atoi(date));
        printf("经纬度:%.6f, %.6f\n", lat_dec, lon_dec);
        printf("地面速度:%.1f 节(%.1f km/h)\n",
            atof(speed), atof(speed)*1.852);
        printf("定位状态:✅ 有效锁定\n");
    } else {
        printf("\033[H\033[J");
        printf("=== 树莓派GPS定位单元 ===\n");
        printf("定位状态:❌ 无效,请等待卫星锁定\n");
    }
}

int main() {
    // 打开GPS串口(默认波特率9600,需提前确认模块参数)
    FILE *gps_fd = fopen("/dev/ttyUSB0", "r");
    if (!gps_fd) {
        perror("无法打开GPS串口");
        return 1;
    }

    char buffer[256];
    while (fgets(buffer, sizeof(buffer), gps_fd) != NULL) {
        // 去除换行符
        buffer[strcspn(buffer, "\r\n")] = '\0';
        // 校验并处理GPRMC报文
        if (nmea_verify_checksum(buffer) && strstr(buffer, "$GPRMC")) {
            parse_gprmc(buffer);
            sleep(1); // 控制刷新频率,避免刷屏
        }
    }

    fclose(gps_fd);
    return 0;
}

第四步:注意事项

  • 串口配置:DeLORME Earthmate LT-20默认波特率是9600,8位数据位、1位停止位、无校验,如果fopen读取异常,需要用termios库手动配置串口参数。
  • 权限问题:树莓派访问/dev/ttyUSB0需要用户加入dialout组,执行sudo usermod -aG dialout pi后重启生效。
  • 扩展功能:如果需要显示海拔、卫星数量,可以再添加$GPGGA和$GPGSV的解析逻辑,提取对应字段即可。

内容的提问来源于stack exchange,提问作者Nicholas Rice

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:47:40