直接序列扩频仿真:MATLAB建模仿真、处理增益与误码率分析
简介面向通信专业学生与扩频通信研究人员的实用仿真资料包聚焦直接序列扩频与跳频扩频系统的建模与验证。资源系统覆盖PN码序列生成、BPSK/QPSK调制解调、AWGN与多径衰落信道、干扰模型及同步机制等核心环节并包含误码率与抗干扰性能评估方法可帮助读者从理论理解顺畅过渡到Matlab/Simulink实践。压缩包内共68个文件包含18个m脚本仿真算法实现、13个mdl模型Simulink系统框图和37个mat数据文件实验数据与参数整体14.81MB文件结构清晰便于对照学习。已有420人学习使用适合课程设计、课题研究及通信系统预研参考尤其对直接序列扩频和跳频扩频的仿真落地具有直接借鉴价值。1. 直接序列扩频仿真为什么说它是扩频通信里最值得先跑通的一环做扩频通信系统最怕的不是理论算不清而是射频前端还没到位算法方案就要给结论。扩频通信系统实用仿真技术就是在这种时候把直接序列扩频DSSS的数据调制、伪码扩频、加噪解扩和误码统计放到一套可复现的模型里闭环。它解决的核心问题是在花硬件成本之前先用仿真把处理增益、抗干扰余量、同步门限定下来别等板子贴完才发现方案不行。适合正在搭扩频收发链路、做抗干扰评估或者刚接手相关项目的通信工程师。下面这套方法按我常用的 MATLAB 基带仿真结构讲三个晚上就能跑出第一版误码率曲线。2. 直接序列扩频系统的仿真结构从发送端到接收端的模块拆解直接序列扩频的仿真链路不算复杂但很容易因为“少一个模块”而得出错误结论。我的习惯是先把链路拆成四段信源与扩频调制、码片脉冲成型、信道叠加、接收端解扩与判决。仿真里不需要把射频载波完整做出来只要抓住复基带模型后面加频偏、加干扰、加同步都方便。2.1 复基带模型为什么是仿真首选省掉载波但不能省相位如果用 MATLAB 直接生成 2.4GHz 载波采样率必须到 GHz 量级一次 Monte Carlo 仿真跑上几分钟才能出几百个误码点不现实。常见做法是省略载波在复基带里做等价只在需要验证频偏时乘一个exp(j*2*pi*fOffset*t)的旋转项。对直接序列扩频来说信息全部承载在 ±1 的码片序列上载波只是搬运工仿真时把搬运工去掉保留相位关系就够了。复基带下的发送信号可以写成s_bb(t) sqrt(P) * d(t) * c(t)其中d(t)是数据符号序列取 ±1c(t)是伪随机码片序列也取 ±1P是信号功率。数据速率低码片速率高两者相乘后信号能量就被展宽到码片速率对应的带宽里。这个“带宽展宽”就是直接序列扩频的名字由来。仿真里不需要真的在时域连续积分而是做离散采样。这里有一个重要选择先用码片级仿真也就是每个码片只取 1 个采样点。这样可以快速得到误码率聚焦扩频解扩是否正确等要加脉冲成型滤波器、看频谱占用或者校同步时再切换成过采样。这个区分后面会反复用到。2.2 发射端参数表先定信息速率、码片速率和处理增益做仿真前先把参数表定下来参数不齐后面全是糊涂账。我一般用一组容易换算的数值参数仿真值说明信息速率 Rb10 kbit/s未扩频的数据速率PN 码长度 Lc1272^7 - 1也就是扩频增益码片速率 Rc1.27 Mchip/sRb * Lc处理增益 Gp21.04 dB10 * log10(Rc / Rb)采样率 Fs1.27 MHz码片级每码片 1 个采样点过采样率 spc1 起步8 用于波形级与成型滤波器配合这组参数里PN 码长度 127 就是每个数据比特被 127 个码片调制。Rc/Rb 的比值直接决定处理增益也是后面抗干扰分析的核心。仿真时不必死守 10M 之类的大码片速率关键是比值要和目标一致否则处理增益就对不上。发射端核心代码是 PN 码生成和扩频。下面这段用comm.PNSequence生成 m 序列需要 Communication Toolbox如果没有替代方案在第 5 章避坑里会讲。% 发射端核心PN码生成 数据扩频码片级每码片1个采样 Rb 10e3; % 数据速率 10kbps Lc 127; % PN码长度扩频增益 hPn comm.PNSequence( ... Polynomial, z^7z^31, ... % 7级m序列本原多项式 InitialCondition, ones(1,7), ... NumBitsOut, Lc); pn 2*double(hPn()). - 1; % 0/1 映射成 1/-1 data 2*randi([0 1], 100, 1) - 1; % 100个数据比特±1 tx kron(data, pn); % 每个数据比特后面跟一个完整PN周期kron的作用是把data里每个元素与pn所有元素逐点相乘结果长度是100 * 127。这样每个数据比特都被同一个完整 PN 周期调制也就是扩频动作本身。注意pn的初相由InitialCondition决定同样是本原多项式不同初相会产生不同相位偏移。这就是后面接收端同步要搜索的东西本地 PN 码和发送端 PN 码的相位差。2.3 接收端相关解扩为什么积分清零能恢复数据接收端不知道数据但知道 PN 码。把接收信号和本地 PN 码逐点相乘因为c(t)只有 ±1平方恒为 1所以r(t) * c(t)等于±d(t)再加上被展宽的噪声。接着对一个数据比特周期做积分信号部分会累加出一个明显的峰值噪声因为随机正负不能同向累加这就是相关解扩。在离散仿真里积分就是一个分组求和。以码片级仿真为例% 接收端相关解扩假设码相位已经对齐 r tx; % 先用无噪声信号演示 m reshape(r, Lc, []) .* repmat(pn(:), 1, 100); y sum(m, 1); dataHat sign(y).;reshape(r, Lc, [])把码片流按比特分块第 i 列就是第 i 个数据比特对应的 127 个码片。repmat(pn(:), 1, 100)让每个块乘同一个本地码。sum(m,1)对每一列求和完成积分清零。无噪声时y的值要么是127要么是-127符号判决就能还原数据。到这里扩频和解扩的数学关系已经立住了。第 3 章把它扩展成带噪声、带误码率统计的完整脚本。3. 用 MATLAB 跑通可直接复现的 DSSS 仿真链路完整脚本与误码率统计有了第 2 章的模块拆解这章给一份能直接复制运行的完整脚本。它的目标不是性能最优而是把“扩频→加噪→解扩→误码统计”的最小闭环跑通让新手能照着改参数熟手能当骨架用。3.1 完整脚本从参数到误码率曲线的闭环脚本分四段参数、PN 码生成、误码率扫描、画图。我建议先跑通 0~10dB再逐步加深。% 直接序列扩频 BPSK-AWGN 误码率仿真码片级 % 依赖 Communication Toolbox 的 comm.PNSequence clear; clc; rng(42); % ---- 参数 ---- Rb 10e3; % 数据速率 10kbps Lc 127; % 每个比特对应的码片数扩频增益 Rc Rb * Lc; % 码片速率 1.27Mchip/s numBits 5000; % 每个信噪比点的仿真比特数 EbN0dB 0:2:10; % Eb/N0 扫描范围 % ---- PN 码 ---- pnObj comm.PNSequence( ... Polynomial, z^7z^31, ... InitialCondition, ones(1,7), ... NumBitsOut, Lc); pn 2*double(pnObj()). - 1; % 转成行向量取值 1/-1 % ---- 误码率扫描 ---- ber zeros(size(EbN0dB)); for k 1:length(EbN0dB) EbN0 10^(EbN0dB(k)/10); N0 1 / EbN0; % 归一化 Eb 1 noiseVar N0 / 2; % 实噪声每码片采样方差 data 2*randi([0 1], numBits, 1) - 1; % 数据比特 ±1 tx sqrt(1/Lc) * kron(data, pn); % 扩频同时归一化比特能量 rx tx sqrt(noiseVar) * randn(size(tx)); % 加性高斯白噪声 despread reshape(rx, Lc, numBits) .* repmat(pn., 1, numBits); y sum(despread, 1); % 积分清零 dataHat sign(y).; ber(k) sum(dataHat ~ data) / numBits; end % ---- 画图 ---- figure; semilogy(EbN0dB, ber, o-); grid on; xlabel(Eb/N0 (dB)); ylabel(BER); title(DSSS-BPSK AWGN 仿真结果);这段代码最需要理解的是sqrt(1/Lc)和noiseVar。sqrt(1/Lc)让扩频后每个码片幅度变成sqrt(1/Lc)这样 127 个码片能量加起来刚好等于 1也就是归一化比特能量Eb 1。噪声方差N0/2 1/(2*EbN0lin)对应实 AWGN 的单边功率谱密度。解扩时 127 个码片相干累加信号分量变成sqrt(127)量级噪声方差累加成127*N0/2最终信噪比就是2*Eb/N0和 BPSK 理论一致。如果不想手推也可以用 MATLAB 的awgn函数但我在工程里更喜欢显式控制方差因为后面加窄带干扰、加频偏时这种写法更容易定位问题。3.2 验证结果和 BPSK 理论曲线放在同一张图上扩频并没有改变每个比特的能量所以 DSSS-BPSK 在 AWGN 下的理论误码率就是普通 BPSK 的0.5*erfc(sqrt(EbN0))。在脚本后面补两行theory 0.5*erfc(sqrt(10.^(EbN0dB/10))); hold on; semilogy(EbN0dB, theory, -, LineWidth, 1.2); legend(仿真, 理论BPSK);如果仿真曲线和理论曲线相差超过 0.3dB先不要调参数回查噪声方差和比特能量归一化。最常见错误是忘了sqrt(1/Lc)导致实际 Eb 变成 Lc 倍曲线产生明显偏移。另一个常见错误是把扩频增益算进 Eb/N0以为仿真会得到比 BPSK 更好的误码率实际上在纯 AWGN 下不会。3.3 参数扫描不同 PN 码长度和处理增益的关系想快速看不同扩频增益的影响可以写一个外层循环把Lc从 31、63 扫到 127。每次仿真只改Lc和对应的 PN 码生成器观测误码率曲线。你会发现横轴用 Eb/N0 时不同 Lc 的曲线几乎重叠这就是“扩频不提高 AWGN 性能”的直接演示。如果希望看到扩频的真实收益要把横轴变成信噪比 SNR 或者固定干扰功率。比如在同样发射功率和干扰功率下Lc 越长的系统解调后的误码率越低。这个区别新手容易绕进去建议把这段实验做一遍比看十篇资料都管用。4. 直接序列扩频仿真参数的三个关键旋钮采样率、滤波器滚降和同步门限上一章的码片级仿真适合验证基带逻辑但真正贴近工程的波形级仿真还要处理过采样、成型滤波和同步。这章讲三个参数的设置逻辑每个都直接影响你能不能把仿真结果搬进真实系统。4.1 过采样率1 个采样/码片和 8 个采样/码片差在哪码片级仿真里每个码片 1 个采样点计算快但不能加带宽受限的脉冲成型滤波器。真实系统里码片脉冲不可能占满整个频带都要经过升余弦或根升余弦滤波器限带这时每个码片至少 4~8 个采样点否则滤波器会产生严重的时域混叠。我一般用spc 8起步滚降因子 0.22。在 MATLAB 里可以用rcosdesign生成根升余弦滤波器再配合upfirdn完成内插和滤波spc 8; % 每码片采样数 rrc rcosdesign(0.22, 6, spc, sqrt); txFiltered upfirdn(tx, rrc, spc, 1); % tx 是码片级序列upfirdn的第三个参数是内插倍数第四个参数是抽取倍数。这里按spc内插后不做抽取得到过采样信号。接收端要用同一个根升余弦滤波器做匹配滤波然后再解扩。注意滤波器的群延迟会让信号的码片边界“错位”解扩前要先把群延迟补偿掉否则误码率会降不下来。这个坑在第 5 章会专门展开。过采样率的选择不是越大越好。spc 16在 MATLAB 里跑起来还快但后面做捕获的滑动相关时计算量会翻倍。如果只是为了波形观察8 倍足够如果做高精度定时同步仿真再考虑 16 倍。4.2 处理增益 vs Eb/N0为什么扩频没有让 BER 曲线左移很多刚接触直接序列扩频的同行会问处理增益不是 21dB 吗为什么误码率曲线没往左挪 21dB答案是处理增益改变的是带宽不是每比特能量。发送端把 N 个码片拼成一个比特每个码片能量只有 Eb/N接收端相干累加 N 个码片又把能量收回来。噪声也经历了同样的展宽和收窄所以 Eb/N0 不变误码率也不变。处理增益的收益要在有干扰时看。比如加一个窄带干扰干信比 JSR 是 20dB解扩后干扰功率会被处理增益抑制等效干信比变成20 - 21 dB也就是约 -1dB。验证方法很简单在tx上叠加一个单音干扰频率放在信号频带中央幅度比信号高 20dB然后跑同一套解扩链路。你会看到扩频增益越大误码率恢复得越好。% 在扩频信号上加窄带单音干扰观察解扩前后频谱 fI Rc/4; % 干扰频率放在信号频带内 jamming sqrt(100) * sin(2*pi*fI*(0:length(tx)-1)/ ... (Rb*Lc)); % 功率比信号高20dB rxJ tx jamming sqrt(noiseVar)*randn(size(tx));这段代码里干扰幅度取sqrt(100)对应功率是信号的 100 倍也就是 20dB。解扩后这个干扰会被扩散到整个码片速率带宽落在数据带宽里的那部分再被积分清零平均。仿真输出频谱时你能看到解扩后窄带峰值明显被压低这就是处理增益的直观证据。4.3 码相位同步相关峰搜索和门限设置上一篇仿真假设接收端已经知道码相位实际系统做不到。工程上通常用滑动相关或匹配滤波器做粗同步把本地 PN 码和一个或多个数据比特长度的接收信号逐位滑动找相关峰超过门限的位置。最粗的实现就是循环移位求点积seg rx(1:Lc); % 截取接收信号长度为一个PN周期 corrOut zeros(1, Lc); for lag 0:Lc-1 segShift circshift(seg, lag); corrOut(lag1) sum(segShift .* pn); end [peak, lagPeak] max(abs(corrOut));相关峰出现的位置就是码相位的估计值。门限不能拍脑袋常见做法是先测无信号时相关输出的统计特性用噪声功率的均方根做基准threshold alpha * sqrt(mean(abs(corrNoise).^2))alpha通常取 3~5对应虚警率约在 10^-3 到 10^-5 之间。信噪比越低越需要多个比特相干积累后再检测否则单个比特的相关峰会被噪声淹没。这里要注意过采样之后滑动步长可以小于一个码片得到更精细的相位估计但计算量会随 spc 增长。先用Lc个整数码片位置搜索找到初相后再做插值细同步是一种更实用的折中。5. 仿真避坑直接序列扩频最容易翻车的 5 个细节这部分是血泪经验每一条我都见过不止一个人踩进去。坑往往不在公式而在参数定义和信号加在哪一级。5.1 误码率比理论高一大截噪声方差没按归一化能量取现象仿真跑出来 BER 曲线比理论差 1.5~3dB而且高信噪比时斜率也不对。 原因直接把未归一化的码片信号当作能量 1又随手写噪声方差。比如省略了sqrt(1/Lc)实际比特能量变成 Lc噪声方差却按 Eb1 算两边对不上。 解决先明确Eb 1把码片幅度设成sqrt(Eb/Lc)再设noiseVar 1/(2*10^(EbN0/10))。改完后和0.5*erfc(sqrt(EbN0))比偏差应控制在 0.2dB 内。5.2 加了大功率干扰误码率反而比没加还低干扰加错了级现象在信号上叠加一个比信号强 20dB 的窄带干扰误码率曲线居然和纯 AWGN 一样好甚至更好。 原因很多人把干扰加在解扩之后的判决量上或者加在没有经过成型的基带数据上。比如干扰只出现在y里没有和扩频信号一起走扩频相关器窄带干扰就不会被处理增益抑制。 解决干扰必须叠加在发送端扩频信号之后也就是tx生成完再加然后和接收信号一起进解扩器。这样窄带干扰才真正经历扩频和解扩过程处理增益的作用才会显现。5.3 用随机序列代替伪码相关峰毛刺多同步容易跟丢现象用randi生成一组随机 ±1 序列当扩频码无噪声时相关峰正常一加噪声就出现多个峰同步抖动很大。 原因随机序列的自相关旁瓣不是恒定的。m 序列的自相关旁瓣恒为 -1/Lc随机序列的旁瓣随机起伏接近主峰高度时会造成假锁。 解决换用 m 序列或 Gold 序列。至少也要固定随机种子后预先检查自相关旁瓣电平再用于同步仿真。工程上涉及多用户或者抗截获场景要优先用 Gold 序列互相关特性更可控。5.4 解扩后星座图一直在旋转加了频偏却没有载波同步现象发射端没有理想相位参考接收端也没有加载波同步环解扩后得到的符号点在 I/Q 平面上转圈误码率接近 50%。 原因仿真里只要引入了exp(j*2*pi*fOffset*t)就相当于真实系统存在载波频偏。BPSK 解扩没有做相位校正旋转角度随时间变化判决自然失效。 解决先把频偏设成 0 验证链路再加频偏并补上科斯塔斯环或锁相环。仿真阶段的频偏不要超过数据速率的 10%否则捕获时间太快误码率曲线会变成一团乱麻。5.5 加了升余弦滤波器后误码率多出 1dB群延迟没补偿现象码片级仿真正常加上rcosdesign成型滤波后 BER 比理论差 1dB而且信噪比提高也不见好转。 原因根升余弦滤波器不是零相位滤波器信号经过滤波后码片边界被群延迟推移解扩时本地码和接收码相位不再对齐。群延迟对应几十个采样点不补偿相当于引入几十个码片的相位误差。 解决接收端先做同样参数的根升余弦匹配滤波然后把滤波器群延迟对应的采样点数从解扩窗里“挪掉”。用grpdelay函数求群延迟再在reshape前用txFiltered(grpDelay1:end)之类的办法对齐起点。补偿后误差通常能回到 0.1dB 内。6. 把仿真结果落回工程验证矢量信号源与频谱仪的对账方法仿真跑到这一步你可能已经有了一条漂亮的误码率曲线。但工程上更重要的是这条曲线换成真实信号源还能不能复现。我通常的做法是把仿真生成的扩频波形导出灌进矢量信号源播放再用频谱仪和接收机做对账。6.1 用频谱仪核对处理增益和带宽先把码片级仿真波形用根升余弦滤波成形输出为双精度基带 I/Q 文件矢量信号源回放成中频或射频信号。频谱仪上看信号占用带宽约为码片速率乘以(1 滚降因子)也就是 1.27MHz * 1.22 ≈ 1.55MHz。如果测出来带宽明显偏小多是滤波器滚降因子没对上如果带宽偏大可能是信号源内插滤波器的成形参数和仿真不一致。6.2 实测与仿真的偏差常规校准流程我习惯按下面流程走能避免大多数“玄学偏差”先用码片级仿真确定 BER 理论上限作为后续所有环节的基准。加入过采样和根升余弦滤波确认频谱占用和理论带宽一致。信号源发射接收机解调测同一 Eb/N0 下的 BER。偏差超过 1.5dB优先查群延迟、功率回读和噪声系数而不是调仿真参数。有次实测比仿真差 2.3dB我调了两天最后发现是接收端根升余弦滤波器的群延迟没补偿。补偿后偏差立刻掉到 0.5dB。后来我把每个环节的能量参考点都写在仿真脚本注释里避免再看错功率定义。直接序列扩频仿真的价值不在曲线本身而在于让你提前确认“处理增益从哪来、在什么条件下失效、同步门限怎么设”。代码会越写越顺手但一直保留“每改一个参数就重跑一次理论对照”的习惯能省下大量返工时间。希望这几点能帮你在扩频通信这条路上少踩几个坑。本文还有配套的精品资源点击获取