1. RTCM 3.3 消息帧结构解析

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。

RTCM 3.3 帧结构 (字节偏移) 0xD3 前导码(1B) 保留(6bit) 长度(10bit) 帧头(2B) 消息内容 (长度可变) 消息类型+数据 CRC-24Q 校验(3B) 偏移: 0 1 3 3+len 6+len 解析流程 ① 检查前导码0xD3 ② 提取长度+保留位 ③ CRC-24Q校验 ④ 输出解析结果