用Python+NumPy实战卷积码编码:从理论到可视化实现

在通信工程和信息论的学习中,卷积码是一个既令人着迷又让人头疼的话题。教科书上那些抽象的状态转移图和生成多项式常常让初学者望而生畏。但当我第一次用Python代码实现了一个完整的卷积码编码器后,那些神秘的数学符号突然变得清晰可见。本文将带你用NumPy从零构建一个(3,1,3)卷积码编码器,通过实时可视化理解每个比特是如何被编码的。

1. 卷积码基础与编码器设计

卷积码与常见的分组码不同,它具有记忆性——当前输出不仅取决于当前输入,还与前几个输入有关。这种特性使得卷积码在纠错性能上往往优于分组码。我们以(3,1,3)卷积码为例,其中参数含义为:

  • 1 :每次输入1个信息比特
  • 3 :每次输出3个编码比特
  • 3 :约束长度(影响输出的前输入比特数)

编码器结构可以用移位寄存器实现。对于(3,1,3)卷积码,典型的生成多项式为:

g1 = [1, 1, 1]  # 第一个输出位的连接
g2 = [1, 0, 1]  # 第二个输出位的连接 
g3 = [1, 1, 0]  # 第三个输出位的连接

用Python表示这些多项式:

import numpy as np

# 生成多项式定义
g = np.array([
    [1, 1, 1],  # 输出位1
    [1, 0, 1],  # 输出位2
    [1, 1, 0]   # 输出位3
])

2. 编码过程逐步实现

让我们从初始化开始,逐步构建编码器。首先需要设置移位寄存器状态:

class ConvolutionalEncoder:
    def __init__(self):
        self.shift_register = np.zeros(3)  # 3位移位寄存器
        
    def encode_bit(self, input_bit):
        # 更新移位寄存器
        self.shift_register = np.roll(self.shift_register, 1)
        self.shift_register[0] = input_bit
        
        # 计算输出位
        output = np.zeros(3)
        for i in range(3):
            output[i] = np.sum(self.shift_register * g[i]) % 2
            
        return output

编码一个完整的比特流:

def encode_stream(self, bit_stream):
    encoded = []
    for bit in bit_stream:
        encoded.extend(self.encode_bit(bit))
    return np.array(encoded)

编码示例 : 输入 [1, 0, 1, 1] 的输出过程:

输入比特 移位寄存器状态 输出码组
1 [1, 0, 0] 1 1 1
0 [0, 1, 0] 1 0 1
1 [1, 0, 1] 0 0 1
1 [1, 1, 0] 0 0 1

3. 状态转移可视化

理解卷积码的关键是掌握其状态机。对于(3,1,3)码,有2^(3-1)=4种状态:

states = {
    0: '00',
    1: '01',
    2: '10',
    3: '11'
}

我们可以用NetworkX库绘制状态转移图:

import networkx as nx
import matplotlib.pyplot as plt

def draw_state_diagram():
    G = nx.DiGraph()
    
    # 添加状态转移
    transitions = [
        ('00', '00', '0/000'),
        ('00', '10', '1/111'),
        ('01', '00', '0/011'),
        ('01', '10', '1/100'),
        ('10', '01', '0/101'),
        ('10', '11', '1/010'),
        ('11', '01', '0/110'),
        ('11', '11', '1/001')
    ]
    
    for src, dst, label in transitions:
        G.add_edge(src, dst, label=label)
    
    pos = nx.spring_layout(G)
    nx.draw(G, pos, with_labels=True, node_size=2000)
    edge_labels = nx.get_edge_attributes(G, 'label')
    nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
    plt.show()

运行后会生成一个直观的状态转移图,显示输入/输出与状态变化的关系。

4. 完整编码系统实现

将上述组件整合为一个完整的编码系统,并添加可视化功能:

class VisualEncoder(ConvolutionalEncoder):
    def __init__(self):
        super().__init__()
        self.history = {
            'input': [],
            'state': [],
            'output': []
        }
    
    def encode_bit(self, input_bit):
        old_state = self.shift_register.copy()
        output = super().encode_bit(input_bit)
        
        # 记录历史
        self.history['input'].append(input_bit)
        self.history['state'].append(old_state)
        self.history['output'].append(output)
        
        return output
    
    def plot_encoding(self):
        fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6))
        
        # 绘制输入和状态
        ax1.step(range(len(self.history['input'])), 
                self.history['input'], 
                where='post', label='Input')
        ax1.set_ylabel('Input Bit')
        
        # 绘制输出
        output = np.array(self.history['output'])
        for i in range(3):
            ax2.step(range(len(output)), 
                    output[:, i], 
                    where='post', 
                    label=f'Output {i+1}')
        ax2.set_ylabel('Output Bits')
        ax2.set_xlabel('Time Step')
        
        plt.legend()
        plt.tight_layout()
        plt.show()

使用示例:

encoder = VisualEncoder()
input_bits = [1, 0, 1, 1, 0, 0, 1]
encoder.encode_stream(input_bits)
encoder.plot_encoding()

这将生成一个分步显示输入比特和三个输出比特变化的时序图,直观展示编码过程。

5. 性能分析与实际应用

卷积码的性能通常用自由距离衡量——任意两条不同编码路径间的最小汉明距离。通过枚举可能的编码路径,我们可以计算自由距离:

def calculate_free_distance(encoder, max_length=10):
    # 尝试所有可能的输入序列
    min_distance = float('inf')
    
    for length in range(1, max_length+1):
        for bits in itertools.product([0,1], repeat=length):
            encoder.reset()
            encoded1 = encoder.encode_stream(bits)
            
            # 尝试找到不同的输入产生的最小距离
            for other_bits in itertools.product([0,1], repeat=length):
                if other_bits == bits:
                    continue
                    
                encoder.reset()
                encoded2 = encoder.encode_stream(other_bits)
                
                distance = np.sum(encoded1 != encoded2)
                if distance < min_distance:
                    min_distance = distance
                    
    return min_distance

对于我们的(3,1,3)编码器,自由距离为5,这意味着它可以纠正最多2个随机错误。

在实际通信系统中,卷积码常与维特比译码配合使用。虽然本文聚焦编码,但理解编码过程是设计高效译码器的基础。例如,在卫星通信和2G/3G移动通信中,卷积码都发挥了重要作用。

更多推荐