ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

直接序列扩频仿真:MATLAB建模仿真、处理增益与误码率分析

直接序列扩频仿真: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)是数据符号序列,取 ±1;c(t)是伪随机码片序列,也取 ±1;P是信号功率。数据速率低,码片速率高,两者相乘后信号能量就被展宽到码片速率对应的带宽里。这个“带宽展宽”就是直接序列扩频的名字由来。

仿真里不需要真的在时域连续积分,而是做离散采样。这里有一个重要选择:先用码片级仿真,也就是每个码片只取 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^7+z^3+1', ... % 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^7+z^3+1', ... '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(lag+1) = 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,噪声方差却按 Eb=1 算,两边对不上。 解决:先明确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(grpDelay+1:end)之类的办法对齐起点。补偿后误差通常能回到 0.1dB 内。

6. 把仿真结果落回工程验证:矢量信号源与频谱仪的对账方法

仿真跑到这一步,你可能已经有了一条漂亮的误码率曲线。但工程上更重要的是:这条曲线换成真实信号源,还能不能复现。我通常的做法是把仿真生成的扩频波形导出,灌进矢量信号源播放,再用频谱仪和接收机做对账。

6.1 用频谱仪核对处理增益和带宽

先把码片级仿真波形用根升余弦滤波成形,输出为双精度基带 I/Q 文件,矢量信号源回放成中频或射频信号。频谱仪上看信号占用带宽约为码片速率乘以(1 + 滚降因子),也就是 1.27MHz * 1.22 ≈ 1.55MHz。如果测出来带宽明显偏小,多是滤波器滚降因子没对上;如果带宽偏大,可能是信号源内插滤波器的成形参数和仿真不一致。

6.2 实测与仿真的偏差常规校准流程

我习惯按下面流程走,能避免大多数“玄学偏差”:

  1. 先用码片级仿真确定 BER 理论上限,作为后续所有环节的基准。
  2. 加入过采样和根升余弦滤波,确认频谱占用和理论带宽一致。
  3. 信号源发射,接收机解调,测同一 Eb/N0 下的 BER。
  4. 偏差超过 1.5dB,优先查群延迟、功率回读和噪声系数,而不是调仿真参数。

有次实测比仿真差 2.3dB,我调了两天,最后发现是接收端根升余弦滤波器的群延迟没补偿。补偿后偏差立刻掉到 0.5dB。后来我把每个环节的能量参考点都写在仿真脚本注释里,避免再看错功率定义。

直接序列扩频仿真的价值不在曲线本身,而在于让你提前确认“处理增益从哪来、在什么条件下失效、同步门限怎么设”。代码会越写越顺手,但一直保留“每改一个参数就重跑一次理论对照”的习惯,能省下大量返工时间。希望这几点能帮你在扩频通信这条路上少踩几个坑。

本文还有配套的精品资源,点击获取

返回列表