有效NMEA语句须以$开头、含5字符标识、逗号分隔字段、以加两位十六进制校验和结尾;需校验起始位置、存在性与位置、校验和正确性、字段数量(如GPGGA必须14个),并跳过含乱码、空字段异常、校验失败或超范围值的脏数据。

如何识别有效的NMEA语句并跳过脏数据
NMEA语句必须以 $ 开头,后跟5字符标识(如 GPGGA、GPGLL),接着是逗号分隔的字段,最后是 * 加两位十六进制校验和。常见错误是把串口缓冲区残留的乱码、换行符混入解析——比如读到 "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47" 是合法的,但 "\r\n$GPGGA,..." 或 "$GPGGA,123,,456,*XX"(校验位非法)必须丢弃。
实操建议:
- 用
std::string::find("$")定位起始位置,而非无脑按行切分(NMEA不保证每行一条) - 检查
*是否存在且位于倒数第三、四位之间;缺失或位置错就整条跳过 - 校验和计算前,截取
$到*之间的子串(不含$和*),逐字节异或,结果转为大写十六进制字符串比对 - 字段数硬校验:例如
GPGGA必须有14个字段(用std::count(str.begin(), str.end(), ',')+ 1 判断)
用 std::stringstream 还是 std::strtok 拆分字段
std::stringstream 更安全但稍慢,std::strtok 快但破坏原字符串且非线程安全。实战中推荐折中方案:先用 std::string::find_first_of(",") 手动定位逗号索引,再用 substr() 提取字段——零拷贝、不改源串、可复用。
示例关键逻辑:
立即学习“C++免费学习笔记(深入)”;
std::vector<std::string> fields;
size_t start = 0, end = 0;
while ((end = line.find(',', start)) != std::string::npos) {
fields.push_back(line.substr(start, end - start));
start = end + 1;
}
fields.push_back(line.substr(start)); // 最后一个字段(无尾逗号)
注意点:
- NMEA字段允许为空(如
"$GPGGA,123519,,4807.038,N,..."),对应fields[i].empty() == true,不能直接stof() - 时间字段如
"123519"是 HHMMSS 格式,需手动拆解,不是浮点数 - 经纬度格式如
"4807.038"表示 48°07.038′,要转为十进制度:整数部分取前两位作度,小数部分整体作分,再除60
校验和计算为什么总对不上
最常踩的坑是:把 $ 或 * 包进校验范围,或忽略字符串末尾的 \r\n。NMEA标准规定——校验和只计算 $ 之后、* 之前的所有字符(不含这两个符号),且必须是原始字节值异或,不是字符ASCII码再转换。
正确做法:
- 定位
*位置,记为star_pos;若star_pos == std::string::npos,直接拒收 - 从
line[1](即跳过$)开始,到line[star_pos - 1]结束,逐字节异或 - 将异或结果转成两位十六进制大写字符串(如
0x2F → "2F"),与line.substr(star_pos + 1, 2)比较 - 用
std::stoi(hex_str, nullptr, 16)转回整数再比对更稳妥,避免大小写问题
解析后的数据怎么防溢出和异常值
GPS模块可能输出明显离谱的值:纬度字段为 "9999.999"(应为 0–90)、UTC时间为 "999999"、卫星数为负数。这些不是解析错误,而是硬件/信号异常导致的无效报文。
建议在字段转类型后立即做业务级校验:
- 纬度:检查
lat_deg = std::stoi(fields[2].substr(0,2))≤ 90,且lat_min = std::stof(fields[2].substr(2)) - 经度:同理,度部分 ≤ 180
- UTC时间:6位数字,各位拆开验证(小时
- 定位状态字段(
GPGGA第7位):只接受"0"(无效)、"1"(GPS)、"2"(DGPS),其他值视为异常 - 所有数值字段优先用
std::from_chars(C++17)替代stof,避免异常抛出,返回错误码
真正麻烦的是多语句交叉——比如 GPGGA 和 GPVTG 时间戳不一致,或同一秒内收到重复 GPRMC。这时候得加时间戳+序列号缓存,而不是单条孤立解析。



















