
简介面向通信原理课程学习与扩频通信入门实践的MATLAB仿真文档适合电子、通信专业学生及需要理解直接序列扩频DSSS链路的自学者。内容围绕直扩系统模型、BPSK调制与解扩流程展开并梳理m序列、GOLD码、WALSH码和OVSF码的适用场景结合2kbps信源、扩频因子64、高斯信道下的误码率仿真帮助读者建立从原理到参数验证的完整认知。包内共1个doc文件约320KB体量轻便便于按章节查阅实验原理、程序代码与结果分析。已有142人学习可作为课程实验报告撰写、MATLAB仿真复现和抗干扰性能对比的参考材料文中对处理增益、抗多径与抗同频干扰的讨论也便于延伸至码分多址相关学习。1. 扩频不是「把信号摊薄」为什么加了噪声反而更容易判对把 2 kbps 的信息流塞进 64 倍宽的频带里功率谱密度掉到原来的六十四分之一——这是直接序列扩频通信系统最直观的样子。按常理同样的发射功率摊在更宽的频带上每赫兹承载的能量更少接收机理应更难把信号捞出来。可基于 MATLAB 的直接序列扩频通信系统仿真设计跑出来的曲线恰好相反信噪比从 1 dB 扫到 15 dB误码率一路往下掉扩频因子越大拐点来得越早。这个反差是整个直扩体系的地基。答案不在发射端的频谱搬移里而在接收端那个看起来最不起眼的动作——把收到的序列再乘一遍伪随机码PN 码。乘法做两次等于没做信号被还原干扰只被乘了一次反而被推得更宽。处理增益就是这么凭空长出来的不是放大器给的是相关运算给的。搞懂这条链路值当的地方在于它同时解释了抗同频干扰、抗多径、抗频率选择性衰落这三件事为什么能用同一套数学描述也解释了码分多址为什么能在一根天线上叠几十路用户。下面从能量账本算起一路推到移位寄存器抽头和积分清零判决器。2. 处理增益的账怎么算从模二加到相关累加的能量守恒2.1 一个比特被「摊」成 N 个码片之后能量去哪了设信源序列 a(t) 的码元周期为 Ta伪随机码 c(t) 的码片周期为 Tc且 Tc 远小于 Ta扩频因子 N Ta / Tc。双极性表示下模二加等价于乘法信息比特 1 映射为 1、0 映射为 -1PN 码也用同样的映射两路序列逐位相乘就完成了扩频。原文那句「1 用某个序列代替、0 用另一个序列代替」说的正是这个操作在波形层的样子——每个信息比特位置上放的不是一个方波而是一整段正负跳变的码片图案。关键在于能量账本。扩频前后每个比特的能量 Eb 没变仍是 Eb Ps × TaPs 为平均功率但信号占用的带宽从 Bs 变成了约 N × Bs。功率谱密度等于能量除以带宽带宽涨 N 倍谱密度就掉 N 倍。所以扩频确实是在「摊薄」摊薄的是功率谱密度不是总能量。这一点必须先钉死否则后面会误以为解扩能造出能量来。能量一直躺在那段宽频带里只是被摊得低于噪声底单看任何一个窄带滤波器都捞不出来。接收端要做的不是放大而是把散落在 N 个码片上的能量重新聚回一个符号周期内——这就是相关运算的全部工作。顺便说清采样层面的对应关系。在仿真里一个信息符号被展开成 L 个采样点每个 PN 码片又被复制成若干采样点L 就等于「码片数 × 每码片采样数」。后面第 5 章会看到这两个倍数没对齐时误码率曲线会莫名其妙地抖根子就在这里。2.2 相关器为什么等价于窄带滤波器干扰被扩频、信号被压缩接收端把收到的信号再乘一次本地 PN 码。对信号项而言a(t)·c(t)·c(t) a(t)·c²(t) a(t)因为 c(t) 只取 ±1平方恒等于 1。乘两次相互抵消宽带扩频信号被压回原来的窄带。这一步在频域上相当于把信号频谱从 N·Bs 重新搬回 Bs能量密度回升 N 倍。对干扰项而言情况完全相反。设干扰为 i(t)与 PN 码不相关乘完之后变成 i(t)·c(t)。两个不相关序列相乘频谱等于各自频谱的卷积结果是干扰的带宽被展宽了大约 N·Bs落进信号通带的那一小段功率只剩约 1/N。原文说「相关解扩对干扰相当于一次扩频」指的就是这个镜像过程——信号被压缩干扰被摊开一压一摊之间信噪比抬高了 N 倍。于是处理增益的定义顺理成章G 10 lg(N) dB。扩频因子 64 对应约 18 dB扩频因子 400 对应约 26 dB。原文提到「处理增益达到 10 dB 以上」那是保守说法只要码长够几十 dB 都能拿到代价是带宽。这条增益不是白拿的它用带宽换信噪比带宽就是扩频系统的硬通货。多径干扰为什么也能被吃掉用同一套逻辑就能解释。不同路径到达的信号是同一 PN 码的不同延迟副本只要延迟超过一个码片周期 Tc本地码与其相关值就掉到接近零几路多径在相关器输出端被分成几个互不重叠的尖峰。想合成一路就把它们按时间对齐后相加想要哪一路就只在一个码片窗口内积分。抗多径能力本质上来自 PN 码尖锐的自相关峰而不来自任何滤波电路。2.3 从 10lg(N) 到仿真里的 400 点累加增益在代码的哪一行兑现理论归理论增益在代码里究竟落在哪一行答案是判决器前面那段看起来最笨的累加循环。% 相关解扩后的积分清零判决一个信息符号对应 400 个采样点 xo zeros(size(x)); % 预分配判决输出长度等于信源符号数 for i 1:100 % 逐个信息符号做判决 z2 0; % 积分器清零积分清零这个名字的由来 for j (1(i-1)*400):(i*400) z2 z2 zl(j); % 400 点累加等价于与矩形窗做相关 end if z2 0 % 相关值非负判为 1 xo(i) 1; else xo(i) -1; % 相关值为负判为 -1 end end这段代码里 400 就是每个符号的采样点数 L循环变量 i 走 100 次对应 100 个信息符号。内层 400 次加法完成的是矩形窗匹配滤波数学上等于把解扩后的波形与一个宽度为 Ta 的矩形脉冲做相关。判决门限取 0是因为双极性映射下「1 与 -1 等概」的中点在零只有这样才能让误判概率最小。真正兑现处理增益的动作发生在加法之前——jk_code rs .* k_code这一行把接收信号与本地扩频码逐点相乘把 400 个采样点上散落的信号能量重新同相相加而噪声因为与本地码不相关相加后按 √400 增长信号按 400 增长幅度信噪比提升 √400 20 倍功率信噪比提升 400 倍。这就是 10 lg(400) ≈ 26 dB 在代码里的落点。有一个容易忽略的细节这段累加只统计了 100 个符号z1(a) z/100的误码率分辨率被钉死在 0.01。曲线画到 10⁻⁴ 的坐标轴上时会突然「掉不下去」不是系统性能到顶了是统计样本用完了。这个坑留到第 5 章细说。3. PN 码生成m 序列的移位寄存器为什么必须「抽头」3.1 线性反馈移位寄存器的状态转移与周期 2^n-1扩频码决定了抗干扰能力的上限自相关峰越尖、互相关底越低接收端分辨信号与干扰的本事越大。仿真里最常打交道的 m 序列由线性反馈移位寄存器LFSR产生n 级寄存器最多能枚举 2ⁿ 个状态其中一个全零状态是死锁——各级都输出 0反馈回来还是 0永远出不来。所以可用状态是 2ⁿ - 1 个m 序列的最大周期也就是 2ⁿ - 1。stage 3; % 移位寄存器级数 n决定码长 2^n - 1 ptap1 [1 3]; % 反馈抽头位置等价于选择一个本原多项式 regi1 [1 1 1]; % 初始状态绝不能全零否则寄存器死锁 code mseq(stage, ptap1, regi1, user); % 生成周期 7 的 m 序列三个参数各司其职stage定码长ptap1定反馈结构regi1定从哪个状态起跑。user参数用于在多用户场景下取不同相位的副本本质上是给同一序列换一个起始偏移。这里有个容易被当成玄学的地方为什么偏偏是 [1 3] 和 [2 3] 这两组抽头能用n 3 时8 个状态去掉全零剩 7 个想让寄存器跑满 7 个状态再回头反馈多项式必须是本原多项式。3 级里满足条件的只有 x³ x 1 和 x³ x² 1 两个对应抽头 [1 3] 和 [2 3]。换成 [1 2] 或 [2 3] 之外的组合周期会缩短成 3 或 1码长不够处理增益直接打折。3.2 抽头选择决定本原多项式为什么 stage3 只有两组可用把移位过程摊开看会更清楚。初始状态 111抽头 [1 3] 意思是把第 1 级和第 3 级异或后反馈到输入端每来一个时钟寄存器整体右移一位、最高位填入反馈值。111 → 011 → 001 → 100 → 010 → 101 → 110 → 111七个状态走完一圈第七步回到起点周期正好 7。失败的情形长什么样如果抽头选成 [1 2]异或的是第 1、2 级寄存器会掉进 111 → 011 → 101 → 010 → 001 → 100 → 110 → 111 之外的短循环序列提前重复等效码长缩水。仿真里表现为扩频增益对不上理论值误码率曲线比预期差好几个 dB而且换随机种子也不会改善——这是结构性的不是运气问题。抽头还有一个隐含约束末级必须参与反馈。如果抽头集合里不含第 n 级末级的状态永远影响不到输入端它就成了一个纯延迟单元实际有效级数变少周期自然不够。所以 [1 2] 在 n 3 时必错[1 3]、[2 3] 才成立。工程上不会手工枚举这些组合。常见做法是预先查表给定级数 n查一张本原多项式表把对应抽头位置直接填进ptap参数。n 到十几级时表还很短n 再大就要靠搜索算法验证周期了。3.3 GOLD 码与正交码族地址数从 2^n-1 到平方级的跃迁单个 m 序列的自相关很好互相关却不理想。码分多址要求几十路用户在同一频段上共存彼此之间的互相关底必须压得足够低否则一路用户对另一路就是强干扰。m 序列的互相关峰值随相位差剧烈起伏不能直接当多址码用。GOLD 码解决的正是这个问题。取一对码长相等、码速率相同的 m 序列优选对逐位模二加每改变两序列的相对位移就得到一个新的 GOLD 序列一共能凑出 2ⁿ 1 个。互相关峰值被压到一个可控的三值集合上虽然比 m 序列的自相关峰差一点但地址数从 2ⁿ - 1 跃升到 2ⁿ 1且互相关特性稳定可预测。m1 mseq(stage, ptap1, regi1); % 优选对中的第一个 m 序列 m2 mseq(stage, ptap2, regi2); % 优选对中的第二个 m 序列 code goldseq(m1, m2, user); % 模二加生成第 user 个 GOLD 码goldseq的第三个参数user就是地址索引不同用户拿到序列循环移位后的不同副本靠位移区分身份。这也解释了码分多址的基本运作方式所有用户同时同频发射接收端用本用户的码做相关其他用户的信号因为互相关低而被压成噪声底。另一条路线是正交码。WALSH 函数是一组非正弦的完备正交函数系两两互相关严格为零在多址场景里天然免疫用户间干扰代价是正交性对同步极度敏感——延迟偏了半个码片正交性立刻崩塌。OVSF 码则把正交性和可变扩频因子结合起来允许不同速率的业务在同一时隙上共存速率低的用长码、速率高的用短码只要不违背码树上的父子关系就能保持正交。四类码的取舍可以这么记m 序列自相关最尖、适合单用户测距与同步GOLD 码互相关可控、适合多址WALSH 码正交完美但对同步苛刻OVSF 码灵活但码资源管理复杂。仿真里默认走 GOLD 或 m 序列是因为它们对定时误差的容忍度更适合教学验证。4. 仿真链路的实现细节BPSK 调制、载波搬移与相干解调4.1 从 x 到 d符号复制与采样率的对账信源产生 100 个 ±1 的符号每个符号要展开成 400 个采样点才能和后续的扩频码、载波逐点相乘。代码里用了一段笨但直观的循环完成这件事d((1(i-1)*400):i*400) x(i)把第 i 个符号原样填进对应的 400 个位置。等价写法是d kron(x, ones(1,400))用 Kronecker 积一次搞定运行速度快一个数量级做长序列仿真时差别很明显。采样间隔 dt Tb / L 是整个仿真时间轴的刻度。Tb 1/Rb 4L 400所以 dt 0.01。后面所有波形的时间轴 t、载波相位 2π·fc·t、滤波器的归一化截止频率全都挂在这个 dt 上。dt 一旦改动滤波器系数、载波周期、判决窗口长度都要跟着重算这是仿真代码耦合最紧的一处。载波调制那一步把所有能量搬到射频频段s b .* cos(2*pi*fc*td)。这里用的是双边带抑制载波没有直流分量接收端必须靠本地载波相干解调不能包络检波。BPSK 的相位只有 0 和 π 两种映射回比特就是 sign 函数的正负。4.2 载波搬移与欠采样的隐患fc 4e4、fs 4000这一组参数放在一起需要停下来看一眼。按奈奎斯特准则采样率至少要达到信号最高频率的两倍才能无失真重建4 kHz 的采样率对应 40 kHz 的载波差了整整二十倍。这不是笔误就是参数没有联动更新。后果不是简单地「波形画得难看」。数字域里 cos(2π·fc·n·dt) 的自变量每步增量是 fc·dt 400而余弦函数以 2π 为周期400 是 2π 的整数倍附近实际算出来的相位几乎每步都回到同一个等效角度。载波在数值上退化成了常数调制没把频谱搬走解调乘的又是同一个常数整条射频链路在数值层面等于被短路了。这时候误码率当然好看因为噪声只在基带上加了一次扩频和解扩的增益评估全被架空。正确做法有两条路。要么把 fc 降到 fs 的十分之一到五分之一之间比如 fs 4000 时取 fc 400要么把 fs 提上去至少 8×10⁴让每个载波周期有 2 个以上采样点。前者跑得快适合验证算法逻辑后者更接近真实射频链路但循环次数涨二十倍仿真时间也涨二十倍。做参数扫描时通常选前者做波形细节核对时选后者。4.3 解扩、低通与判决三级流水接收端整条链路可以拆成三段加噪、相干解调、解扩滤波判决。每一段的参数口径都不一样混用是误码率对不上理论值的头号原因。r awgn(s, SNR(10), measured); % 按实测信号功率加高斯白噪声 rs r .* cos(2*pi*fc*td); % 相干解调乘同频同相本地载波 jk_code rs .* k_code; % 解扩乘与发端同步的扩频码 [f, rf] T2F(td, jk_code); % 时域转频域 [tl, l] lpf(f, rf, fc/10000); % 低通滤掉倍频分量与带外噪声awgn的第三个参数measured表示先测量输入信号的实际功率再按第二个参数给定的信噪比注入噪声省去了手工计算信号功率的步骤。这个口子很方便也很容易踩坑它给的 SNR 是「信号总功率与噪声总功率之比」不是通信理论里常用的 Eb/N0两者差一个 10 lg(fs/2/Rb) 的换算因子。直接拿这个 SNR 去对理论误码率曲线永远对不上。相干解调那一行乘完本地载波后频谱里同时存在基带分量和二倍载频分量低通滤波器负责把后者扔掉。lpf的第三个参数fc/10000是归一化截止频率这个 10000 不是随便写的它和采样率、FFT 点数绑在一起改了任何一项都得重算。解扩那一步要求本地码与发端码严格同步——同码型、同相位、同速率。仿真里因为收发共用同一个k_code变量同步是白送的真实系统里这一步要靠捕获与跟踪环路是整个接收机最复杂的部分。仿真代码把最难的部分跳过去了这也是为什么仿真曲线总是比实测漂亮。5. 常见问题排查这套链路最容易翻车的五处5.1 信号层的三处翻车采样率、码周期与噪声口径现象一解调后的波形和发端几乎重合误码率低得反常。原因在 4.2 已经点破——载波频率高于采样率的一半数字域里载波退化成常数射频搬移在数值上根本没发生。解决方法是让 fs 至少达到 2fc工程上取 4 到 10 倍留余量或者直接把 fc 压到 fs 的十分之一以下。现象二判决值在门限附近抖动误码率曲线不平滑换随机种子结果跳变很大。根子是扩频码周期与符号长度没对齐。PN 码长 7每个码片复制 4 份得到 28 个采样点而一个符号占 400 个采样点400 / 28 14.28不是整数最后一个码片被硬生生截断。截断处本地码与发端码失配相关值少了那一小段的贡献判决就飘。解决方法是让 L 取「码长 × 每码片采样数」的整数倍比如 7 × 4 × 14 392或者干脆把码长改成能整除 400 的数。现象三加不同信噪比扫出来的误码率曲线整体平移形状对但位置不对。这是awgn的 SNR 口径问题。measured模式下参数是信号功率与噪声功率之比而扩频系统的抗干扰能力应该用 Eb/N0 衡量换算关系是 Eb/N0 SNR 10 lg(fs / (2·Rb))。少了这一步换算曲线会整体偏移十几 dB看着像系统性能差其实是坐标系没对齐。5.2 统计层的两处翻车判决门限与误码率分辨率现象四误码率曲线落到 0.01 就断崖式结束怎么加大信噪比都下不去。不是系统到顶是统计样本用完了。z1(a) z/100里分母是 100 个符号能表示的最小非零误码率就是 1/100 0.01再低只能记成 0画在半对数坐标上就是一条断掉的线。解决方法很简单把符号数 M 从 100 提到 10⁵ 以上或者改成统计错误比特数而非错误符号数让分辨率跟着样本量一起涨。代价是循环次数增加运行时间线性增长做参数扫描时要权衡。现象五码序列输出全零或者判决结果里冒出 0 值。两个不同的坑撞在一起了。码序列全零通常是把regi1初始化成了 [0 0 0]LFSR 掉进死锁状态反馈永远输出 0扩频码变成直流扩频增益归零。判决出 0 则是sign函数的行为——sign(0)返回 0而双极性系统里只应该有 ±1。解法是把初始状态设成非全零判决改成显式的if z2 0 ... else ...避免 sign 在零点上的歧义。这五条里前三条会让人怀疑算法后两条会让人怀疑理论实际上全是实现细节。仿真翻车十次有九次不在公式而在参数口径。6. 从仿真到工程处理增益、误码率曲线与三个取舍把曲线跑出来只是开始真正要判断的是取舍。扩频因子 N 决定处理增益也决定带宽占用两者严格成正比没有免费午餐。下表是按 10 lg(N) 算出来的对应关系可以拿来快速估参数扩频因子 N处理增益相对带宽占用典型用途78.5 dB7×短码验证、教学演示6418.1 dB64×中等抗干扰链路40026.0 dB400×强干扰环境、低信噪比接收409536.1 dB4095×远距离扩频、测距第一个取舍是增益与带宽。增益每涨 3 dB带宽翻一倍。频谱资源紧张时不可能无限加码长这时要么靠信道编码补增益要么靠天线增益补链路预算。仿真里改一个数字就行工程上改的是射频前端和频谱许可。第二个取舍是码长与同步时间。捕获一个长度为 N 的码串行搜索最坏要扫过 N 个相位每个相位积分一个符号周期总捕获时间正比于 N·Ta。码越长增益越高冷启动越慢。工程上常用两级方案先用短码快速捕获再用长码精跟踪。第三个取舍是仿真精度与运行时间。符号数从 100 提到 10⁵误码率分辨率从 0.01 降到 10⁻⁵但循环次数涨一千倍。我一般会先拿 100 个符号跑通链路看波形确认判决正确后再把符号数拉到 10⁵ 出曲线两轮跑完比一轮跑到底省时间。验证方法上最省事的一招是拿无噪声的理想信道跑一遍误码率必须是零再拿纯噪声信道跑一遍误码率必须接近 0.5。这两头对上了中间的曲线才有意义。如果无噪声都有误码问题一定在同步或参数对齐如果纯噪声误码率明显低于 0.5说明判决器有偏置多半是门限没取在零。早些年我做这类链路仿真习惯是先把所有参数写成一坨常量改一个忘一个最后曲线对不上理论值查了两天才发现是滤波器截止频率还挂在旧的采样率上。从那以后我每次动采样率或载波频率都会强制把 dt、滤波器归一化频率、判决窗口长度这三个量一起重算一遍写在代码最上面做联动。这套习惯让我少走了不少弯路希望帮到你。这份 MATLAB 仿真设计里从 m 序列生成、BPSK 调制到相关解扩和误码率统计的完整链路都在代码虽然带着教学代码特有的松散但每一段都能对上前面讲的机制拿它改参数、加信道、换码型都是现成的起点。本文还有配套的精品资源点击获取