首页 / 资讯中心 / 文章详情

直接序列扩频的Simulink仿真:扩频增益、PN码与误码率避坑指南

直接序列扩频的Simulink仿真:扩频增益、PN码与误码率避坑指南 ★ FEATURED ARTICLE
简介一套基于Simulink搭建的直接序列扩频DSSS通信系统仿真资源面向通信工程学生与初入无线通信的研发人员用于理解扩频通信原理、伪随机码生成、调制解调及噪声信道下的性能表现。压缩包内含13个文件以.m脚本、.mat数据文件、.mdl与.slx模型及配套doc说明为主模型与脚本配合可完整呈现从信源编码、扩频调制、10dB信噪比信道传输到解扩解码的仿真流程。资源整体仅144KB结构精炼适合快速复现与二次修改。已有759人学习下载实用性获一定认可。通过运行主要程序并对照说明文档读者可查看时域波形与频谱分析深入理解DSSS抗干扰与保密特性并掌握Simulink通信仿真建模的基本方法和参数调整思路。1. 直接序列扩频仿真到底在仿什么一个工程视角的速写做通信系统预研的工程师十有八九都栽过同一个跟头理论误码率曲线画得漂漂亮亮仿真一跑结果在低信噪比区域差了好几个dB。直接序列扩频通信仿真就是用Simulink把发射、扩频、调制、信道、解扩、解调这条链路完整搭出来专门用来回答“扩频增益到底带来了多少抗干扰余量”“PN码同步做没做对”这类问题。它适合课程设计、毕业课题、以及产品预研阶段拿不准方案时先用模型验证的人。我见过太多人只盯着信噪比和误码率却忽略了扩频码同步、采样率匹配这些真正的黑匣子。这篇笔记我会从原理到建模参数把坑都摊开讲。2. 直接序列扩频的数学基础与Simulink建模选型从扩频增益到PN码2.1 扩频增益怎么算为什么仿真里必须盯住它直接序列扩频的核心是用一个高速率的伪随机序列去调制低速率的原始数据把信号带宽展宽。接收端用同一个PN码做相关解扩把宽带信号重新压回窄带同时把干扰和噪声的能量分散掉。这个过程中最关键的数字是处理增益也叫扩频增益公式很简单Gp 10 * log10(Rc / Rb)其中Rc是码片速率Rb是信息速率。比如信息速率9.6kbps码片速率9.6Mcps扩频增益就是30dB。这个增益意味着即使信道信噪比是负的只要接收端能完成同步和解扩系统仍然能工作。仿真的意义就是把这条链路跑通验证这个理论上限能不能真的达到。但仿真里有个容易掉的坑Simulink里的信噪比定义和扩频后的带宽要一致。很多人直接在AWGN信道模块里填一个“SNR”却没搞清楚这个SNR是相对于信息带宽还是码片带宽的。扩频后信号带宽展宽了噪声功率也跟着带宽走。正确做法是先用带宽归一化算Es/N0或者Eb/N0再换算成信道模块需要的SNR。我一般先算清楚处理增益再设定噪声功率否则误码率曲线会整体平移几个dB让人误以为同步算法有问题。2.2 用Simulink还是手写m脚本这个场景我为什么选Simulink纯写m脚本做直接序列扩频仿真当然可行但工程落地时我几乎总是选Simulink。原因不是Simulink算得快而是它能让你把时频域信号流动看得明明白白。扩频通信仿真最复杂的是同步PN码的相位对齐、载波同步、定时恢复这些在Simulink里可以用Scope和星座图直接观察中间信号排错效率比m脚本高得不是一点半点。Simulink里做直接序列扩频仿真常用的工具箱是Communications Toolbox和DSP System Toolbox。这些工具箱提供了现成的PN Sequence Generator、AWGN Channel、Rectangular QPSK Modulator等模块不需要自己写底层算法。如果要做成产品级还可以用Simulink Model的C代码生成把模型直接落到嵌入式平台。这个流程后面我会单独讲。这里先说明选型理由如果你的目标是快速验证算法Simulink的模块化拖拽比手写代码快如果你要把模型跑在实时硬件上Simulink的代码生成也比手写C要省去不少移植工作量。3. 在Simulink里搭建直接序列扩频通信链路从发射到接收的完整步骤3.1 发射端模型数据源、扩频、调制与脉冲整形搭建模型的第一步是从Simulink库浏览器里把需要的模块拖到模型编辑区。我习惯把所有模块按信号流横向排列每段信号线上用Goto/From或直接标注变量名这样调试时一眼能看出断点。发射端我通常由这几个模块组成Bernoulli Binary Generator生成随机比特PN Sequence Generator生成扩频码一个乘法器做扩频然后是Rectangular QPSK Modulator做调制最后是Raised Cosine Transmit Filter做脉冲整形。这里有一个关键设计扩频和调制顺序不能错。先扩频再调制这是标准做法。如果先调制再扩频就成了跳频或者别的方案不是直接序列。扩频时每个信息比特与一段PN码逐码片相乘码片速率要高于信息速率。在Simulink里我一般用帧模式Frame-Based处理把每个比特扩展成一个包含Nc个码片的帧这样后续同步处理更容易写。下面是一段初始化发射端参数的m脚本它用set_param给模型里的模块赋值% 发射端参数定义 Rb 9600; % 信息速率单位 bps Nc 64; % 每比特扩频码片数处理增益 10*log10(64) 18.06dB Rc Rb * Nc; % 码片速率 614.4 kcps Fs Rc * 4; % 采样率取码片速率的4倍满足脉冲整形过采样 sps Fs / Rc; % 每个码片的采样点数 4 % 创建PN码生成器参数 pnGenPoly [7 6 1]; % 本原多项式 x^7 x^6 1周期 2^7-1 127 pnGenMask 7; % 反馈抽头掩码用于取其中一位 pnGenInitialStates [1 0 1 0 1 0 1]; % 初始状态不能全零 % 将这些参数写到Simulink模型中 set_param(dsss_tx/PN Sequence Generator, Polynomial, mat2str(pnGenPoly)); set_param(dsss_tx/PN Sequence Generator, Mask, mat2str(pnGenMask)); set_param(dsss_tx/PN Sequence Generator, InitialStates, mat2str(pnGenInitialStates));这段代码里pnGenPoly决定了PN码的周期和相关性质量。本原多项式必须查表选择不是随意定的。我用的是7阶周期127大于需要扩频的64码片所以每比特截取64个连续码片。注意初始状态不能全零否则m序列输出全零整个扩频就失效。代码里用mat2str把向量转为字符串是因为set_param接受的是字符串形式的参数这个细节我刚开始写的时候也卡了半天。3.2 信道与接收端解扩、解调与误码率统计接收端是仿真里最复杂的部分。信道我用AWGN Channel模块但参数不能直接填“SNR”而是要先在m脚本里根据Eb/N0换算。解调用的是Rectangular QPSK Demodulator解扩则是一个乘法器把接收信号与同步好的PN码相乘然后累加一个比特周期做相关判决。这里最大的难点是PN码同步——接收端必须知道PN码的起始相位。对于课程设计和算法验证简单做法是给接收端一个“假同步”直接用同一个PN Sequence Generator模块设置相同的初始状态再用一个延迟模块对齐相位。这不是工程方案但能先验证解扩和解调算法的正确性。后面我会讲如何用外部模式或代码生成做真正的同步捕获。接收端m脚本里我比较关心的是误码率统计。可以直接用Error Rate Calculation模块但要注意它需要三个输入接收比特、发射比特和使能信号。使能信号要在一个比特周期的整数倍上产生脉冲否则统计错位误码率会虚高。% 接收端参数设置 EbN0_dB 0:2:12; % 仿真用的Eb/N0范围 numBits 1e5; % 每个信噪比点仿真的比特数 berResults zeros(size(EbN0_dB)); for idx 1:length(EbN0_dB) % 根据Eb/N0计算AWGN信道需要设置的SNR % QPSK时每个符号2比特符号速率 信息速率 / 2 Rs Rb / 2; % 符号速率 BW Rs * (1 0.35); % 考虑滚降系数0.35的噪声带宽 SNR_dB EbN0_dB(idx) 10*log10(2) - 10*log10(Rc/BW); % 换算到码片带宽 set_param(dsss_rx/AWGN Channel, SNR, num2str(SNR_dB)); % 运行仿真 simOut sim(dsss_model.slx, StopTime, num2str(numBits / Rb)); berResults(idx) simOut.get(ber); end这段代码里最关键的是SNR换算那一行。我减去了10*log10(Rc/BW)因为AWGN模块的SNR定义是信号功率除以总噪声功率而噪声功率正比于带宽。如果直接把Eb/N0当SNR用曲线会整体往右偏。实际上对于扩频系统噪声带宽应该取接收滤波器带宽我这里用了符号速率乘(1滚降系数)近似。很多教科书上直接写SNR EbN0 10log10(2)那是未扩频QPSK的公式扩频后必须考虑带宽展宽。这个公式我每次用都会重新推导一遍防止自己偷懒抄错。3.3 辅助m脚本设置仿真参数与跑批量误码率除了上面的分段代码我还会整理一个总控制脚本把仿真参数集中放在文件头避免每次修改都要翻模型里的模块。Simulink模型本身是图形化的但参数源于脚本这样既保留了可视化的优势又能批量跑实验、画曲线。这个习惯让我从“手动调参”的泥潭里爬了出来。%% 总控制脚本dsss_sim_controller.m clear; clc; % 模型名称 mdl dsss_model; open_system(mdl); % 全局参数表 params struct(); params.Rb 9600; params.Nc 64; params.Rc params.Rb * params.Nc; params.Fs params.Rc * 4; params.Poly [7 6 1]; params.InitStates [1 0 1 0 1 0 1]; % 把参数写入模型工作区 assignin(base, params, params); % 设置仿真时长秒 simTime 5; % 先跑5秒确认链路是否通 set_param(mdl, StopTime, num2str(simTime)); % 运行仿真 simOut sim(mdl, ReturnWorkspaceOutputs, on); % 从仿真输出中提取误码率 ber simOut.ber(end); fprintf(Simulated BER: %e\n, ber);这里我用assignin把结构体写到模型工作区模型里的模块参数直接用params.Rb这种写法引用。好处是参数一目了然改一处全局生效。坏处是如果你忘了运行这个脚本Simulink模型里会出现未定义变量或空的参数值仿真直接报错。所以我的习惯是模型里所有参数都填变量名坚决不填裸数字这样批处理时才不会漏改。4. 直接序列扩频仿真参数设计码片速率、滚降系数、PN码长度的匹配关系4.1 码片速率与信息速率处理增益怎么设定处理增益是直接序列扩频的核心指标它决定了系统能容忍多大的干扰。但很多人在仿真里把码片速率定得很随意比如随意扩到几十倍却不考虑带宽和频谱资源的限制。工程上处理增益的设定要看对抗干扰需求每增加3dB处理增益系统抗窄带干扰的能力就翻一倍但带宽也翻一倍。在仿真里我通常先定信息速率再根据系统指标反推需要的处理增益最后确定码片速率。举例来说如果系统要求输出信噪比在-10dB时误码率低于1e-3QPSK理论门限大约是6.5dB的Eb/N0那么处理增益至少要留出16.5dB的余量。扩频增益取64倍约18dB是比较常见的选择既满足要求又不至于让采样率爆炸。在Simulink里你还要保证采样频率Fs至少是码片速率的2倍工程上我一般取4到8倍才能让脉冲整形和匹配滤波正常工作。4.2 PN码生成与同步m序列的初始状态和Gold码的选型PN码的选择直接影响扩频性能。m序列的自相关特性好旁瓣电平低生成也简单所以仿真里最常用。但它有个弱点互相关特性不是最优多用户场景下不如Gold码。如果你的课题是单用户链路级仿真m序列足够如果有多个用户或多址需求就要换Gold码。Gold码是由两个优选m序列异或生成的互相关峰值受控但生成结构稍复杂。在Simulink里PN Sequence Generator模块默认支持m序列指定本原多项式和初始状态就行。初始状态决定了序列的起始相位。这里有个常见的坑接收端和发射端必须用完全相同的初始状态如果差一个码片解扩出来的信号就是噪声。我在仿真里做同步验证时会故意设置一个延迟然后用一个可变延迟模块去对齐看看误码率能不能恢复。初始状态不能全零这个我在前面已经强调过。Gold码生成我一般不用Simulink模块而是用m脚本先把两个m序列算出来再存成工作区变量或者用Communications Toolbox的goldSequence函数。仿真时直接把序列作为参数写入模型。这样做的好处是后续如果要评估不同PN码的性能差异只需要替换变量模型结构不动。4.3 滚降系数和采样率仿真步长与频谱泄漏的平衡直接序列扩频的信号带宽是码片速率的倍数但这只是理想情况。实际发射端必须用脉冲整形滤波器来限带最常用的是升余弦滤波器滚降系数alpha决定过渡带宽度。alpha越小带宽越窄频谱效率越高但时域波形拖尾越长对定时误差更敏感。alpha取0.22或0.35是工程上常见的值。在Simulink仿真里滚降系数直接影响采样率的选择。我通常让过采样倍数sps Fs / Rc 4alpha取0.35这样滤波器的群延迟大概在6个码片左右信号频谱不会溢出。如果你把sps取成2滚降系数又小频谱会贴到Nyquist边界仿真结果会出现莫名的误码率抬高。另一个问题是仿真步长固定步长仿真时步长要小于奈奎斯特采样间隔。如果模型里有Variable-Step Solver它可能因为信号切换太频繁而自动减小步长拖慢仿真速度。我习惯把求解器设为固定步长步长取1/Fs同步保证所有模块在同一个时钟上工作。下面是一个典型参数组合表方便你直接套用参数建议值说明信息速率 Rb9600 bps可任意修改扩频码片数 Nc64处理增益约18dB码片速率 Rc614.4 kcpsRb * Nc采样率 Fs2.4576 MHzRc * 4过采样倍数4每个码片4个采样点滚降系数 alpha0.35升余弦滤波器求解器步长1/Fs约0.407usPN码本原多项式[7 6 1]周期127PN码初始状态[1 0 1 0 1 0 1]不能全零这张表里的采样率和步长是绑定的改任何一个都要同时改另一边。我见过很多翻车案例就是只改了码片速率忘了改采样率结果滤波器输出出现频谱混叠。5. 直接序列扩频仿真避坑5个让结果翻车的典型问题5.1 现象误码率曲线在高信噪比下不下降信噪比很高比如Eb/N0到了15dB误码率还是停留在1e-3怎么都降不下去。看星座图接收信号聚集在四个点旁边但偶尔有散点跳出去。原因是PN码同步出错或者扩频和解扩使用的序列相位不一致。接收端虽然用的是同一条m序列但如果没有精确对齐解扩过程其实是在把一个扩频信号乘以一个不相关的码片序列结果就是噪声能量被平均掉但信号也被打散了。高信噪比下残留的误码是因为相关峰没有完全对准旁瓣干扰限制了性能。解决方法是先检查接收端与发射端的PN码初始状态是否一致再用一个相关性测量模块去估计码相位偏移。我一般会在模型里加一个Scope观察解扩后的信号眼图——如果眼图张开度不够同步就有问题。另外用“假同步”方案时要确保延迟模块的延迟样点数正好是采样率乘以码片偏移量不能多一拍少一拍。5.2 现象解扩后星座图旋转但眼图看不出问题星座图四个点随着时间慢慢旋转幅度没有太大变化但误码率很高。眼图看起来张得很开这说明符号定时是好的问题出在载波相偏上。原因是信道模型或者本地振荡器引入了残余载波频偏。在Simulink的AWGN信道里你不一定设置了频偏模块但如果你用了Phase Noise或直射路径就可能引入慢变相位。QPSK解调对相位误差敏感一个固定的45度相偏就会让误码率飙到1e-1。排查方法是先冻结信道把所有噪声设为0只看星座图是否稳定。如果旋转逐级检查乘法器前后的信号用复数频谱分析判断载波频率偏了多少。对于仿真最直接的办法是给接收端加一个载波同步模块比如Communications工具箱里的Carrier Synchronizer。但注意它的环路带宽不能设太大否则会引入额外噪声。5.3 现象PN码同步总是错一位导致整个链路全错我遇到过一种情况PN码周期是127扩频长度64然后再跑到第二段时同步突然崩掉。后来发现是每比特的PN码截取位置没有循环回绕。原因是PN Sequence Generator生成的m序列是连续的但你的扩频模块每比特取了64个码片这个64不是PN码周期127的整数倍。于是第1比特用码片1到64第2比特用码片65到128但第127位之后又回到1。如果接收端也按同样的方式取值理论上是同步的但一旦接收端起始相位差一个码片错误会累积并且出现周期性错位。解决方法是要么让扩频码片数正好等于PN码周期的整数倍比如周期127就取127个码片但这样处理增益变成21dB不一定是你想要的。要么在发射端把PN码序列先存成一个周期向量然后在扩频时按索引取模。在Simulink里我常用一个buffer模块把连续PN码流重组成每比特固定长度同时保证每个比特的起始码片是序列周期对齐后的位置。这个坑特别隐蔽因为低信噪比下误码率可能只是稍微抬高你看不出规律。5.4 现象仿真速度极慢批处理一晚上跑不完误码率仿真需要跑几千到几万个比特尤其是低信噪比区域为了统计到足够的错误事件仿真时间会非常长。很多人直接把StopTime设成几十秒然后仿真器就像蜗牛爬。原因是Simulink的默认求解器是变步长当信号中有快速跳变时比如PN码切换瞬间求解器会自动缩小步长导致计算量爆炸。另外如果你用了一堆Scope模块并且打开着显示图形渲染也会占用大量CPU。解决方法是把求解器改成固定步长步长设为采样间隔的整数倍比如1/Fs。同时在批量仿真时关闭所有Scope用set_param把OpenAtSimulationStart设为off或者干脆把Scope模块从模型里删除等需要看波形时再临时加上。还可以用Simulink的Accelerator模式把模型编译成C代码再跑速度提升5到20倍。我自己的习惯是批处理时先跑短时间确认链路通然后关掉可视化再回头跑长仿真。5.5 现象Simulink模型和m脚本结果对不上同一个扩频方案我用m脚本手写了一个仿真又搭了Simulink模型结果两者误码率曲线差很多。查来查去发现是滤波器延迟没有对齐。原因是Simulink里的升余弦滤波器有群延迟导致发射和接收之间的信号路径有额外的延迟。m脚本里你可能没有模拟这个延迟或者是延迟补偿没做好。这会让误码率无法统计因为比较的比特不是同一时刻的。解决方法是先测量整个链路的延迟用Simulink的布尔脉冲通过全链路观察脉冲延迟了几个采样周期。然后在误码率统计模块里设置一个延迟偏置让发射比特和接收比特对齐。我常用一个简单的办法在做误码率对比之前先用一个1024比特的测试序列发射接收端和自己发射端存活的序列做互相关相关峰值对应的索引就是延迟。对了这个互相关计算用m脚本做即可不要把模型搞复杂。两种仿真对不上时优先怀疑延迟其次才是信噪比定义。6. 从仿真到工程验证用外部模式跑实时数据以及C代码生成前的检查清单Simulink仿真的最终归宿往往是把模型部署到硬件上。外部模式External Mode是Simulink连接实物硬件的第一步它允许你在真实运行中在线调参、观察信号。做法很简单在模型配置界面里把求解器类型设为固定步长选择外部模式作为仿真目标然后在模型里加入UDP或串口通信模块把解扩后的数据发出来。我一般先在纯仿真里确认固定步长能跑通再切换到外部模式。注意外部模式下模型的实时性取决于步长是否满足系统约束比如采样率2.4576MHz步长只有0.4us普通嵌入式平台很难跑动这时候就要把采样率降低或者把部分算法用代码生成部署到FPGA。C代码生成是另一个进阶方向。在检查清单里我首先确认所有模块都是支持代码生成的尤其要检查PN Sequence Generator和滤波器模块的“Support code generation”属性。其次把工作区变量全部转成模型里的Constant模块或Parameter对象因为生成代码时不会自动导入工作区。最后检查有无不可综合的模块比如Scope、Display这类可视化模块要在生成前移除。如果你要生成AUTOSAR或嵌入式代码还要配置系统目标文件和步长设置。这些步骤我踩过很多坑最惨的一次是生成代码前忘了初始化PN码的初始状态结果板子上的扩频码全是零。我的习惯是每完成一个阶段的仿真就保存一份当时的模型和m脚本并用日期做文件夹名。这个习惯救了我很多次因为调参调到最后你很可能已经忘了最初哪个参数组合给出过最好的结果。代码生成之前我一定会单独建立一个干净的模型副本从原模型里删掉一切示波器和示波器相关的逻辑再进行生成。希望这些从仿真到落地的思路能帮到你少走我走过的那些弯路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站