Contents

2. 奇偶校验(Parity Check)

2.1 基本原理

  • 奇校验(Odd Parity)或 偶校验(Even Parity)在原始数据后面 附加 1 位校验位(Parity Bit),使得 整个码字中 1 的个数为奇数(奇校验)或偶数(偶校验)
  • 接收端重新统计 1 的个数,若不符合约定的奇/偶性,则判定出现错误。

优点:实现极其简单,只需异或(XOR)一次即可得到校验位。
缺点:只能检测 奇数个比特错误,对偶数个错误(如两个比特同时翻转)无能为力。

2.2 手算示例(偶校验)

假设要发送的 8 位数据为:

1
数据位 D = 1 0 1 1 0 0 1 0
  1. 统计 1 的个数:1+0+1+1+0+0+1+0 = 4(偶数)。
  2. 偶校验要求 整体 1 的个数为偶数,所以 校验位 P = 0(不需要再补 1)。
  3. 发送的码字为 D‖P = 1 0 1 1 0 0 1 0 0(共 9 位)。

接收端检测

  • 若在传输中第 3 位被翻转,收到 1 0 0 1 0 0 1 0 0,1 的个数变为 3(奇数) → 检测到错误。
  • 若第 3 位和第 5 位同时翻转,收到 1 0 0 1 1 0 1 0 0,1 的个数仍为 4(偶数)未检测到错误(偶数错误被掩盖)。

2.3 代码实现(Python)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def even_parity_bit(data: bytes) -> int:
    """返回 data(字节序列)的偶校验位(0 或 1)。"""
    # 统计所有位中 1 的个数
    ones = sum(bin(b).count('1') for b in data)
    # 偶校验:若 1 的个数为奇数,需要补 1;否则补 0
    return ones % 2

def add_even_parity(data: bytes) -> bytes:
    """在 data 末尾添加一个字节的校验位(仅最低位有效)。"""
    p = even_parity_bit(data)
    # 将校验位放在最低位,其余位填 0
    return data + bytes([p])

# 示例
payload = b'\xB2'          # 1011 0010
codeword = add_even_parity(payload)
print(f"原始: {payload.hex()}, 校验位: {codeword[-1] & 1}, 码字: {codeword.hex()}")

3. 循环冗余校验(Cyclic Redundancy Check, CRC)

3.1 基本原理

CRC 把 待发送的比特序列 看作一个 二进制多项式(系数为 0/1),并 除以 预先约定的 生成多项式(Generator Polynomial),取余数作为 冗余校验码(CRC 码) 附加在数据后面。

  • 发送端

    1. 把原始数据左移 r 位(r 为生成多项式的阶数,即最高次幂),相当于在低位补 r 个 0。
    2. 模 2 除法(即不进位的二进制除法,等价于 XOR)除以生成多项式,得到余数 R(长度为 r 位)。
    3. R 附加到左移后的数据后面,形成 发送码字
  • 接收端

    1. 对收到的码字同样用相同的生成多项式进行模 2 除法。
    2. 若余数为全 0,则认为 没有检测到错误;否则判定 出现错误

检测能力

  • 能检测所有 单比特错误双比特错误奇数个错误(取决于生成多项式的特性)。
  • 能检测所有长度 ≤ r突发错误(即连续错误位数不超过 r)。
  • 对于更长的突发错误,检测概率取决于生成多项式,一般非常高(> 99.9%)。

3.2 常用生成多项式

标准 多项式(十六进制) 阶数 r 用途
CRC‑4‑ITU x⁴ + x + 10x3 4 简单实验
CRC‑8 x⁸ + x² + x + 10x07 8 1‑Wire、蓝牙
CRC‑16‑CCITT x¹⁶ + x¹² + x⁵ + 10x1021 16 X.25、PPP
CRC‑32 (IEEE 802.3) x³² + x²⁶ + x²³ + x²² + x¹⁶ + x¹² + x¹¹ + x¹⁰ + x⁸ + x⁷ + x⁵ + x⁴ + x² + x + 10x04C11DB7 32 以太网、ZIP、PNG

