c++无法直接解析nmea语句,需先通过串口(如/dev/ttyusb0或com3)可靠接收原始字符串,再手动校验xor校验和、处理空字段及度分格式转换。

不能直接用 C++ 解析 NMEA 语句——你得先拿到串口或网络上的原始 $GPGGA、$GPRMC 这类字符串,再做解析;C++ 本身不提供 GPS 协议层支持。
怎么从串口读到 NMEA 字符串(Linux/macOS/Windows)
GPS 模块(如 UBLOX、SIMCOM)通常通过串口(/dev/ttyUSB0、COM3)输出 NMEA,C++ 要做的第一件事是可靠收数,不是解析。
- 别用
fgets或std::getline直接读——NMEA 行末是\r\n,但某些模块可能只发\n,或偶尔丢字节,导致断行错位 - 推荐用带超时的非阻塞读 + 自行缓存拼接:每次读若干字节进缓冲区,扫描
\r\n或\n切分完整句子 - Windows 下注意设置
DCB的BaudRate、ByteSize=8、StopBits=ONESTOPBIT、Parity=NOPARITY;Linux 下记得stty -F /dev/ttyUSB0 9600 raw -echo - 常见错误:
read() returns 0(串口被其他进程占用)、Permission denied(没加uucp或dialout用户组)
怎么安全解析 $GPGGA 和 $GPRMC
NMEA 是逗号分隔的 ASCII 文本,但字段含义、空值、校验和都得手动处理;没有标准库函数能“一键解析”。
- 必须校验
*后的 XOR 校验和(如$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47中*47),否则脏数据会传进定位逻辑 - 字段可能为空(如
$GPGGA,123519,,N,,E,...),std::stoi/std::stod会抛std::invalid_argument,得先!field.empty()再转 -
latitude和longitude是度分格式(ddmm.mmmm),不是十进制度:要拆成dd + mm.mmmm/60,别直接当小数 parse - 示例片段:
std::string sentence = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47"; size_t star = sentence.find('*'); if (star != std::string::npos && star > 1) { std::string chk = sentence.substr(star+1, 2); uint8_t expected = 0; for (size_t i = 1; i < star; ++i) expected ^= sentence[i]; if (chk != fmt::format("{:02X}", expected)) return; // 校验失败 }
为什么别自己写 NMEA 解析器(除非你真需要)
看似几行 std::stringstream 就能 split,但真实场景下容易漏掉边界问题。
立即学习“C++免费学习笔记(深入)”;
- 协议变体多:
$GNGGA(伽利略)、$BDGGA(北斗)字段顺序和含义略有不同,$GNRMC时间戳字段位置也和$GPRMC不同 - 性能影响不大,但维护成本高:比如 UTC 时间字段在
$GPRMC是第 2 位,在$GNRMC是第 2 或第 3 位(取决于是否含mode字段) - 已有成熟轻量库可嵌入:C++ 可用
nmea(header-only,github.com/15knots/nmea),它处理了校验、空字段、多系统前缀、坐标转换等细节 - 自己写的解析器上线后常遇到:某天模块固件升级,突然多发一个
$GPVTG,结果你的if (line.starts_with("$GPGGA"))完全跳过——而实际你需要航向信息
真正麻烦的从来不是“怎么写 parse 函数”,而是“怎么保证串口不丢帧、校验不绕过、空字段不崩、多系统不漏”。这些点一旦出问题,定位漂移或卡死,很难复现。










