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("解析失败")
运行与验证
- 将代码保存为
wifi11p_parser.py,直接运行:python3 wifi11p_parser.py - 预期输出(确定性结果):
Rate: 0xa (应=0xa)
Length: 10 (应=10)
Parity校验: 通过
PSDU长度: 10 字节
验证通过条件:输出与上述预期完全一致,且Parity校验显示“通过”。若修改测试帧中任意bit,解析器应能正确报错或校验失败。
常见问题
- 前导码处理:实际捕获数据可能已去除前导码,代码通过长度判断自动兼容两种输入。
- 字节序:Signal字段按IEEE 802.11标准使用MSB-first传输,代码使用
big字节序解析。 - PSDU长度:Length字段单位为字节,但物理层会按OFDM符号填充,代码按字节边界截取,实际应用中需根据Rate计算符号数。