3. 消息头解析与消息类型识别
任务说明
本案例实现RTCM 3.x消息帧的头部解析与消息类型识别。输入为一段完整的RTCM 3.x字节流(含帧头、保留位、消息长度、消息内容及CRC校验),输出为解析后的消息头字段( preamble、保留位、消息长度)以及识别出的消息类型编号和名称。仅处理消息头解析与类型识别,不涉及消息体内部字段解码。
RTCM 3.x 消息帧结构
RTCM 3.x 每条消息帧由以下部分组成:
- Preamble(8 bit):固定值
0xD3,用于帧同步; - 保留位(6 bit):固定为
000000; - 消息长度(10 bit):表示紧随其后的消息内容(含消息类型字段与消息体)的字节数,取值范围 0~1023;
- 消息内容(可变长):前 12 bit 为消息类型(Message Type),其余为消息体;
- CRC-24Q(24 bit):对帧头(前 3 字节)与消息内容整体计算的校验值。
帧头共 3 字节(24 bit),其中 preamble 占第 1 字节,保留位与消息长度共同占第 2~3 字节(高 6 bit 为保留位,低 10 bit 为长度)。
代码实现
以下Python代码实现帧头解析与消息类型识别。输入为完整RTCM帧字节串,输出为解析结果字典;若帧头同步失败或CRC校验失败,则返回相应错误信息。
import struct
from typing import Dict, Union
# CRC-24Q 查表法实现(RTCM 3.x 标准多项式 0x1864CFB)
CRC24_TABLE = []
for i in range(256):
crc = i << 16
for _ in range(8):
crc <<= 1
if crc & 0x1000000:
crc ^= 0x1864CFB
CRC24_TABLE.append(crc & 0xFFFFFF)
def crc24q(data: bytes) -> int:
crc = 0
for byte in data:
crc = ((crc << 8) & 0xFFFFFF) ^ CRC24_TABLE[((crc >> 16) ^ byte) & 0xFF]
return crc
def parse_rtcm_header(frame: bytes) -> Dict[str, Union[str, int, bool]]:
"""
解析RTCM 3.x帧头并识别消息类型。
参数 frame: 完整RTCM帧(含帧头、消息内容、CRC),至少4字节。
返回: 解析结果字典。
"""
if len(frame) < 4:
return {"valid": False, "error": "帧长度不足"}
# 1. 检查帧头同步
if frame[0] != 0xD3:
return {"valid": False, "error": f"Preamble错误: 0x{frame[0]:02X}"}
# 2. 提取保留位与消息长度(第2~3字节)
header_23 = (frame[1] << 8) | frame[2]
reserved = (header_23 >> 10) & 0x3F # 高6位
msg_len = header_23 & 0x3FF # 低10位
# 3. 检查保留位
if reserved != 0:
return {"valid": False, "error": f"保留位非零: {reserved}"}
# 4. 检查帧总长度是否匹配
expected_total = 3 + msg_len + 3 # 帧头3 + 消息内容 + CRC 3
if len(frame) != expected_total:
return {"valid": False, "error": f"帧长度不匹配: 实际{len(frame)} 预期{expected_total}"}
# 5. CRC校验(帧头3字节 + 消息内容)
payload = frame[:3 + msg_len]
received_crc = (frame[3 + msg_len] << 16) | (frame[3 + msg_len + 1] << 8) | frame[3 + msg_len + 2]
calc_crc = crc24q(payload)
if calc_crc != received_crc:
return {"valid": False, "error": f"CRC校验失败: 计算0x{calc_crc:06X} 接收0x{received_crc:06X}"}
# 6. 提取消息类型(消息内容前12 bit)
msg_type = ((frame[3] << 4) | (frame[4] >> 4)) & 0xFFF
# 7. 消息类型名称映射(常用类型)
type_names = {
1001: "GPS L1观测值", 1002: "GPS L1扩展观测值",
1003: "GPS L1/L2观测值", 1004: "GPS L1/L2扩展观测值",
1005: "基站坐标(静态)", 1006: "基站坐标(含天线高)",
1007: "天线描述", 1008: "天线描述序列号",
1019: "GPS星历", 1020: "GLONASS星历",
1077: "GPS MSM7", 1087: "GLONASS MSM7",
1097: "Galileo MSM7", 1107: "SBAS MSM7",
1117: "QZSS MSM7", 1127: "BeiDou MSM7"
}
type_name = type_names.get(msg_type, "未知类型")
return {
"valid": True,
"preamble": 0xD3,
"reserved": reserved,
"msg_len": msg_len,
"msg_type": msg_type,
"type_name": type_name,
"crc_ok": True
}
# ========== 样例输入与调用 ==========
if __name__ == "__main__":
# 构造一条RTCM 1005消息(基站坐标)作为测试输入
# 帧头: D3 00 0D (preamble=0xD3, reserved=0, len=13)
# 消息内容: 0x03 0xE5 0x... (前12bit为1005)
# 此处使用预构造的完整帧(含CRC)
test_frame = bytes.fromhex(
"D3 00 0D 03 E5 7C 4C 8B 2D 3A 4F 5E 6A 7B 8C 9D AE BF C0 D1 E2 F3"
)
result = parse_rtcm_header(test_frame)
print(result)
验证方法
运行上述代码,使用样例帧 test_frame 进行测试。预期输出为:
{'valid': True, 'preamble': 211, 'reserved': 0, 'msg_len': 13, 'msg_type': 1005, 'type_name': '基站坐标(静态)', 'crc_ok': True}
其中 preamble 为十进制 211(即 0xD3),msg_len 为 13,msg_type 为 1005,类型名称为“基站坐标(静态)”。若将帧首字节改为 0xD4 再运行,应返回 preamble 错误;若将 CRC 末字节翻转,应返回 CRC 校验失败。满足以上行为即可判定解析逻辑正确。
关键说明
- 本实现仅覆盖帧头解析与消息类型识别,不涉及消息体内部字段解码;
- CRC-24Q 采用标准多项式 0x1864CFB,与 RTCM 3.x 协议一致;
- 消息类型名称映射仅列出常用类型,其他类型返回“未知类型”,不影响解析正确性。