2. CANopen协议PDO数据映射与解析

任务目标

实现一个IEEE 802.11p物理层帧解析器,能够从原始字节流中提取PLCP头字段(Rate、Reserved、Length、Parity、Tail)及PSDU数据,并校验Signal字段的奇偶校验位。输入为模拟的无线信道捕获字节,输出为解析后的字段值与校验结果。

环境依赖

Python 3.8+,无需第三方库,仅使用标准库struct与binascii。

核心实现

IEEE 802.11p物理层帧结构:PLCP前导码(32μs) + Signal字段(24bit) + PSDU数据。Signal字段包含:Rate(4bit)、Reserved(1bit)、Length(12bit)、Parity(1bit)、Tail(6bit)。解析时需跳过前导码,读取Signal字段并逐位拆解。

import struct

def parse_80211p_frame(raw_bytes):
    """
    解析IEEE 802.11p物理层帧
    输入: 包含前导码+Signal+PSDU的原始字节
    返回: 字段字典或None(校验失败)
    """
    # 前导码长度: 32μs @ 10MHz = 320采样点 = 40字节(8bit采样)
    # 实际捕获通常已去除前导码,此处兼容两种输入
    if len(raw_bytes) < 3:
        return None
    
    # 若包含前导码(前40字节为训练序列),跳过
    offset = 40 if len(raw_bytes) > 43 else 0
    
    # 读取Signal字段(3字节)
    signal_bytes = raw_bytes[offset:offset+3]
    if len(signal_bytes) < 3:
        return None
    
    # 解析24bit Signal字段 (MSB first)
    signal_val = int.from_bytes(signal_bytes, byteorder='big')
    
    # 字段提取
    rate = (signal_val >> 20) & 0xF          # bit 23-20
    reserved = (signal_val >> 19) & 0x1     # bit 19
    length = (signal_val >> 7) & 0xFFF      # bit 18-7
    parity = (signal_val >> 6) & 0x1        # bit 6
    tail = signal_val & 0x3F                 # bit 5-0
    
    # 奇偶校验: 对Rate+Reserved+Length共17bit计算偶校验
    calc_parity = bin((signal_val >> 7) & 0x1FFFF).count('1') % 2
    
    # 校验Tail必须为0
    if tail != 0:
        return None
    
    # 提取PSDU数据
    psdu_start = offset + 3
    psdu_len = (length + 3) // 4 * 4  # 填充到字节边界
    psdu = raw_bytes[psdu_start:psdu_start+psdu_len]
    
    return {
        'rate': rate,
        'reserved': reserved,
        'length': length,
        'parity': parity,
        'tail': tail,
        'parity_ok': (calc_parity == parity),
        'psdu': psdu
    }

# 最小验证示例
if __name__ == '__main__':
    # 构造测试帧: Rate=6Mbps(0b1010), Length=10字节, Parity=偶校验
    rate = 0b1010
    length = 10
    # 计算偶校验位
    payload_bits = (rate << 13) | length  # 17bit
    parity_bit = bin(payload_bits).count('1') % 2
    signal = (rate << 20) | (length << 7) | (parity_bit << 6)
    signal_bytes = signal.to_bytes(3, byteorder='big')
    
    # 构造完整帧: 前导码(40字节) + Signal + PSDU
    preamble = b'\x00' * 40
    psdu_data = bytes(range(10))
    test_frame = preamble + signal_bytes + psdu_data
    
    result = parse_80211p_frame(test_frame)
    if result:
        print(f"Rate: {result['rate']:#x} (应=0xa)")
        print(f"Length: {result['length']} (应=10)")
        print(f"Parity校验: {'通过' if result['parity_ok'] else '失败'}")
        print(f"PSDU长度: {len(result['psdu'])} 字节")
    else:
        print("解析失败")

运行与验证

  1. 将代码保存为wifi11p_parser.py,直接运行:python3 wifi11p_parser.py
  2. 预期输出(确定性结果):
Rate: 0xa (应=0xa)
Length: 10 (应=10)
Parity校验: 通过
PSDU长度: 10 字节

验证通过条件:输出与上述预期完全一致,且Parity校验显示“通过”。若修改测试帧中任意bit,解析器应能正确报错或校验失败。

常见问题

  • 前导码处理:实际捕获数据可能已去除前导码,代码通过长度判断自动兼容两种输入。
  • 字节序:Signal字段按IEEE 802.11标准使用MSB-first传输,代码使用big字节序解析。
  • PSDU长度:Length字段单位为字节,但物理层会按OFDM符号填充,代码按字节边界截取,实际应用中需根据Rate计算符号数。