M序列、Gold序列与Walsh序列的MATLAB仿真及误码率对比 简介M序列、Gold序列与Walsh序列是扩频通信与CDMA系统中的三类典型扩频码其生成方式与相关特性直接决定了多址接入系统的抗干扰能力和用户容量。这套MATLAB仿真资源面向通信工程专业学生、算法工程师及系统设计人员可用于理解序列原理并评估其在数字通信系统中的性能表现。资源包内包含2个文件——1个MATLAB主脚本main.m与1份说明文档README.md压缩包大小约4KB结构简洁、便于快速部署目前已有156人学习使用。通过运行脚本可依次生成上述三种序列完成自相关与互相关特性可视化并进一步仿真信噪比与误码率曲线对比不同序列在相同信道条件下的抗噪声能力资源还支持用户修改序列长度、信道模型等参数以探索不同配置下的系统性能对于从事CDMA、蜂窝网络、卫星通信等方向研究的读者这是一套轻量而完整的实验框架可作为序列选型与系统优化的起点。1. 数字通信性能仿真为什么绕不开M序列、Gold序列和Walsh序列在通信原理的课后题里M序列、Gold序列、Walsh序列都只是书上的生成多项式和表格可一旦要交付一个能跑的MATLAB仿真很多人连Gold序列的优选对该选哪一组都说不清更别说把三种序列丢进同一个数字通信系统里比误码率。这篇笔记用一套完整的MATLAB脚本把三种扩频序列从生成、自相关/互相关验证一直做到BPSK直扩系统的误码率曲线。跑通之后你会看到Walsh的正交性在同步多用户场景里有多强势Gold如何在接近正交的同时保持足够多的序列数量M序列的优势又在哪里。适合刚做完通信原理课程设计、正在搭数字通信系统仿真的同学也适合需要用仿真结果对比扩频码性能的工程师。2. 从生成原理到MATLAB实现三种扩频序列的代码与性质验证2.1 M序列线性反馈移位寄存器与最小生成脚本M序列是最长线性反馈移位寄存器序列周期为2^n-1其中n是寄存器阶数。它的自相关函数接近理想冲激旁瓣为-1/n这一性质让它在单用户扩频系统里成为天然的伪随机码。生成M序列的硬件逻辑很简单把若干寄存器输出做异或反馈到第一级其余寄存器右移一位。下面是一个不依赖任何工具箱的纯MATLAB生成函数function seq m_seq(n, taps, init) % n移位寄存器阶数序列周期为 2^n - 1 % taps反馈抽头位置元素取值 1..n对应参与异或的寄存器 % init1×n 的初始状态向量不能全为 0 len 2^n - 1; reg init(:); seq zeros(1, len); for i 1:len seq(i) reg(end); % 取最后一级输出 fb mod(sum(reg(taps)), 2); % 抽头异或得到反馈位 reg [fb, reg(1:end-1)]; % 反馈位进入第一级整体右移 end seq seq * 2 - 1; % 0/1 映射为 ±1 end这段代码把移位寄存器建模为行向量。每次循环先输出最后一级再对指定抽头取模2和作为新的反馈位然后右移。关键参数是taps它直接决定序列是不是M序列只有本原多项式对应的抽头组合才能让寄存器穷尽所有非全零状态序列周期才达到2^n-1。例如n6时抽头[6 1]对应本原多项式x^6x1如果用[6 2]这种非本原抽头序列周期会缩短自相关也不纯。初始化init不能全0否则反馈一直为0序列永远全0。我一般习惯把最高位置1即[1 0 0 0 0 0]。生成后用的值域是±1方便后续直接做BPSK乘法。如果你在工具箱环境里也可以直接用comm.PNSequence但自己写这个函数能直观看到M序列的本质调参数也方便。2.2 Gold序列从优选对到MATLAB实现Gold序列是两条M序列逐码片异或得到的长度与M序列相同。它的价值在于数量多n阶M序列只有有限几条本原多项式而Gold序列一族可以有2^n1条并且任意两条的互相关峰被限制在一个可接受范围内。代价是自相关旁瓣略差于M序列。需要强调这里的M序列对必须是优选对否则生成序列的互相关特性没有理论保证。手动生成Gold序列时最稳的做法是复用上面的m_seq函数function gold gold_seq_by_m(n, taps1, taps2, init1, init2) m1 m_seq(n, taps1, init1); m2 m_seq(n, taps2, init2); gold m1 .* m2; % 逐码片相乘等价于模2加后的±1映射 end两条M序列逐位相乘对应二进制里的异或。这个函数的可靠性取决于taps1和taps2是否构成优选对。手动验证优选对很繁琐所以我在真实项目里更常直接用通信工具箱的comm.GoldSequencegoldGen comm.GoldSequence(... FirstPolynomial, [6 1 0], ... SecondPolynomial, [6 5 2 1 0], ... FirstInitialConditions, [1 0 0 0 0 0], ... SecondInitialConditions, [1 0 0 0 0 0], ... IndexOutput, true); goldCode goldGen(); goldCode double(goldCode(:).); goldCode goldCode * 2 - 1;这里[6 1 0]表示x^6x1[6 5 2 1 0]表示x^6x^5x^2x1是一组文档里常见的优选对。工具箱会自己校验多项式本原性参数错了会直接报错比手工检查省心。需要注意的是输出类型comm.GoldSequence返回的默认是0/1逻辑值我要先转成double再做0→1、1→-1映射保证后续与BPSK信号相乘时值域一致。如果不想用工具箱还有一个折中办法取两个不同抽头的M序列直接异或虽然不能保证是最优Gold序列但在性能趋势演示中能看到多址干扰明显低于单条M序列。正式的课程设计或论文建议还是用comm.GoldSequence输出参数明确复现性更好。2.3 Walsh序列Hadamard矩阵与正交性表Walsh序列来自Hadamard矩阵行与行之间严格正交。长度必须是2的幂例如32、64、128。MATLAB里的hadamard函数可以直接生成function [H, seq] walsh_seq(L, idx) % LHadamard矩阵阶数建议取 2 的幂如 32、64 % idx取第几行作为扩频码范围 1..L H hadamard(L); seq H(idx, :); endHadamard矩阵的生成规则是递归的2L阶矩阵等于[L L; L -L]的块结构。因此每一行都是±1任意两行内积为0。这个正交性在数学上很干净但工程上有代价Walsh序列之间的互相关是理想0前提是所有用户的信号严格同步到码片级一旦出现时间偏差正交性会迅速恶化。另外Walsh序列的频谱不平坦自相关旁瓣也不如M序列这在某些需要抗多径的系统里是短板。到这里三种序列的生成代码都有了。下一步应该验证它们的性质计算自相关和互相关用xcorr画归一化曲线。[acor, lags] xcorr(mCode, normalized); figure; stem(lags, acor); xlabel(延迟); ylabel(归一化自相关); title(M序列自相关);M序列的自相关主峰在0处为1旁瓣接近-1/n。Gold序列的互相关峰值被限定在固定范围而Walsh序列的互相关在完全同步时理论为0。这个验证步骤不是走过场它能提前暴露序列生成参数错误的问题比直接进系统找bug快得多。3. 搭建数字通信系统仿真链路BPSK直扩、相关解扩与误码率统计3.1 仿真链路模型与扩频增益设定数字通信系统仿真的核心目标不是把通信过程忠实还原一遍而是用一种可复现的方式回答“扩频码A比B好多少”。最常用来回答这个问题的链路是BPSK直扩系统每个信息比特先乘一个扩频序列把比特速率扩展到码片速率再经过AWGN信道接收端用同步的本地扩频码做相关解扩积分判决。发射端表达式很简单信息比特 b ∈ {0,1}映射为 a 2b-1 ∈ {-1,1}发射信号 s a * c其中 c 是长度为 L 的扩频码字值为±1接收信号 r s nn 是零均值高斯白噪声接收端相关判决变量 y sum(r .* c)。因为 c 的每个元素平方为1所以信号分量是 aL噪声分量的方差是 Lσ²。可以看到信噪比和扩频码内容无关只和长度L有关这正是单用户AWGN信道上所有扩频码误码率相同的理论原因。因此仿真参数的重点不是选哪种码而是如何设置信噪比和比特能量。MATLAB里常见误用是把码片信噪比当成比特信噪比导致BER曲线整体右移。正确做法是用比特能量Eb除以噪声功率谱密度N0。对±1扩频码每个信息比特发射能量Eb sum(c.^2) L然后根据Eb/N0反推噪声方差。% 常用参数 nBits 20000; % 统计误码率需要的比特数 EbN0dB -10:2:0; % 低信噪比区间更适合观察扩频增益 codeLength 64; % 扩频码长度 Eb codeLength; % 每个比特信号能量 码片能量之和这里对M序列和Gold序列用63对Walsh用64虽然L不同但Eb/N0都是按各自Eb计算的所以比较是公平的。很多教材为了省事让三种码长严格相同实际上没有必要只要你按Eb/N0而不是码片SNR建模即可。3.2 MATLAB主循环与BER统计代码下面的代码用向量化方式完成整个蒙特卡洛统计一次仿真nBits比特不再嵌套逐比特循环速度能快两个数量级。% 假设已经通过 2.1/2.2/2.3 生成 code长度为 L值为±1 L length(code); Eb sum(code.^2); ber zeros(size(EbN0dB)); for idx 1:length(EbN0dB) EbN0 10^(EbN0dB(idx) / 10); N0 Eb / EbN0; sigma sqrt(N0 / 2); % 实AWGN噪声标准差 bits randi([0 1], nBits, 1); tx (2*bits - 1) * code; % nBits×L 扩频后信号 noise sigma * randn(nBits, L); rx tx noise; y rx * code.; % 相关解扩 bitsHat y 0; % 判决门限0 ber(idx) sum(bitsHat ~ bits) / nBits; end这段代码的关键在tx (2*bits - 1) * code左边是列向量code是行向量外积得到矩阵每一行是一个信息比特扩频后的完整信号。rx * code.做的是矩阵乘以码向量等于对每一行做内积一次性完成nBits次相关解扩。MATLAB的矩阵乘在这里非常高效比我以前写的内层for循环快了很多。噪声标准差为什么是sqrt(N0/2)因为对实信号噪声的双边功率谱密度是N0/2高斯噪声方差等于N0/2。如果你仿真复基带信号噪声标准差应该设为sqrt(N0)不要搞混。另外nBits取20000在目标误码率10⁻³量级时够用如果想看10⁻⁴以下的误码率需要把nBits提到5e5或更多否则抖动会很大。3.3 参数设置说明Eb/N0、码片数和统计量三个参数最容易出错Eb/N0的参考点、扩频码长度、蒙特卡洛次数。Eb/N0必须定义在信息比特上而不是扩频码片。如果你调用的berawgn函数它的EbN0参数也是比特级可以直接对齐。扩频码长度决定处理增益但在单用户AWGN下不影响BER理论值它影响的是仿真带宽和序列数量。蒙特卡洛次数决定了BER曲线的置信度至少要保证误码个数大于100否则统计结果波动大。我一般会先用理论BER曲线把仿真链路校准一遍再跑三种序列的对比。校准方法很简单把扩频码改成固定全1序列此时系统退化为普通BPSK仿真BER应该紧贴qfunc(sqrt(2*EbN0))。如果对不齐问题多半在噪声方差或信号能量定义上不要在三种序列上浪费时间。4. 性能对比单用户AWGN、多用户干扰下的误码率曲线与选型结论4.1 单用户AWGN信道下的BER曲线与理论值运行第三章的仿真把M序列、Gold序列、Walsh序列分别填入code得到三条误码率曲线。在单用户AWGN信道下理论上三条曲线完全重合都等于BPSK理论值。很多新手看到这里会疑惑既然都一样为什么还要研究序列答案是扩频码的差异要到多用户或抗干扰场景才显现单用户AWGN只是验证链路正确性的第一步。为了直观我把三种序列放在同一张图里看差异semilogy(EbN0dB, berM, -o, EbN0dB, berGold, -s, EbN0dB, berWalsh, -^); grid on; xlabel(Eb/N0 (dB)); ylabel(误码率);如果三条线在高Eb/N0区域因为误码个数少而抖动分离这正常。解决办法是增加nBits或者做多次独立实验取平均。不要为了追求完美重合而把Eb/N0范围设窄仿真的意义在于趋势不是单点精度。4.2 多用户干扰场景Walsh为何占优、Gold为何够用多用户场景是这三种序列真正分高下的地方。假设小区内有K个用户同步发送每个用户分配不同扩频码接收端解扩期望用户时其他用户的信号会贡献一项互相关干扰。干扰大小取决于两个码字的归一化互相关绝对值。Walsh序列完全正交同步时多址干扰理论上为零Gold序列互相关峰受限会有一定多址干扰M序列互相关不可控可能出现较大干扰峰。为了看到这个现象可以在仿真里叠加干扰用户。具体做法是生成K个扩频码发送期望用户信号和K-1个干扰用户信号干扰用户功率通常与期望用户相同。% 简化版多用户干扰仿真 users 8; % 总用户数 codeSet walsh_code_set(64, users); % 取hadamard矩阵前users行 desiredIdx 1; tx (2*bits - 1) * codeSet(desiredIdx, :); for k 2:users dataK 2*randi([0 1], nBits, 1) - 1; tx tx dataK * codeSet(k, :); % 干扰用户等功率 end rx tx sigma * randn(nBits, L); y rx * codeSet(desiredIdx, :).;在完全同步的条件下Walsh的解扩结果里只有噪声没有多址干扰误码率仍是单用户理论值。Gold序列会有一个小的误码平台M序列则可能出现明显平台平台高度与互相关峰值有关。这个仿真结论在直扩CDMA系统里是选码的核心依据Walsh适合同步下行链路Gold适合异步或准同步系统M序列则更多用在单点对点或需要数量较少的场景。4.3 序列数量、同步要求与应用选型表除了误码率选型还要看序列数量和同步容限。下面的表是我做课程设计时常用的一张决策表指标M序列Gold序列Walsh序列序列长度2^n-12^n-12的幂序列数量取决于本原多项式个数2^n1量级等于阶数L自相关旁瓣低接近-1/n中旁瓣稍高偏高互相关同步性不可控峰值受限完全正交异步能力尚可较好差典型场景单用户扩频、测距CDMA移动通信同步多用户、OFDM?这张表能直接回答“什么时候用哪种序列”。如果你的仿真里用户严格同步Walsh是最优解如果用户时间不同步或系统有随机接入Gold是稳妥选择如果只要求单链路扩频M序列的自相关优势反而是最重要的。5. 仿真中常见的5个翻车点现象、原因与解决5.1 翻车点一序列长度不一致导致相关峰错位现象M序列长度为63Walsh长度为64直接把两组序列放在同一个接收机里做相关解扩误码率在低信噪比时都还正常到了高信噪比反而出现地板或者相关峰幅度对不上。原因扩频码长度不同比特能量和噪声积累项都不同。如果误把码片SNR当成Eb/N0或者解扩时没有对齐到各自码长相关值就会差一个比例系数导致判决门限偏移。解决不要强行把63补一位成64那会破坏序列性质。正确做法是各自统计误码率用比特级Eb/N0作为横坐标然后在同一张图里比较。长度不同只影响带宽和扩频增益不影响BPSK理论BER。5.2 翻车点二Walsh序列直流偏置造成载波泄漏现象把Walsh序列映射成±1后均值并不为0。某一行可能正负有偏导致发射信号在星座图上有一个固定直流分量仿真频谱出现明显载波泄漏单用户BER却看不出问题。原因Hadamard矩阵的行并不保证均值都为0只有部分行是平衡的。实际射频系统不允许带直流分量仿真如果只看BER会忽略这个问题。解决在发射前做去直流处理或者在多用户分配时避开均值不为0的行。更常见的做法是明确你的仿真是基带等效仿真不考虑射频载波泄漏只在论文里说明这个前提。如果做带通仿真务必检查每条Walsh码的均值。5.3 翻车点三Gold序列优选对选错后互相关异常现象用两条任意M序列异或生成“Gold序列”多用户干扰仿真中出现很高的互相关峰误码平台比理论预期高出很多曲线无法收敛。原因Gold序列有严格的优选对约束。很多教材例子给的抽头组合只适合生成普通M序列并不保证互相关最优。我最初学的时候也直接用两个M序列拼后来才发现优选对的意义。解决优先使用comm.GoldSequence让工具箱校验本原多项式和优选对。如果版本没有这个对象就查Gold序列优选对表不要凭感觉选抽头。调试时可以打印两条序列的互相关如果峰值大于理论值基本可以断定优选择不了。5.4 翻车点四误码率在低误码区抖动现象仿真BER在高Eb/N0时像心电图一样上下跳误码率在1e-4和1e-5之间反复横跳无法得到平滑曲线。原因误码个数太少。例如你统计1万比特需要至少100个误码才能让相对误差小于10%对应误码率1e-2想估1e-5至少需要几百万比特。解决按目标误码率设置nBits经验值是误码个数在100以上。如果算力不够可以把Eb/N0范围控制在10dB以内或者用berfading、berawgn理论值代替低误码区仿真。不要为了画图好看去平滑数据那会掩盖仿真实现的错误。5.5 翻车点五同步假设过强与真实接收机差距现象仿真里假设接收端本地扩频码和发送端完全同步码相位准确知道结果BER很好实际系统里码同步需要捕获和跟踪稍有偏移性能就崩盘。原因MATLAB仿真链路为了聚焦扩频码性能省略了同步过程这本身合理。但如果论文里只写“理想同步”读者容易误以为实际也能达到这个性能。解决在仿真代码注释里明确标注“理想同步假设”并额外跑一组带定时偏差的仿真作为对照。对于Walsh序列尤其要测试1/2码片偏差下的性能劣化这往往是它的致命短板。6. 进阶用并行蒙特卡洛与理论曲线压榨仿真可靠性想快速得到平滑的BER曲线可以引入两个技巧。第一个是用parfor替换普通for循环前提是每个循环内部随机数生成相互独立且不在工作区大量累积数据。把Eb/N0分成小块每块用不同随机种子独立仿真最后拼接结果。parfor idx 1:length(EbN0dB) rng(idx 100); % 每个分支不同种子保证可复现 % 复制单点EbN0仿真代码 % 返回该点的ber值 endparfor不是免费午餐。如果你直接把第三章的向量化代码塞进去每个worker都要生成完整码集和噪声矩阵内存会翻倍。更合适的做法是一个worker负责多个Eb/N0点每个点内部用矩阵化计算这样并行开销小速度提升明显。第二个技巧是用置信区间校验误码率。BER估计服从二项分布误码率p的方差约为p(1-p)/N。当我看到两次仿真结果差一倍时第一反应不是改代码而是算一下95%置信区间。比如统计10万比特得到100个误码p1e-3标准差约1e-4区间已经能接受如果统计1万比特得到10个误码p1e-3标准差约3e-4区间大得没意义这时候加大仿真量比调参有意义得多。我自己的习惯是每次跑完仿真先画理论BPSK曲线叠上去。如果扩频码在单用户场景里偏离理论曲线超过0.5dB我基本不会看多用户结果因为链路建模一定有问题。理论曲线不仅验证正确性还能帮你判断噪声方差定义是否合理。等单用户完美贴合理论后再放心做多用户和序列对比这样得出来的结论才敢写进报告。三套序列的仿真跑通之后你会发现真正耗时间的不是序列生成而是对“为什么性能一样”和“为什么性能不一样”的解释。希望这些参数细节和踩坑记录能帮你少走几段弯路。本文还有配套的精品资源点击获取