基于MATLAB的直接序列扩频仿真与误码率分析
简介基于MATLAB的直接序列扩频通信系统仿真完整方案主要面向通信工程、电子信息及计算机相关专业的高年级本科生用于课程设计、大作业或毕业设计初期的系统仿真参考。项目包含可直接运行的m源码与配套文档覆盖M序列生成、Walsh码分配、扩频解扩、调制解调、加扰解扰等关键模块并提供了测试脚本与辅助工具便于学习扩频通信原理和验证系统性能。压缩包共27个文件以24个m源文件为核心另含1个md说明文档和1个license许可证整体仅15KB轻量易部署。该资源已有137人学习下载属于小巧但完整的高分课设案例既适合初学者对照仿真流程逐步理解也支持在此基础上二次开发如调整扩频码或增加误码率分析模块提升课设完成度。1. 直接序列扩频的MATLAB仿真值得一整天研究些什么这个zip里最值钱的其实是那个叫“全部数据”的文件夹。源码跑完就扔的项目到处都是但能把BER结果、频谱图、不同扩频因子下的对照表都留全的才具备答辩时讲清楚的基础。直接序列扩频DSSS是通信原理里少有的一个题目能同时从时域波形、频谱展宽、误码率曲线三个角度把理论课上的公式验证一遍。用MATLAB脚本而不是Simulink来搭是因为脚本把扩频、解扩、判决的每一步都暴露在workspace里答辩被追问细节时不慌。适合通信工程做课程设计的学生、刚接手扩频系统测试的工程师、想用一上午把DSSS完整跑通的开发者。2. 扩频因子、码片速率与处理增益的计算和选型2.1 处理增益怎么算参数表怎么定直接序列扩频的本质是让发射功率不变带宽变宽。信息速率$R_b$先经过扩频码映射成码片速率$R_c$两者之比就是扩频因子$G_p R_c / R_b$换成对数就是处理增益$PG 10\lg G_p$。这个值直接决定了系统能把信噪比改善多少也是答辩时第一个大概率被问到的数字。Rb 10e3; % 信息速率 10 kbps Gp 31; % 扩频因子每个信息比特对应31个码片 Rc Rb * Gp; % 码片速率 310 kcps PG_dB 10*log10(Gp); % 处理增益 ≈ 14.91 dB这段代码里Gp选了31因为周期31的m序列正好是5级移位寄存器能产生的长度$2^5-1$是课程设计里性价比最高的选择。PG大约是15dB意味着在理想解扩条件下一个本来要被淹没在噪声里的信号经过相关累加后等效信噪比可以抬升约15dB。仿真开始前先把下面这张参数表定下来后面所有代码都围着这张表转。信息速率取10kbps是为了让码片速率落在310kcps这个量级在纯基带仿真里计算量小画出来的波形也直观。采样倍数sps取10每个码片采10个点既能看到码片形状又不至于让仿真时间拉得太长。需要观察更精细的频谱时再临时把sps调大到20或32。参数符号取值说明信息速率Rb10 kbps每比特持续时间 0.1 ms扩频因子Gp31每信息比特对应的码片数码片速率Rc310 kcps扩频后速率 Rb×Gp采样倍数sps10每个码片的采样点数采样率Fs3.1 MHzRc×sps2.2 m序列、Gold序列与Kasami序列怎么选课程设计选扩频码绝大多数情况选m序列就够了。m序列周期$N2^m-1$自相关函数是理想的二值特性零偏移时相关峰值最大偏移一个码片以上相关值立刻掉到$1/N$。这个性质让接收端的解扩相关器在码同步对齐时能拿到最大的累加增益仿真曲线也干净。Gold序列和Kasami序列用在CDMA系统里是为了降低用户间的互相关性。m序列的互相关不是恒定的某些相位差会出现较大的相关峰值Gold序列从优选对m序列组合出来互相关值有上界所以多用户场景才需要它。单用户点对点DSSS仿真里用Gold属于杀鸡用牛刀还会让代码多一层生成逻辑。序列类型周期自相关互相关上界生成复杂度典型用途m序列$2^m-1$理想二值不恒定最简单单用户扩频、测距Gold序列$2^m-1$较好有界且恒定低CDMA多用户Kasami序列$2^{2m}-1$较好逼近Walsh下界中低截获概率系统具体实现时m序列的本原多项式可以直接查表。表里给的是移位寄存器反馈抽头位置不同文献对抽头的编号习惯可能不一样但只要是本原多项式生成的序列周期和自相关特性就一样只有相位不同不影响仿真结论。级数 m周期常用反馈抽头位置37[1 3]415[1 4]531[2 5]7127[1 7]2.3 BPSK调制与理想同步模型怎么定扩频后的码片序列最终要调制到载波上。课程设计里我一般用BPSK因为BPSK的误码率理论公式最干净$P_b \frac{1}{2}\operatorname{erfc}\left(\sqrt{E_b/N_0}\right)$仿真结果可以直接和这条曲线比对验证链路有没有搭错。QPSK、MSK这些调制方式在误码率推导上都会多一层映射关系写进文档反而显得啰嗦。同步问题是DSSS仿真里最容易卡住新手的地方。接收端要知道载波频率和相位、码片起始时刻、PN码的相位三个信息才能正确解扩。完整仿真这些环节会引入锁相环、延迟锁定环代码量翻倍。课程设计的合法做法是把载波同步和码同步都做成“理想同步”即接收端直接使用与发送端相同的PN序列并且从正确的码片位置开始取值。这个假设要在文档里明确写出来。提示答辩时被问到“同步怎么实现”不要回避。先承认仿真采用理想同步再补充一句实际系统中码同步一般用延迟锁定环DLL载波同步用Costas环方向就给对了。3. 用MATLAB搭一条完整的DSSS仿真链路3.1 生成m序列自实现反馈移位寄存器先用一个函数把m序列生成器写出来参数只有三个反馈抽头位置、输出长度、寄存器初值。这样后面换序列只需要改taps向量。function seq gen_mseq(taps, nBits, initReg) % gen_mseq 用线性反馈移位寄存器生成m序列 % taps: 反馈抽头位置例如 [2 5] 对应 m5 的常用本原多项式组合 % nBits: 输出的比特长度不必是完整周期 % initReg: 寄存器初值1×max(taps)默认全1 if nargin 3 || isempty(initReg) initReg ones(1, max(taps)); end reg initReg(:); seq zeros(1, nBits); for k 1:nBits seq(k) reg(end); % 末级寄存器输出 fb reg(taps(1)); % 反馈值从抽头位置异或得到 for t taps(2:end) fb xor(fb, reg(t)); end reg [fb, reg(1:end-1)]; % 左移并写入反馈值 end end这个实现的逻辑核心是fb xor(...)这一行。每个时钟周期寄存器整体左移一位新的输入由抽头位置的几个寄存器值异或后得到末级同时作为序列输出。反馈多项式决定了状态会遍历除全零外的所有组合所以输出序列周期是$2^m-1$。注意初值不能设成全零全零状态下反馈一直是0序列就卡死了。如果安装了Communications Toolbox也可以直接用comm.PNSequence对象做对照验证pnObj comm.PNSequence(... Polynomial, [1 1 0 0 1], ... % 对应 x^5 x^2 1 SamplesPerFrame, 31, ... InitialConditions, ones(1,5)); pnToolbox pnObj();两个输出序列可能看起来相位不一致但只要教材里的本原多项式表和这里一致周期和统计特性就是同一回事。仿真中不要混用两条序列解扩端用的必须是发送端切出来的同一段PN序列。3.2 发射机比特扩频、码片映射与手动加噪发射机的输入是一串随机比特输出是已经扩频且调制到基带波形上的采样点序列。扩频操作我习惯用异或实现每个信息比特先重复Gp次再与完整PN序列逐位异或得到长度N*Gp的码片序列。Nbits 1000; % 每帧信息比特数 Gp 31; % 扩频因子 sps 10; % 每码片采样点数 info randi([0 1], 1, Nbits);% 待发送信息比特 pnSeq gen_mseq([2 5], Gp); % m序列长度为31 pnFull repmat(pnSeq, 1, Nbits); % 复制到与码片总数相同 infoUp repelem(info, Gp); % 每个信息比特重复Gp次 chipSeq xor(infoUp, pnFull); % 逐位异或完成扩频 symbols 2 * chipSeq - 1; % 0/1映射为 -1/1 tx upsample(symbols, sps); % 矩形脉冲成形每个码片sps个采样点repelem把每个信息比特沿时间方向复制Gp份repmat把PN序列横向拼Nbits次两个向量长度都是Nbits*Gp逐位异或后得到的码片序列在统计上等价于“每个信息比特被一段PN序列直接调制”。这里故意用矩形脉冲成形每个码片保持sps个恒定幅值采样点方便后面计算能量关系等需要画频谱图时再换成根升余弦滤波器。加噪声是最容易出错的环节。MATLAB的awgn函数按采样带宽定义SNR直接往里塞Eb/N0经常得到一条偏离理论的BER曲线。通信仿真里我一般手动加噪声把能量关系完全控制在自己手里EbN0dB 6; EbN0 10^(EbN0dB/10); samplesPerBit Gp * sps; % 每个信息比特持续Gp*sps个采样点 N0 samplesPerBit / EbN0; % 由Eb/N0反推噪声单边功率谱密度 noise sqrt(N0/2) * randn(size(tx));% 实基带BPSK用双边谱 rx tx noise;这里的关键推导是发送波形每个采样点幅度为±1所以每个信息比特的能量$E_b G_p \cdot sps$。设定目标$E_b/N_0$后噪声的单边功率谱密度就是$N_0 E_b/(E_b/N_0)$实基带信号在每个采样点上的噪声方差是$N_0/2$。这样生成的噪声在接收端解扩后判决变量上的信噪比正好是$2E_b/N_0$与理论公式的前提严格一致。改信噪比只需改EbN0dB这条链路的能量关系不需要重新校正。3.3 接收机匹配滤波、解扩累加与比特判决接收端先对每个码片内的采样点做平均等效于矩形脉冲的匹配滤波。然后在每个码片中心位置抽取得到带噪码片序列再用本地PN序列的±1形式做相关累加判决出信息比特。rxMatched filter(ones(1, sps)/sps, 1, rx); % 码片匹配滤波 chipRx rxMatched(sps:sps:end); % 抽取码片中心点 chipMat reshape(chipRx, Gp, Nbits); % 每列对应一个信息比特 pnVec 2 * pnSeq(:) - 1; % PN序列映射为±1 decVar pnVec * chipMat; % 相关累加得到判决统计量 bitRx decVar 0; % BPSK正负判决 berFrame mean(bitRx ~ info);reshape把长度为Nbits*Gp的码片序列按列填成Gp×Nbits矩阵第一列是第一个信息比特对应的31个码片依此类推。pnVec * chipMat这一步是解扩的全部秘密31个带噪码片乘以对应PN码片后累加信号分量同相叠加变成31倍噪声分量由于PN码的伪随机性近似相互抵消只增大$\sqrt{31}$倍于是判决信噪比获得约15dB的改善这就是处理增益在时域上的体现。3.4 误码率扫描主循环与参数化封装把前面几段拼成一个函数外面套一层Eb/N0扫描循环。每个信噪比下跑几十帧统计足够多的错误比特BER曲线的抖动就压下来了。function ber run_dsss_ber(ebn0dB, Gp, sps, Nbits, Nframes) % run_dsss_ber 执行完整DSSS链路返回该信噪比下的平均误码率 pnSeq gen_mseq([2 5], Gp); pnVec 2 * pnSeq(:) - 1; errCnt 0; totCnt 0; for f 1:Nframes info randi([0 1], 1, Nbits); pnFull repmat(pnSeq, 1, Nbits); chipSeq xor(repelem(info, Gp), pnFull); symbols 2 * chipSeq - 1; tx upsample(symbols, sps); % 发射 N0 Gp * sps / 10^(ebn0dB/10); % 噪声参数 rx tx sqrt(N0/2) * randn(size(tx)); % 加噪 rxMatched filter(ones(1, sps)/sps, 1, rx); chipRx rxMatched(sps:sps:end); decVar pnVec * reshape(chipRx, Gp, Nbits); bitRx decVar 0; errCnt errCnt sum(bitRx ~ info); totCnt totCnt Nbits; end ber errCnt / totCnt; end这个函数的运行成本主要在调gen_mseq、reshape和filter。Gp31、sps10、Nbits1000时一帧大约3.1万个采样点对MATLAB来说是非常轻的量级。扫描6个信噪比、每个跑50帧普通笔记本一两分钟就能完成。提示如果发现BER曲线在某个信噪比之后不降反升或者“仿真发散”先检查噪声方差是不是被重复叠加了。常见原因是主循环把rx tx noise写成了rx rx noise噪声每帧累加信噪比就会越来越差。4. 仿真结果怎么验证理论曲线、处理增益与抗干扰实验4.1 BER曲线与理论值的对齐是链路正确性的唯一判据链路搭没搭错不看波形长得像不像只看BER曲线能不能和理论曲线靠在一起。BPSK理论误码率是$P_b \frac{1}{2}\operatorname{erfc}\left(\sqrt{E_b/N_0}\right)$MATLAB里直接画出来和仿真点叠加ebn0List 0:2:10; berSim zeros(size(ebn0List)); for k 1:length(ebn0List) berSim(k) run_dsss_ber(ebn0List(k), 31, 10, 1000, 50); end ebn0Fine 0:0.1:10; berTheory 0.5 * erfc(sqrt(10.^(ebn0Fine/10))); semilogy(ebn0Fine, berTheory, -); hold on; semilogy(ebn0List, berSim, o); grid on; xlabel(Eb/N0 (dB)); ylabel(BER); legend(理论BPSK, DSSS仿真); title(DSSS误码率曲线验证);判断标准很简单在$10^{-3}$量级仿真点和理论曲线差距应该在0.5dB以内。如果差得远按概率从高到低排查噪声方差算错、扩频码解扩时没用同一相位、判决门限设成了0.5而不是0。下表是Gp31、sps10、每点1000帧时常见的对照区间随机种子不同会有波动但量级不会跑偏。Eb/N0 (dB)理论BER仿真BER参考区间07.86e-27.5e-2 8.2e-223.75e-23.5e-2 4.0e-241.25e-21.1e-2 1.4e-262.39e-32.0e-3 3.0e-381.91e-41.5e-4 2.5e-4106.0e-6需要更多帧数才稳定4.2 单音干扰实验处理增益能不能直接测出来BER曲线只能证明链路正确不能直观展示扩频的抗干扰能力。我一般会再加一组单音干扰实验把干扰信号与合法信号同频带叠加固定Eb/N0然后看有扩频和无扩频的BER差异。fI 0.1 * Fs; % 干扰频率取信号带内 JNR 10; % 干扰噪声功率比单位dB jamAmp sqrt(10^(JNR/10) * mean(tx.^2)); % 干扰幅度按JNR设置 jammer jamAmp * sin(2 * pi * fI/Fs * (0:length(tx)-1)); rxWithJam tx noise jammer; % 干扰叠加到接收波形上窄带单音干扰进入解扩相关器后会与本地PN码相乘。PN码在频域上是宽谱的相乘相当于把单音干扰的能量扩散到整个扩频带宽上再经过解扩累加干扰贡献被摊薄了约$G_p$倍。这就是为什么DSSS系统能在强窄带干扰下维持通信。这张表的数值依赖Gp31、sps10的具体参数重点看量级关系不必与随机种子严格一致。干扰场景干扰功率相对信号 (dB)无扩频 BERGp31 扩频 BER无干扰01.2e-21.2e-2单音干扰10直接通信基本失效2e-2 4e-2单音干扰20完全无法通信8e-2 1.2e-1干扰功率越高扩频带来的改善越明显。答辩时把这组数据放进文档再配合处理增益公式就能把“为什么扩频能抗干扰”从定性描述变成定量结论。4.3 码相位偏差带来的BER恶化最后一个必测项目是码相位偏差。发送端和接收端使用同一段PN码是理想同步假设但实际系统不可能对齐到零偏差。做一个快速实验把接收端PN序列循环移动1个码片、半个码片观察BER变化。pnShift circshift(pnVec, 1); % 偏移1个码片 decVarShift pnShift * reshape(chipRx, Gp, Nbits);只需要把3.3节代码里的pnVec替换掉其余不动。也可以对每个码片偏移整数个采样点来模拟亚码片偏差。实验结果一定是整码片偏移时相关器输出几乎被置零BER接近0.5亚码片偏移时性能损失随偏差单调增大。这个现象的根源就是m序列自相关函数在零偏移处尖锐、其他地方迅速跌落的性质。把这组曲线放进文档正好用来解释接收端为什么需要延迟锁定环来把码相位钉在峰值的斜面上。5. 把源码、文档与数据组织成一份能直接答辩的交付物5.1 工程目录四层结构别人拿到就能复现课程设计的“全部数据”不是把结果截图堆在一起而是让评审拿到压缩包后不聊天就能对照源码和文档重跑一遍。我一般按下面这个结构组织DSSS_CourseDesign/ ├── main.m # 一键仿真入口调用全部函数并保存结果 ├── run_dsss_ber.m # 单信噪比BER统计函数 ├── gen_mseq.m # m序列生成函数 ├── plot_results.m # 读取mat文件并绘制三张关键图 ├── doc/ │ └── 课程设计报告.md # 含原理、参数表、结果分析 ├── data/ │ ├── results_ber.mat # BER原始数据 │ ├── results_jam.mat # 干扰实验数据 │ └── figures/ # PNG与fig图窗文件 └── run_verification.m # 答辩前快速验证脚本main.m只做三件事设置全局参数、依次调用误码率扫描和干扰实验、把结果保存成mat文件并调plot_results.m出图。函数文件不要在里面画图否则每个函数都会弹图窗批量跑帧时很烦。5.2 文档里必备的三张图和三张表文档部分不用长篇大论但要保证评审想验证的每个结论都能找到出处。三张图是扩频前后的功率谱对比图、BER仿真与理论对照图、抗单音干扰的BER对比图。三张表是仿真参数总表、各Eb/N0下的BER数据表、干扰实验条件与结果表。图表类型生成脚本保存格式频谱对比图plot_results.mPNG 300dpi figBER对照图plot_results.mPNG 300dpi fig干扰结果图plot_results.mPNG 300dpi fig参数总表main.m 输出data/params_table.csvBER数据表main.m 输出data/ber_table.csv干扰数据表main.m 输出data/jam_table.csv图表统一由plot_results.m从mat文件生成不要手动截图。这样源码、文档、数据三者的对应关系一查便知也避免出现“文档里的图和代码跑出来的图对不上”的尴尬。5.3 提交前的十分钟验证清单答辩前最后一天不要打开编辑器再跑一遍那样很容易被某个workspace里残留的变量干扰。准备一个独立的验证脚本用clear all清空workspace后完整执行一次全流程。验证项做法通过标准环境依赖run_verification.m只依赖MATLAB基础环境不报工具箱缺失数据完整性load data/results_ber.matberSim不为空且为有限值曲线一致性BER曲线与理论差距小于0.5dB图表数据与文档一致截图存档input(按回车继续)逐图压制PNG文件时间戳更新run_verification.m最后一行用savefig把当前生成的图窗全部另存到data/figures/答辩演示时直接双击fig文件在MATLAB里拖动查看BER点的具体坐标值比放一张静态PNG更有说服力。做完整套验证后把终端输出的运行时间和最终误码率贴到文档附录里再把data目录连同.mat结果一并打包提交。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →