1. RTCM 3.3 消息帧结构解析
任务说明
本案例要求解析RTCM 3.3协议的消息帧结构,实现从二进制字节流中识别帧边界、提取帧头信息、校验CRC-24Q并输出各字段解析结果。输入为一段包含完整RTCM 3.3帧的字节序列,输出为帧各字段的解析值及校验结果。
RTCM 3.3帧结构
RTCM 3.3消息帧由以下字段组成:
- 前导码(Preamble):1字节,固定值0xD3
- 保留位(Reserved):6比特,固定为0
- 消息长度(Message Length):10比特,表示消息内容(不含前导码、保留位、长度和CRC)的字节数
- 消息内容(Message Content):长度可变,由消息长度字段指定
- CRC-24Q校验:3字节,对从保留位开始的(2+消息长度)字节进行CRC-24Q校验
代码实现
#include <stdio.h>
#include <stdint.h>
#include <string.h>
// CRC-24Q 查表法实现
static uint32_t crc24q_table[256];
static int crc_table_ready = 0;
static void init_crc24q_table(void) {
if (crc_table_ready) return;
for (uint32_t i = 0; i < 256; i++) {
uint32_t crc = i << 16;
for (int j = 0; j < 8; j++) {
crc <<= 1;
if (crc & 0x1000000) crc ^= 0x1864CFB;
}
crc24q_table[i] = crc & 0xFFFFFF;
}
crc_table_ready = 1;
}
static uint32_t crc24q(const uint8_t *data, size_t len) {
init_crc24q_table();
uint32_t crc = 0;
for (size_t i = 0; i < len; i++) {
uint8_t idx = ((crc >> 16) ^ data[i]) & 0xFF;
crc = ((crc << 8) & 0xFFFFFF) ^ crc24q_table[idx];
}
return crc;
}
// 解析RTCM 3.3帧
// 输入: buf - 包含帧的缓冲区, buf_len - 缓冲区长度
// 输出: msg_len - 消息长度, msg_content - 消息内容指针
// 返回: 1=解析成功, 0=失败
int parse_rtcm33_frame(const uint8_t *buf, size_t buf_len,
uint16_t *msg_len, const uint8_t **msg_content) {
if (buf_len < 6) return 0; // 最小帧: 前导+2长度+1内容+3CRC
// 1. 检查前导码
if (buf[0] != 0xD3) return 0;
// 2. 提取保留位和消息长度
uint16_t word = ((uint16_t)buf[1] << 4) | (buf[2] >> 4);
uint8_t reserved = (word >> 10) & 0x3F;
uint16_t length = word & 0x3FF;
// 3. 检查保留位
if (reserved != 0) return 0;
// 4. 检查缓冲区是否足够
if (buf_len < (size_t)(3 + length + 3)) return 0;
// 5. 校验CRC-24Q (从buf[1]开始, 共2+length字节)
uint32_t calc_crc = crc24q(buf + 1, 2 + length);
uint32_t recv_crc = ((uint32_t)buf[3 + length] << 16) |
((uint32_t)buf[4 + length] << 8) |
buf[5 + length];
if (calc_crc != recv_crc) return 0;
// 6. 输出结果
*msg_len = length;
*msg_content = buf + 3;
return 1;
}
int main(void) {
// 构造一个RTCM 3.3消息帧 (消息类型1005, 内容长度8字节)
// 消息内容: 0x00 0x05 0x00 0x00 0x00 0x00 0x00 0x00
uint8_t frame[14];
frame[0] = 0xD3; // 前导码
frame[1] = 0x00; // 保留位(6bit) + 长度高4bit
frame[2] = 0x08; // 长度低8bit (消息长度=8)
// 消息内容 (8字节)
frame[3] = 0x00; frame[4] = 0x05;
frame[5] = 0x00; frame[6] = 0x00;
frame[7] = 0x00; frame[8] = 0x00;
frame[9] = 0x00; frame[10] = 0x00;
// 计算CRC-24Q (从frame[1]开始, 共10字节)
uint32_t crc = crc24q(frame + 1, 10);
frame[11] = (crc >> 16) & 0xFF;
frame[12] = (crc >> 8) & 0xFF;
frame[13] = crc & 0xFF;
// 解析帧
uint16_t msg_len = 0;
const uint8_t *msg_content = NULL;
int result = parse_rtcm33_frame(frame, sizeof(frame), &msg_len, &msg_content);
if (result) {
printf("帧解析成功\n");
printf("前导码: 0xD3\n");
printf("保留位: 0\n");
printf("消息长度: %u 字节\n", msg_len);
printf("消息内容: ");
for (uint16_t i = 0; i < msg_len; i++) {
printf("0x%02X ", msg_content[i]);
}
printf("\n");
printf("CRC-24Q校验: 通过\n");
} else {
printf("帧解析失败\n");
}
return 0;
}
编译与运行
gcc -o rtcm_parse rtcm_parse.c
./rtcm_parse
预期输出
帧解析成功
前导码: 0xD3
保留位: 0
消息长度: 8 字节
消息内容: 0x00 0x05 0x00 0x00 0x00 0x00 0x00 0x00
CRC-24Q校验: 通过
验证方法
将上述代码保存为rtcm_parse.c,编译并运行。程序构造了一个包含消息类型1005(内容8字节)的RTCM 3.3帧,然后调用parse_rtcm33_frame进行解析。若输出与预期一致,说明帧结构解析和CRC校验逻辑正确。可修改frame数组中的消息内容或长度字段,验证解析器能正确识别不同长度的帧;也可故意篡改CRC字节,验证校验失败时返回0。