下面用 CRC‑4‑ITU(生成多项式 G(x)=x⁴ + x + 1,二进制 10011)演示一次完整的手算过程。

3.3 手算示例(CRC‑4‑ITU)

原始数据(二进制):

1
D = 1 0 1 1 0 1 1 0   (8 位)

生成多项式

1
G = 1 0 0 1 1   (5 位,阶数 r = 4)

步骤 1:左移 r 位(在低位补 4 个 0)

1
D' = 1 0 1 1 0 1 1 0 0 0 0 0   (12 位)

步骤 2:模 2 除法(XOR)求余数

我们用 二进制长除法,每次把最高位对齐后进行 XOR,直到剩余位数 < r+1(即 <5 位)为止。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
            1 0 1 1 0 1 1 0 0 0 0 0   ← 被除数(D')
          ÷ 1 0 0 1 1                ← 除数(G)
-------------------------------------------------
1) 对齐最高位(第 1 位是 1),进行 XOR:
   1 0 1 1 0 1 1 0 0 0 0 0
⊕ 1 0 0 1 1
   -----------------
   0 0 1 0 1 1 0 0 0 0 0   (去掉最高位的 0,左移 1 位)

2) 最高位仍为 1(第 3 位),再 XOR:
   0 0 1 0 1 1 0 0 0 0 0
⊕   1 0 0 1 1
   -----------------
   0 0 0 1 0 1 0 0 0 0   (去掉前导 0)

3) 最高位为 1(第 4 位),再 XOR:
   0 0 0 1 0 1 0 0 0 0
⊕     1 0 0 1 1
   -----------------
   0 0 0 0 1 1 1 0 0   (去掉前导 0)

4) 最高位为 1(第 5 位),再 XOR:
   0 0 0 0 1 1 1 0 0
⊕       1 0 0 1 1
   -----------------
   0 0 0 0 0 1 0 1 1   (去掉前导 0)

5) 最高位为 1(第 6 位),再 XOR:
   0 0 0 0 0 1 0 1 1
⊕         1 0 0 1 1
   -----------------
   0 0 0 0 0 0 1 1 0   (去掉前导 0)

6) 最高位为 1(第 7 位),再 XOR:
   0 0 0 0 0 0 1 1 0
⊕           1 0 0 1 1
   -----------------
   0 0 0 0 0 0 0 1 1   (余数长度已 <5)

余数 R = **1 1**(实际应补齐到 4 位) → `0011`

步骤 3:形成发送码字

把余数 R(4 位)附加到原始数据后面:

1
码字 T = D ‖ R = 1 0 1 1 0 1 1 0 0 0 1 1

即十进制 0xB6 0x03(如果按字节划分)。

接收端验证

接收端对 T 再次进行同样的模 2 除法:

1
T ÷ G → 余数 = 0

如果在传输过程中出现了 单比特错误(比如第 2 位被翻转),余数将不为 0,检测成功。

3.4 代码实现(Python)

下面给出一个通用的 位级 CRC 实现,支持任意生成多项式(以整数形式给出):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def crc_remainder(data: bytes, poly: int, width: int) -> int:
    """
    计算 CRC 余数(remainder)。
    - data: 待校验的原始字节序列
    - poly: 生成多项式(整数形式),最高位必须为 1
    - width: 多项式的阶数(即 CRC 位数),比如 CRC-4 => width=4
    返回值为 width 位的余数(整数)。
    """
    # 把 data 转为一个大的整数(左移 width 位后再加 0)
    # 例如 data = b'\xB6' (10110110) -> 0b10110110
    # 左移 width 位相当于在低位补 width 个 0
    data_int = int.from_bytes(data, 'big')
    data_int <<= width

    # 生成多项式的位宽(包括最高位 1)
    poly_shift = poly.bit_length() - 1  # 例如 0b10011 -> 4

    # 进行模 2 除法(XOR)直到被除数的位数 < poly_shift + width
    while data_int.bit_length() >= poly_shift + 1:
        shift = data_int.bit_length() - (poly_shift + 1)
        data_int ^= (poly << shift)

    # 此时 data_int 即为余数,确保只保留 width 位
    return data_int & ((1 << width) - 1)

def crc_encode(data: bytes, poly: int, width: int) -> bytes:
    """返回 data + CRC(宽度为 width 位)的完整码字(字节序列)。"""
    remainder = crc_remainder(data, poly, width)
    # 将余数左移到最低位后拼接到原始数据后面
    # 这里假设 width <= 8,直接作为一个字节追加;若 width>8 需要更通用的处理
    if width <= 8:
        return data + bytes([remainder])
    else:
        # 把 remainder 按宽度拆成若干字节(高位在前)
        n_bytes = (width + 7) // 8
        return data + remainder.to_bytes(n_bytes, 'big')

def crc_check(frame: bytes, poly: int, width: int) -> bool:
    """检查收到的帧(data+crc)是否无误,返回 True 表示校验通过。"""
    # 直接对整个帧做 CRC 计算,若余数为 0 则通过
    return crc_remainder(frame, poly, width) == 0

# 示例:使用 CRC-4-ITU (poly=0b10011, width=4)
poly = 0b10011
width = 4
payload = bytes([0b10110110])  # 0xB6

frame = crc_encode(payload, poly, width)
print(f"原始数据: {payload.hex()}, CRC 帧: {frame.hex()}")

# 验证
print("校验通过?" , crc_check(frame, poly, width))

# 演示一次错误:翻转第 2 位
error_frame = bytearray(frame)
error_frame[0] ^= 0b01000000   # 把第 2 位 (从左数) 翻转
print("错误帧:", error_frame.hex())
print("校验通过?" , crc_check(error_frame, poly, width))

运行结果(示例)

1
2
3
4
原始数据: b6, CRC 帧: b603
校验通过? True
错误帧: b503
校验通过? False
  • b6(10110110)是原始 8 位数据。
  • 03(00000011)是 4 位 CRC(左侧 4 位为 0,实际 CRC 为 0011),拼接后得到 b603
  • 当我们把第 2 位翻转后得到 b503,校验不通过,说明 CRC 成功检测到了错误。

3.5 CRC 与奇偶校验的对比

项目 奇偶校验 CRC
实现复杂度 极低(一次 XOR) 较高(循环左移或表查)
检测能力 只能检测奇数个错误,无法检测偶数错误 能检测单比特、双比特、奇数错误以及长度 ≤ r 的突发错误
冗余位数 1 位 r 位(常见 8、16、32 位)
常见应用 串口、低速总线、磁盘块校验 以太网、USB、存储介质、无线链路、文件压缩等
误检率 0(只要出现奇数错误必检测)但 漏检率 较高(偶数错误不被检测) 误检率极低(取决于生成多项式),通常 < 10⁻⁵
适用场景 对错误率要求不高、资源受限的系统 对可靠性要求高、错误模式复杂的通信链路

4. 小结

  1. 奇偶校验:实现最简单,只用 1 位冗余,能够检测 奇数个错误,但对偶数错误无能为力。适用于 低速、低成本 的场合。
  2. 循环冗余校验(CRC):通过 模 2 多项式除法 产生 r 位冗余,能够检测 单比特、双比特、奇数错误 以及 长度 ≤ r 的突发错误,检测能力远强于奇偶校验。常见的 CRC‑8、CRC‑16、CRC‑32 在各种网络协议和存储介质中得到广泛使用。
  3. 实现要点
    • 选取合适的 生成多项式(决定检测能力)。
    • 发送端 左移 r 位后做模 2 除法,余数即 CRC。
    • 接收端 同样除法,余数为 0 则认为无误。
    • 在软件实现中,常用 查表法(256 条目)或 位移/异或循环 来加速运算。