用PythonNumPy实战卷积码编码从理论到可视化实现在通信工程和信息论的学习中卷积码是一个既令人着迷又让人头疼的话题。教科书上那些抽象的状态转移图和生成多项式常常让初学者望而生畏。但当我第一次用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 10[0, 1, 0]1 0 11[1, 0, 1]0 0 11[1, 1, 0]0 0 13. 状态转移可视化理解卷积码的关键是掌握其状态机。对于(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, labellabel) pos nx.spring_layout(G) nx.draw(G, pos, with_labelsTrue, node_size2000) edge_labels nx.get_edge_attributes(G, label) nx.draw_networkx_edge_labels(G, pos, edge_labelsedge_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], wherepost, labelInput) ax1.set_ylabel(Input Bit) # 绘制输出 output np.array(self.history[output]) for i in range(3): ax2.step(range(len(output)), output[:, i], wherepost, labelfOutput {i1}) 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_length10): # 尝试所有可能的输入序列 min_distance float(inf) for length in range(1, max_length1): for bits in itertools.product([0,1], repeatlength): encoder.reset() encoded1 encoder.encode_stream(bits) # 尝试找到不同的输入产生的最小距离 for other_bits in itertools.product([0,1], repeatlength): 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移动通信中卷积码都发挥了重要作用。
别再死记硬背了!用Python+NumPy手把手带你理解卷积码的编码过程(附代码)
用PythonNumPy实战卷积码编码从理论到可视化实现在通信工程和信息论的学习中卷积码是一个既令人着迷又让人头疼的话题。教科书上那些抽象的状态转移图和生成多项式常常让初学者望而生畏。但当我第一次用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 10[0, 1, 0]1 0 11[1, 0, 1]0 0 11[1, 1, 0]0 0 13. 状态转移可视化理解卷积码的关键是掌握其状态机。对于(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, labellabel) pos nx.spring_layout(G) nx.draw(G, pos, with_labelsTrue, node_size2000) edge_labels nx.get_edge_attributes(G, label) nx.draw_networkx_edge_labels(G, pos, edge_labelsedge_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], wherepost, labelInput) ax1.set_ylabel(Input Bit) # 绘制输出 output np.array(self.history[output]) for i in range(3): ax2.step(range(len(output)), output[:, i], wherepost, labelfOutput {i1}) 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_length10): # 尝试所有可能的输入序列 min_distance float(inf) for length in range(1, max_length1): for bits in itertools.product([0,1], repeatlength): encoder.reset() encoded1 encoder.encode_stream(bits) # 尝试找到不同的输入产生的最小距离 for other_bits in itertools.product([0,1], repeatlength): 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移动通信中卷积码都发挥了重要作用。