简介:一份面向通信原理课程设计、实验报告撰写和编码仿真初学者的Matlab代码包,以(15,11)汉明码为对象,系统对比其在BSC与AWGN两种典型信道下的误码率性能。代码将完整流程拆成两条链路:一条是汉明编码进BSC信道后译码、统计BER;另一条是汉明编码经BPSK调制进AWGN信道,解调译码后再计算BER,两部分均可直接运行并输出对比曲线,便于观察信道噪声对纠错编码效果的影响。压缩包共12个文件,6个.m脚本构成仿真主体,另有2张结果图片、2份Word说明文档、1个关键词txt和1个asv备份文件,整体219KB,轻量易用。当前已有581人学习/下载,既适合课堂教学演示,也可作为期末作业、课程论文或毕业设计的参考资料。借助附带说明文档与脚本注释,可以快速掌握汉明码编译码流程、BPSK调制解调实现以及误码率统计方法,节省从头搭建环境的时间,即使基础薄弱也能按步骤完成仿真实验。
1. 汉明码不是万能的:这个仿真题目为什么值得自己动手跑一遍
第一次看到“(15,11)汉明码通过BSC和AWGN信道的误码率分析”这个标题,很多人第一反应是拿现成的MATLAB自带函数hammgen、decode跑一遍,出两张图就完事。但你真把误码率曲线拉出来对比理论值时就会发现,这事远没有想象中“两条线贴在一起”那么轻松——尤其是在AWGN信道下,模型里要不要做软判决、形式怎么映射、比特能量怎么换算,每一步都有一堆模糊地带,已经工作的老手也会在“能量换算”上翻车,方差σ²写成N0还是N0/2、是否除以码率R,都能让曲线陡然偏移几个dB。标题里这组参数挺有讲究:汉明码的经典结构是(7,4),而(15,11)是最常见的一类汉明码形式,码率提升到0.733,只能纠1比特错但冗余开销明显降低,恰好能反映出“纠错增益”和“带宽代价”这对矛盾。适合做实的是三类人:通信工程学生做课程仿真想跑通流程,通信系统工程师用来当链路起始demo验证噪声模型,以及想彻底搞懂BSC和AWGN模型差别的研究者。这篇笔记把一个可复现的仿真流程从头拆到尾,含生成矩阵、伴随式译码、软硬判决差异和几个最容易踩的坑,中间穿插可直接抄的参数脚本。读完你会清楚(15,11)在两种信道下的真实表现边界在哪,也才能在面试或汇报时说清为什么某个信噪比区间曲线会出现“平台”。
2. 信道模型与汉明码参数:先把误码率分析的前提钉死
2.1 汉明码结构与(15,11)的生成矩阵、校验矩阵
汉明码本质上是“基于线性分组码”的纠错方案,核心是奇偶校验方程的编码:先把11个信息比特扩成15个码字比特,其中4个冗余校验比特用于纠正任意单个错误。这背后的参数关系并不是拍脑袋定的,最小距离d_min = 3的汉明不等式是 2^r ≥ n + 1,当校验位r = 4时,最多能覆盖n = 15,于是(n, k)=(15, 11)成立。
冗余比例较低、纠错能力有限是这套结构的基本底色:它只能纠1比特错误,若出现2比特错,译码器会“自信地”错纠成第三个位置——这恰恰是后面分析误码率曲线时看到“高信噪比下比理论差一点”的原因。
工程的常见做法是直接构造生成矩阵G和校验矩阵H。G要写成系统码形式 [I|P],H对应是[Pᵀ|I],这样编码时码字c = [信息位 校验位],译码时伴随式s = H·rᵀ直接和错误图样对应。对于(15,11),校验矩阵是4行15列,每行对应一条校验方程。这里给出一个可抄的H矩阵初始形态:
H = [1 0 0 0 1 1 1 0 1 0 1 1 0 0 1; 0 1 0 0 1 0 1 1 1 1 0 0 1 0 1; 0 0 1 0 1 1 0 1 0 1 1 1 0 1 0; 0 0 0 1 0 1 1 1 1 0 1 1 1 0 1];这个矩阵不是唯一解,系统形式的好处在于编码简单、直观,校验位放在后4位;也可以用非系统形式,但在仿真里没必要。伴随式译码时,需要先预生成一个“校正子-错误图样”查找表:遍历所有15种单比特错模型,计算各自的校正子s,后面译码时只查表即可。之所以必须建表,是因为直接实时求解线性方程会拖慢仿真循环,而预生成表只需在仿真前做一次。最终误码率分析时真正在乎的指标是“译码后的误码率(BER)”,不是“误字率(WER)”,两者在汉明码仿真里有约11/15的换算关系,PPT上往往不特意区分,自己出图的时候要统一。
2.2 BSC与AWGN:两条完全不同的错误注入逻辑
BSC(二进制对称信道)是离散信道模型:输入0/1,输出以概率p翻转。它不考虑幅度、不考虑符号能量、不考虑噪声相关性,一次传输要么对要么错。这意味着在BSC仿真里,信道根本没有“软信息”的概念,每个比特被翻转的概率完全一样,误码率的理论值由组合数直接决定——单比特错误能被纠正,但2比特以上纠不住。
AWGN(加性高斯白噪声)则站在模拟域,发送端通常是BPSK调制,把0/1映射为-1/+1,接收端叠加高斯噪声y = x + n,n服从N(0, σ²)。这个模型里接收机拿到的是连续值y,本身带着可靠性信息——y越接近门限0,该比特的可信度越低。工程里处理这段的关键分岔来了:你可以直接对y做硬判决(y > 0判为1,否则判为0)再走汉明译码,也可以保留软信息做软判决译码(比如最小欧氏距离/最大似然)。这两条路的误码率在低信噪比下可能差1-2个dB,而很多人误以为汉明码就是硬判决。
BSC更像是“对外层编码器的抽象”,AWGN则是真实物理信道的模型。标题同时列两种信道,本质是要做对比:BSC的结果是纯粹“离散纠错能力”的体现,AWGN的结果则包含了调制方式、判决方式、能量归一化这些物理因素。若只做BSC不做AWGN,你永远不知道“有线信道下加了汉明码到底增益多少”;若只做AWGN不做BSC,你又很难分离“纠错增益”和“调制增益”。所以正规的误码率分析必须两条曲线都画,BSC看误码率绝对值随p下降的斜率,AWGN看Eb/N0随dB增加是否贴近理论界。
2.3 仿真的总体信号流:编码 → 调制 → 加噪声 → 判决 → 译码
在设计仿真代码之前把总流程图在脑内落一遍是最省事的做法:首先生成随机二进制信息比特(长度最好是k的整数倍,便于分块编码),然后reshape成11×M的矩阵,每一列是一个码块;编码后得到15×M矩阵,随后进入信道。BSC信道下直接抛硬币翻转;AWGN信道下映射成±1信号。接收端先完成硬判决或软信息提取,再按列做伴随式译码,纠错后取前11位,最后和原始信息比特做逐位对比,统计误码数并除以总比特数得到误码率。
仿真每个码块是当成独立事件来处理的,帧间没有关联性,所以不存在交织;但实际系统如果还要对抗成串突发错误,汉明码通常会配合交织器使用,这是仿真框架里不体现、链路设计里却很重要的后续扩展。参数设计上还必须考虑“足够多的错误事件”:BSC模型在p=1e-3、每个码块出错的概率约1.5%时,每次实验大概要跑上百万比特才会有可读的误码率数字;AWGN模型高信噪比下更是动辄几百上千万比特,单次蒙混过关的随机结果几乎无法画成平滑曲线。因此仿真里要持续统计当前已发生的误码总数,少于100个误码不能停下来,这是误码率仿真的通用兜底逻辑。
3. 在BSC信道下复现(15,11)汉明码:概率模型与伴随式译码实操
3.1 BSC仿真的核心代码:从比特翻转到译码查表
用MATLAB或Python都可以,这里给一套独立的Python流程,能在任何环境复现。第一步先建校正子查询表:遍历i=0到14,把第i个比特置为1构造错误图样,计算s = H·eᵀ,存到字典里。第二步生成信息比特,编码,然后按给定p逐位翻转。第三步算伴随式、查表、纠正错误、提取信息位,统计误码率。可运行的骨架如下:
import numpy as np # 生成矩阵G与校验矩阵H,系统码形式 H = np.array([[1,0,0,0,1,1,1,0,1,0,1,1,0,0,1], [0,1,0,0,1,0,1,1,1,1,0,0,1,0,1], [0,0,1,0,1,1,0,1,0,1,1,1,0,1,0], [0,0,0,1,0,1,1,1,1,0,1,1,1,0,1]], dtype=int) # 由P构造G = [I | P],这里示意:实际要按列展开 a=np.array([[1,1,1,0,1,0,1,1,0,0,1], [1,0,1,1,1,1,0,0,1,0,1], [1,1,0,1,0,1,1,1,0,1,0], [0,1,1,1,1,0,1,1,1,0,1]], dtype=int) G_sys = np.hstack((np.eye(11, dtype=int), a.T)) # 预生成(校正子 -> 错误位置)查找表,15种单比特错误 syndrome_table = {} for pos in range(15): e = np.zeros(15, dtype=int) e[pos] = 1 s = (H @ e) % 2 syndrome_table[tuple(s)] = pos def encode_bsc(block_bits): # block_bits: 长度为11的0/1数组 code = (G_sys @ block_bits) % 2 # 得到15比特码字 return code def decode_bsc(rx_word): rx_word = np.asarray(rx_word, dtype=int) s = (H @ rx_word) % 2 s_tuple = tuple(s) if s_tuple in syndrome_table: pos = syndrome_table[s_tuple] rx_word[pos] ^= 1 # 翻回正确比特 return rx_word[:11] # 取前11位信息位 # 单次仿真函数:p是BSC的比特翻转概率 def simulate_bsc(p, n_blocks=100000): total_err = 0 total_bits = 0 for _ in range(n_blocks): info = np.random.randint(0, 2, 11) codeword = encode_bsc(info) # 每个码字比特以概率p翻转 flip = np.random.rand(15) < p rx = codeword ^ flip dec = decode_bsc(rx) err = np.sum(dec != info) total_err += err total_bits += 11 return total_err / total_bits参数说明:n_blocks建议从10万到50万,p取0.01到0.1时曲线就能立起来;p低于0.001时每个码块出错概率约1.5%,需要把n_blocks放大到200万以上才不至于误码率曲线抖动。这里用映射查表代替解方程是必要的,因为每次译码做一次矩阵乘法+查表,总耗时很低;如果每个比特都现场计算线性方程组,仿真速度会掉一个数量级。另外这个实现是逐块编码,实际项目里信息比特是一股脑生成的,需要先reshape成(n_blocks, 11)矩阵,再把循环改成矩阵批量运算,两种写法正确率都不变,但性能差很多。要小心这里的随机种子没有固定,实验可比性不好,仿真时第一件事是np.random.seed(42)固定种子,否则两条曲线间看不出细微差异是噪声还是算法差异。
3.2 BSC误码率曲线怎么读:为什么低p时下降斜率反常
BSC译码后的误码率严格来说可以分为两部分:接收端没出错或只错1比特(能纠正),以及出现2比特及以上错误。后者进一步会导致错纠甚至直接加重错误。按组合数估算,没编码时误码率是p,加了汉明码后误码率可以粗略写为P_b ≈ C(15,2)·p²·(1-p)¹³ + 2·C(15,3)·p³…,也就是说曲线斜率从p的一次方变成p的二次方级别。这正是汉明码带来的本质收益:在高翻转概率下可能不如不编码,但p较低时“二次方”的下降弧线和不做编码的“一次方”直线拉开明显差距。
实测画图时,纵坐标用对数坐标,横坐标p也常用对数坐标。你会看到p=0.1时译码后误码率甚至略高于原始误码率——因为2比特错误太多,每纠一次错还有约1/15的概率纠成另一种错误;到p=0.01以下才有明显增益。这是汉明码“低冗余纠错”的固有特性,不是代码写错了。做BSC仿真最容易犯的错是拿“误字率”当“误码率”画图:BSC的每个码字里如果有2个错误,译码后可能错2个信息位,也可能错2个信息位+1个被错纠的位置,如果不逐位对信息比特统计而是按“块是否译对”統計,会导致曲线在低p区间的斜率夸大。这篇分析里所有误码率一律以“错误信息比特数/总信息比特数”为口径。
3.3 BSC下伴随式译码的边界:当错误数超过1时的“错纠”现象
理论框架内汉明码只能纠1位错,但仿真里一定会碰见2位以上的错误样本,此时系统并不会老实报告“无法纠正”,而是按伴随式去匹配一个不存在的单比特错误图样,选出一个“最像”的位置翻转。这意味着接收端的实际误码数可能比原始误码数更多。例如码字里第3位和第7位都翻了,伴随式s正好等于“第9位单独错”的校正子,译码器就会把第9位翻过去,最终错误比特变成3位。
这个现象在BSC误码率曲线上表现为“高p区域性能比原始还差”的最直接原因。做分析时要理解这不是译码bug,而是汉明码的数学边界。如果想让系统更安全一些,可以在译码后重新计算s,若s不为0就说明“纠错了但没纠干净”,此时可以标记为检测失败并丢弃该块——实际系统中这叫“纠错失败检测(detected but uncorrected)”,在ARQ协议里比静默错纠要友好得多。BSC仿真可以顺手统计这类“错纠次数”,它会告诉你系统到底有多少次“哑巴吃黄连”,对后面调编码方案有直接参考价值。
4. 切到AWGN信道:比特能量换算、软判决与硬判决交叉验证
4.1 AWGN链路参数换算:Es/N0、Eb/N0、码率R、噪声方差σ²是怎么缠在一起的
AWGN仿真里最容易出问题的不是信道本身,而是能量换算,这是误码率分析所有“玄学”里最典型的一处。假设BPSK调制,发送码字比特0映射为幅度-1,1映射为幅度+1,系统模型为y = x + n。此处的关键是“总符号能量Es”与“比特能量Eb”的差别:汉明码编码后15个码字比特带11个信息比特,码率R = k/n = 11/15,于是Es = (1/R)·Eb,即每个码字符号的能量高于信息比特能量,因为多了4个冗余比特。在仿真里噪声方差要按符号能量设置:常见写法是σ² = N0/2,Es/N0是符号信噪比。若用户习惯给的是Eb/N0(这是香农理论的标准横轴),需要先把Eb/N0换算为Es/N0再进行加噪声,公式是:
Es/N0(dB) = Eb/N0(dB) + 10*log10(R) = Eb/N0(dB) + 10*log10(11/15)换算好之后,令信号功率为1(即±1的幅度平均功率),噪声方差σ² = 1 / (2 * Es/N0)。若直接把Eb/N0当作Es/N0使用,等效于在计算中多送了约1.34倍的信号能量,曲线会往左偏1.3dB左右,这个偏差在手调方案的置信区间里很容易被忽略,却足够让外包团队交付的仿真图被误判。代码里建议先用dB值计算线性比值再换算,避免“dB与线性傻傻分不清”的问题。
4.2 硬判决AWGN仿真:从连续值到BSC模型的衔接
硬判决AWGN仿真是最直观的做法,接收机拿到y后做sign(y)得到0/1,之后完全复用BSC下的伴随式译码查表程序。这意味着只要你知道AWGN某Eb/N0下的等效翻转概率p,BSC模型的结果就能平移到AWGN上去,表达式为p = Q(sqrt(2 * Eb/N0)),Q是高斯尾函数。这条公式背后的物理含义是:BPSK硬判决后误比特率等于噪声超过信号幅度的概率,只要信噪比定了p就定了。因此很多教材把AWGN硬判决链路的分析收敛到“先算p,再套BSC组合数”,这个思路在实践里同样适用。
仿真代码和BSC共享函数,只把信道替换为:
def simulate_awgn_hard(eb_n0_db, n_blocks=100000): R = 11/15 es_n0_lin = 10 ** ((eb_n0_db + 10*np.log10(R)) / 10) sigma = np.sqrt(1 / (2 * es_n0_lin)) # BPSK, 信号功率=1 total_err = 0 total_bits = 0 for _ in range(n_blocks): info = np.random.randint(0, 2, 11) codeword = encode_bsc(info) x = 2 * codeword - 1 # 0->-1, 1->+1 n = np.random.normal(0, sigma, 15) y = x + n rx_hard = (y > 0).astype(int) dec = decode_bsc(rx_hard) total_err += np.sum(dec != info) total_bits += 11 return total_err / total_bits这里有一个常见扣分点:错误地把sigma写成np.sqrt(N0/2)而忘了信号功率1,实际上sigma就是噪声标准差,信号功率和方差在公式里必须同时保持一致。许多教材里把“信息比特能量”直接当作信号能量,导致方差公式里多乘一个R,仿真出来的曲线偏移1dB+。另一个坑是硬判决之后再用汉明码查表,相当于“先做了非线性量化,再做离散纠错”,这在信息论上不是最优的,因为量化过程丢掉了多数可信度信息。
4.3 软判决译码:为什么同样Eb/N0下性能更好,代码怎么写
如果只做硬判决,你会发现(15,11)汉明码在AWGN信道里的表现并没有比理论p=Q(sqrt(2*Eb/N0))好太多,只是高信噪比有约1.5dB的编码增益。软判决技巧则能把增益再多挤出1~2dB。原理很简单:接收端y的符号是硬判决结果,但y的绝对值|y|正比于“这个比特是否接近门限”,噪声小时这个比特可信度高、噪声大时可信度低。汉明码的伴随式译码本质上是“在码字集合里找一个码字,使得它和接收序列的汉明距离最小”,硬判决已经把所有模拟量压成0/1,软判决则改为在所有码字中挑选“欧氏距离最小”者,或等价地找校正子后翻幅度绝对值最小的位置。
实现上做“软判决伴随式”并不复杂:先对y做硬判决得到0/1序列,再算伴随式;若伴随式非零,说明有错,但现在不直接查表翻转,而是列出所有能和当前伴随式匹配的候选错误位置(可以从错误图样表里找),然后选|y|最小的那个位置翻转。这一步本质上是“最大似然(ML)译码的化简版”。需要注意的是y已经是±1加噪后的连续值,直接取绝对值作为软信息即可。对应的核心改动如下:
def decode_awgn_soft(y_rx): rx_hard = (y_rx > 0).astype(int) s = (H @ rx_hard) % 2 if np.all(s == 0): return rx_hard[:11] # 找到所有伴随式等于s的单比特错误位置 candidates = [] for pos in range(15): e = np.zeros(15, dtype=int) e[pos] = 1 if tuple((H @ e) % 2) == tuple(s): candidates.append(pos) # 选择|y|最小的位置翻转(最不可信的比特) flip_pos = min(candidates, key=lambda pos: abs(y_rx[pos])) rx_hard[flip_pos] ^= 1 return rx_hard[:11]参数说明:这个实现里candidates里一定至少有一个位置,因为伴随式表是完备的单比特错误映射;若某些伴随式对应多个错误位置,说明码的覆盖有冗余。选最小|y|的本质就是最大似然思路,在噪声方差一致时等价于最小欧氏距离。实践里要注意保留原始y的浮点数组,不要在硬判决后把y覆盖掉。软判决的代价是复杂度略微增加,但每个码块只是算一次伴随式+一次最小值遍历,和硬判决相比几乎没有时间差,因此实际链路仿真里应默认用软判决,除非目标本身就是验证BSC模型。
4.4 理论下界与实测曲线对比:lnkd用什么公式画参考线
AWGN信道下硬判决BPSK+汉明码的误码率上限有两条现成的参考线可以画:一条是没有编码的BPSK误码率P_b = Q(sqrt(2*Eb/N0)),另一条是用汉明码“纠1位错”后,把BSC等效p代入前面写过的组合公式。若用软判决,理论界需要转为联合界(union bound),把所有码字对的佩斯里特上界加总,这对汉明码来说可以算出近似准确的上界公式:
P_b ≤ (15·Q(sqrt(2·d_min·Es/N0)) + 其他码距加权项...) # 这里Es/N0要代换算后的值日常验证时更实用的做法是不去抠联合界的解析式,把“软判译码后误码率”和“硬判决BSC模型预测”并列画在同一张图上,如果软判决曲线比硬判决曲线在10⁻⁴级别好上1dB左右,基本可以说明实现没写错。一个额外小技巧:把仿真的Eb/N0范围设在0到8dB之间,步进0.5dB,在这个区间里汉明码的增益从负转正,曲线最有辨识度,也最能看出模型换算是否到位。
5. 仿真避坑指南:从能量换算到统计门限的五个典型翻车现场
5.1 符号能量与比特能量混淆,曲线整体偏移1.3dB
现象:仿真出来的AWGN曲线和理论曲线不重合,低信噪比区看起来还好,高信噪比时总是差1.2~1.5dB,有时候偏左有时候偏右。
原因:把Eb/N0直接当作Es/N0使用了,没有乘以码率R=11/15。BPSK符号承载的其实是码字比特,总能量里包含冗余部分,如果按信息比特能量去设定噪声幅度,体系就不自洽。
解决:加噪声前先统一换算es_n0_db = eb_n0_db + 10np.log10(11/15),再按sigma = np.sqrt(1/(2es_n0_lin))生成噪声。这条检查要在代码最前面完成,后面所有曲线才不会出系统性偏差。判断方法很简单:把Eb/N0=0dB时的仿真点单独打印出来,和理论Q(sqrt(2*Eb/N0))对比,若相差超过0.2dB就是换算有问题。
5.2 硬判决AWGN直接套BSC理论时忘记用Q函数折算等效p
现象:在AWGN仿真里,把Eb/N0换算成比特翻转概率p时,直接取p=1/erfc或p=0.5,画出来的误码率曲线在低信噪比下甚至高于0.5,完全没法看。
原因:混淆了“噪声方差”和“比特翻转概率”的概念。AWGN的噪声是连续的,只有经过判决后才会变成离散翻转概率;Eb/N0和p之间必须用Q函数连接,且Q函数里的参数要乘以2,即sqrt(2*Eb/N0),而不是sqrt(Eb/N0)。
解决:用程序中计算p_hat = 0.5 * np.erfc(np.sqrt(eb_n0_lin)),然后拿这个p去套BSC的误码率理论公式。检查时主要看p在Eb/N0=0dB时是否约为0.08(0.5*erfc(1.0)),不在这个量级就是公式抄错。
5.3 误码总数太少导致曲线尾部剧烈抖动
现象:Eb/N0=7dB时,跑了20万个码块,误码率大约只有1e-5量级,整条曲线在高信噪比区呈锯齿状,甚至相邻两个SNR点的误码率倒挂。
原因:高信噪比下每个码块出错的概率极低,总误码数可能只有个位数,统计置信区间极宽,偶然起伏完全压过真实趋势。
解决:仿真循环加一个停止条件——累计误码数达到至少100个再输出误码率;或者先估算需要多少码块,按“目标误码率倒数”的100倍来预估。固定随机种子的前提下,还可以把多个独立种子的结果平均,这能把尾部曲线磨平很多。这是误码率仿真最容易被“赶进度”牺牲掉的一环,实际上反而是最不能省的统计量。
5.4 用随机种子调参调出的“完美曲线”其实不可复现
现象:同一个参数集,跑两次结果有差异;为了论文/汇报把随机种子反复调到好看,最后交付时别人一跑就对不上。
原因:所有用“随机数”的仿真,如果不固定种子或采用均值统计,天然存在方差。这不算bug但确实是工程上令人头疼的事,尤其当你想比较“加了码”和“没加码”两条曲线时,噪声差异可能把真实增益淹没。
解决:写代码时把所有随机生成器显式设置种子,并把种子放在配置参数区;对比实验用相同种子序列。若条件允许,用“蒙特卡洛重复M次取平均”而不是一次性加大码块数,这样还能顺便给出置信区间,汇报时更有说服力。
5.5 把“编码后误码率”错当成“信息误码率”上报
现象:译码器输出的错误比特数看起来是15bit粒度统计的,和手算的理论曲线对比时总对不上,尤其在高信噪比区域。
原因:误码率定义口径不同。有人统计“译码后15位码字里错误比特比例”,有人统计“恢复的11位信息比特比例”,两者理论值相差约11/15倍(因为码字里冗余错不影响信息)。若不统一,所有比较都没有意义。
解决:在统计函数里明确返回total_err / total_bits,其中total_bits = n_blocks * 11,而不是n_blocks * 15。把“码字错误率(CER)”和“信息误码率(BER)”分开打印,汇报时说明是按信息比特统计的。这一条看似简单,却是最容易在多人协作仿真框架里被悄悄改掉的。
6. 验证与进阶:用联合界、置信区间和缩短码把分析做扎实
6.1 先画“无编码BPSK”曲线做基准线
拿到任何汉明码仿真图,第一件事不是看汉明码的曲线,而是把无编码BPSK理论线画在同一张图上:P_b = Q(sqrt(2Eb/N0))。这有双重意义:一是在Eb/N0=0~8dB区间,它能直观看出汉明码的编码增益到底在哪一段转为正值;二是它同时校验了噪声模型与能量换算的正确性——如果无编码曲线都画不准,后面所有结论都无效。常见做法是用解析公式P_b = 0.5erfc(sqrt(eb_n0_lin)),不仿真直接画,省事且严谨。
6.2 用联合界验证软判决译码是否实现正确
软判决译码没有BSC那样现成的精确理论公式,但可以用“成对错误概率的联合界”来做参考上限。汉明码有15个码字比特,码距分布和重量分布可以预先算好(各码重数量可查表),误码率上界不超过所有码字对中“接收序列被判到错误码字”概率的加权和。写成程序只需几行:遍历重量w的码字数A_w,P_b粗略上界=Σ (w/15)·A_w·Q(sqrt(2wEs/N0))。这个上界在高信噪比区(Eb/N0>6dB)会紧贴实际软判决曲线,差距超过0.5dB说明译码器实现有偏差。
“联合界在低信噪比时过于松散”,因为多个码字项叠加后可能超过1,但没关系,仿真曲线低于上界才算符合预期;如果仿真曲线比联合界还高,几乎可以断定软判决里“取最小|y|”的逻辑写错了或H矩阵没对齐。
6.3 误码率仿真还要配“置信区间”,不然汇报少一张图
最后一个进阶技巧是把置信区间画出来,让听汇报的人一眼看到曲线之间的差别是否在统计误差内。工程上最常用的做法不是直接算方差,而是把仿真拆成B批(比如20批),每批独立跑相同码块数,得到B个误码率样本,再按批间标准差画误差棒。这样做的优点是:第5章说的“随机抖动”直接就可视化了,比单条曲线说服力强很多。无编码的对照同样适用。展示时如果两个方案之间隔着一个误差棒的距离,那就是真差异;如果误差棒互相重叠,说明在这个信噪比段两者没有统计意义上的优劣。
6.4 想继续往下走:缩短码、扩展汉明码和迭代译码的关联
(15,11)汉明码只是个起点,仿真框架建好后可以顺手扩展:想适配更短的载荷,可以把码字截短成“缩短汉明码”,比如(12,8),只传输后12个码字比特位置的前面部分,其余位置填充0;这样码率改变但H矩阵的线性关系保持。需要更强纠错时可以在汉明码基础上级联一个奇偶校验位变成扩展汉明码(16,11),d_min升到4,能纠1错并检2错,这在ARQ场景里非常实用。软输出方向还可以继续升级到BCJR或置信传播迭代译码,但汉明码的短码长性质决定了迭代收益有限。 我做这套误码率分析时养成的一个习惯是:把“理论曲线”“仿真点”“置信区间误差棒”三者放在同一张图之后再往外发,任何一次曲线对不上理论都会立刻暴露,而不是等到审查才出事。遇到一时对不上的偏差,先查噪声方差换算,再查统计口径,最后查随机种子,这套排查顺序能救回至少半小时的无用功。希望某天你被问到“仿真曲线为什么偏了1.3dB”时,第一反应想起这篇笔记里的Es/N0换算——那就值了。
本文还有配套的精品资源,点击获取