基于树莓派的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
相关产品推荐
相关产品推荐

