简介:这份资源是一份面向通信工程、电子信息类专业学生及任课教师的MATLAB课程设计报告,围绕直接序列扩频通信系统的建模与仿真展开,用于完成《扩频通信原理》课程设计、理解扩频通信原理或作为仿真实训参考。报告系统梳理了直扩系统的组成与工作流程,涵盖扩频调制、BPSK调制、扩频解扩与信息解调四个功能模块,并对各模块的实现方式逐一分析:扩频码采用m序列实现频谱展宽,调制选用二相相移键控BPSK,载波提取与解调采用科斯塔斯环,解扩采用相关解扩方式,同时说明各方案的选型依据与性能差异。资源还给出信息码生成、m序列产生、扩频调制、BPSK调制、加噪、解调与解扩等环节的MATLAB源代码及调试过程,并配有信源码、伪随机码、复合码、已调波、加噪波形与解调波形的绘制结果,便于读者对照代码复现仿真、分析波形变化与验证解调效果。整份资料为1个doc文档,压缩包约327KB,已有322人学习,适合需要快速搭建直扩通信仿真框架并撰写课程设计报告的学生参考借鉴。
1. 直接序列扩频为什么成了通信课程设计的默认选题
答辩现场最容易被问住的一句话是:你的处理增益算出来是 14.9 dB,那干扰功率比信号高多少时误码率会掉到 10⁻³?很多人 PCA 图上频谱摊平了、误码率曲线也画了,但答不上这个数,因为仿真里从来没把干信比当成独立变量扫过。直接序列扩频(DSSS)要讲的正是这件事:拿一条速率远高于信息速率的伪随机码去乘基带符号,把窄带信号的能量摊到宽带上,接收端用同一条码做相关,有用信号重新聚拢、干扰被二次摊开,于是同样的干扰功率下判决量信噪比高出一截。这篇内容按课程设计报告的实际交付顺序走:先把扩频因子、处理增益、干扰容限三个量的换算关系钉死,再在 MATLAB 里搭一条可复现的 BPSK 基带链路,然后给出报告里必须有的三组实验图和一条能自检的数据链。适合通信工程、电子信息类做课程设计或毕设起步的读者,也适合手上有 MATLAB 但没系统写过扩频仿真的人。
2. 直接序列扩频的数学底座与 MATLAB 参数映射
2.1 扩频因子、处理增益与干扰容限的换算
先把三个量分清楚,后面所有代码参数都从这里推。
扩频因子 SF 是一个信息比特被多少个码片(chip)表示,工程上取伪随机码周期或它的整数倍。处理增益就是能量摊开的倍数,用分贝写:
Gp = 10·log₁₀(SF)
取 SF = 31,Gp ≈ 14.9 dB。干扰容限是说在保证某个误码率的前提下,接收端最多能容忍多大的干扰,公式是:
Mj = Gp − [Lsys + (S/N)o]
Lsys 是实现损耗(滤波器不理想、同步偏差、量化噪声等),(S/N)o 是解扩后达到目标误码率所需的信噪比。举个能直接写进报告的数:SF = 31、Lsys 取 3 dB、要求 BPSK 在 10⁻³ 误码率下需要约 6.8 dB,则 Mj ≈ 14.9 − 3 − 6.8 = 5.1 dB。这句话的含义是,干扰功率只要不超过信号功率 5.1 dB,系统还能维持 10⁻³。答辩时把这三个数写在黑板上,比念一遍定义有用得多。
2.2 m 序列的 LFSR 写法与工具箱写法怎么选
PN 码用 m 序列最省事,周期 2ⁿ−1,平衡性和自相关都好。MATLAB 里两条路:
- Communications Toolbox 的
comm.PNSequence,一行配置搞定,缺点是换了机器没装这个工具箱就跑不起来; - 自己写线性反馈移位寄存器(LFSR),二十行代码,任何版本都能跑,而且报告里能画出寄存器结构图,显得你是真懂。
课程设计我一般选第二种,因为答辩老师很可能会问「你这个码周期多少、由哪个本原多项式生成」。
function pn = mseq(nstage, taps, seed) % mseq 用线性反馈移位寄存器生成 m 序列 % nstage : 寄存器级数,序列周期为 2^nstage-1 % taps : 反馈抽头位置,需对应一个本原多项式 % seed : 初始状态,1xN 的 0/1 向量,不能全零 reg = seed(:).'; L = 2^nstage - 1; % 序列周期 pn = zeros(1, L); for k = 1:L pn(k) = reg(end); % 从最低位输出 fb = mod(sum(reg(taps)), 2); % 抽头异或作为反馈 reg = [fb, reg(1:end-1)]; % 右移一位并写入反馈 end pn = 1 - 2*pn; % 0/1 映射为 +1/-1 end逻辑说明:每一轮先把当前最低位取出来当输出,再把指定抽头位置的比特做模二加得到反馈位,整体右移后把反馈位补到最高位。pn = 1 - 2*pn把 0/1 变成 ±1,这样扩频就是纯乘法,不用做异或和电平搬移。
参数说明:nstage决定周期,5 级对应 31,7 级对应 127;taps必须对应本原多项式,5 级常用 x⁵+x²+1,抽头取第 2 和第 5 位,写成[2 5];seed任意非零向量都行,但会出现循环移位,报告里要写清楚用了哪个初相。
注意:seed 全零会让 LFSR 锁死在零状态,输出全是 +1,扩频增益直接消失,这种 bug 在误码率曲线上表现为「曲线异常漂亮」,反而更难发现。
2.3 码片率、采样率、比特率的参数表
仿真参数不能随手填,必须自洽。假设信息速率 Rb = 1 kbps,SF = 31,过采样 sps = 8,则:
| 参数 | 符号 | 计算方式 | 取值 |
|---|---|---|---|
| 信息速率 | Rb | 给定 | 1 kbps |
| 码片速率 | Rc | Rb × SF | 31 kchip/s |
| 每比特采样点数 | Ns | SF × sps | 248 |
| 仿真采样率 | Fs | Rc × sps | 248 kHz |
| 干扰频率落点 | fj | 落在 0 到 Fs/2 内 | 归一化 0.13 |
这张表的用处是防止两个常见错误:一是噪声功率按错误的带宽算,二是把单音干扰放在扩频带宽之外,结果「抗干扰效果特别好」,其实是干扰根本没进来。干扰频率归一化到采样率之后必须落在主瓣内,否则频谱图上一眼就看出来对不上。
3. 在 MATLAB 里搭通 DSSS 基带链路
3.1 发射端:BPSK 映射、扩频与过采样
基带等效链路的好处是不用处理载波,频谱搬移的结论照样成立,报告里画图也干净。发射端三步:比特映射成 ±1、逐比特乘以 PN 码、码片级过采样。
Nbit = 2000; % 信息比特数 SF = 31; % 扩频因子 = m 序列周期 sps = 8; % 每码片采样点数 rng(20240501); % 固定随机种子,保证结果可复现 pn = mseq(5, [2 5], [1 0 0 0 0]); % 周期 31 的 PN 码 pn = pn(mod(0:Nbit*SF-1, SF) + 1); % 按周期截断到 Nbit*SF 长度 bits = randi([0 1], 1, Nbit); syms = 1 - 2*bits; % 0->+1, 1->-1 tx = reshape(repmat(syms, SF, 1), 1, []) .* pn; % 逐比特扩频 txu = reshape(repmat(tx, sps, 1), 1, []); % 码片级过采样逻辑说明:repmat(syms, SF, 1)把每个符号复制 SF 份排成 SF×Nbit 的矩阵,reshape(...,1,[])按列拉直成一行,得到的是「每个比特连续重复 SF 次」的波形,与pn的排列方式完全一致,两者点乘就是扩频。第二段repmat(tx, sps, 1)做的是码片级保持,用来模拟矩形码片成形。
参数说明:Nbit越大误码率曲线越平滑,2000 在 10⁻³ 量级已经够用,要看到 10⁻⁴ 建议上到 10⁵;rng一定要设,否则每次跑出来的曲线点会抖,报告里两张图对不上不好解释;sps取 8 是折中,太小码片波形看不出形状,太大内存翻倍。
3.2 信道建模:AWGN 加单音干扰
抗干扰验证的核心是有干扰,不能只加高斯白噪声。信道里同时叠两样东西:按 Eb/N0 定标的热噪声,以及按干信比 JSR 定标的单音。
N = numel(txu); n = 0:N-1; fj = 0.13; % 归一化干扰频率,落在主瓣内 JSRdB = 10; % 干信比:干扰功率 / 信号功率 EbN0dB = 0:2:10; ber = zeros(size(EbN0dB)); for i = 1:numel(EbN0dB) EbN0 = 10^(EbN0dB(i)/10); sigma = sqrt(SF*sps/(2*EbN0)); % 每采样点噪声标准差 jam = sqrt(2*10^(JSRdB/10)) * cos(2*pi*fj*n); % 实单音,功率为 JSR 倍 rx = txu + sigma*randn(1,N) + jam; rxc = mean(reshape(rx, sps, []), 1); % 码片级积分 dec = rxc .* pn; % 解扩 stat = sum(reshape(dec, SF, []), 1); % 比特级累加 hat = stat < 0; ber(i) = mean(hat ~= bits); end逻辑说明:噪声标准差按每比特总能量推。信号每采样点幅度为 ±1、功率为 1,一个比特含 SF×sps 个采样点,故 Eb = SF×sps。实信号下 N0 = 2σ²,由 Eb/N0 反解出 σ = √(SF·sps/(2·EbN0))。单音用余弦,幅度取 √(2Pj) 才能使功率等于 Pj,Pj = 10^(JSRdB/10) 倍信号功率。接收端先按 sps 做块平均降回码片级,再乘 PN 解扩,最后按 SF 累加得到判决量。
参数说明:JSRdB是这根链路的自变量,报告里要扫 0 到 20 dB;fj换成 0.3 或 0.45 可以观察干扰频率靠近带边时的差异;sigma里除的是EbN0而不是EbN0*SF,这是最容易写错的地方,写错会让曲线整体平移十几 dB。
3.3 接收端相干解扩与判决门限
解扩之所以能压干扰,是因为乘上 PN 之后有用信号变回常数,而干扰被二次调制成了宽带噪声,累加器只取一个比特的能量,落在带内的干扰分量就只剩 1/SF。判决门限取 0,因为 BPSK 映射是对称的。
如果要做同步实验,把dec = rxc .* pn换成dec = rxc .* circshift(pn, [0, offset]),offset就是码相位偏差,单位是码片。这是第 4 章相位扫描的基础。
注意:相关累加前不要对
rxc做归一化,判决门限依赖绝对幅度。归一化之后门限 0 仍然成立,但如果你后面想看软判决量分布,幅度信息就丢了。
3.4 一次完整仿真跑出来的结果怎么看
跑完上面的循环,ber数组在无干扰(JSRdB 设成 -40 相当于没有干扰)时应该贴近 BPSK 理论曲线:0 dB 约 0.078、4 dB 约 0.012、8 dB 约 2×10⁻³。加了 10 dB 干信比之后,曲线整体右移,但不会崩掉,因为处理增益 14.9 dB 把干扰压下去了。
对照实验是必做的:把SF改成 1(等价于不扩频),其余参数不动,你会发现同样的 JSR 下误码率从 10⁻² 量级掉到 0.3 附近。这一组对比才是「直接序列扩频有用」的直接证据,比任何文字描述都有说服力。
4. 报告里必须出现的三组实验
4.1 扩频前后功率谱对比图
报告里的频谱图不能只画一条扩频后的曲线,要和无扩频的并排。用pwelch估功率谱密度,两条曲线叠在同一张图上。
txn = reshape(repmat(syms, SF*sps, 1), 1, []); % 未扩频参考:比特持续整段时间 [Px, f] = pwelch(txu, hann(256), 128, 1024, 1); [Pn, ~] = pwelch(txn, hann(256), 128, 1024, 1); figure; plot(f, 10*log10(Px), 'LineWidth', 1.2); hold on; plot(f, 10*log10(Pn), '--', 'LineWidth', 1.2); grid on; xlabel('归一化频率'); ylabel('功率谱密度 (dB)'); legend('扩频后', '未扩频');逻辑说明:pwelch的第三个参数是重叠点数,取窗口长度的一半;第四个参数是 FFT 点数,决定频率分辨率;最后一个是采样率,这里用归一化 1。两条曲线的总能量相同,但扩频后的主瓣被展宽、峰值下降约 10·log₁₀(SF) ≈ 14.9 dB,这就是处理增益在频域的直接体现。
参数说明:窗口取 256 是因为txu长度在十万量级,窗口太短谱线毛刺多,太长会掩盖码片结构。如果报告里图糊在一起,把hann(256)改成hann(512)试试,或者只画 0 到 0.5 的频率区间。用matlab画图的常规套路还可以加xlim([0 0.5])、ylim([-60 0])把无关区域裁掉。
4.2 误码率随干信比变化的曲线
把 Eb/N0 固定在一个值(比如 8 dB),扫 JSR 从 0 到 20 dB,观察误码率什么时候开始抬起来。这条曲线和 2.1 节算出的干扰容限应该能对上。
| JSR (dB) | 预期 BER(SF=31) | 预期 BER(SF=1) |
|---|---|---|
| 0 | 约 2×10⁻³ | 约 0.08 |
| 6 | 约 5×10⁻³ | 约 0.18 |
| 12 | 约 2×10⁻² | 约 0.30 |
| 18 | 约 0.09 | 约 0.38 |
这张表是自检用的:如果你的 SF=31 曲线在 12 dB 就已经到 0.1,说明链路里某处功率定标错了,回头检查sigma那一行。SF=1 的曲线随 JSR 上升反而慢,是因为未扩频时噪声本来就主导,干扰加进来边际影响小,这也解释了为什么两条曲线会在某个点交叉。
4.3 码相位偏差扫描与同步容限
相干解扩要求本地 PN 码和收到的码严格对齐,偏一个码片性能就掉。把偏差从 −15 扫到 15,画出判决量均值曲线,能看到一个三角形的主峰。
offsets = -15:15; % 码相位偏差,单位:码片 peak = zeros(size(offsets)); rx0 = txu + 0.3*randn(1, numel(txu)); % 固定一个低噪场景 rxc = mean(reshape(rx0, sps, []), 1); for k = 1:numel(offsets) dec = rxc .* circshift(pn, [0, offsets(k)]); stat = sum(reshape(dec, SF, []), 1); peak(k) = mean(abs(stat)); % 判决量平均幅度 end plot(offsets, peak, 'o-'); grid on; xlabel('码相位偏差 (chip)'); ylabel('平均判决量幅度');逻辑说明:circshift把本地码整体平移,模拟本地码与接收码的相位差。偏差为 0 时相关峰最高,偏差绝对值超过 1 个码片之后,每个比特内落在正确符号上的码片比例线性下降,判决量幅度随之线性衰减,形成三角峰。峰宽就是同步容限,用延迟锁定环可以在 ±1 码片内维持锁定。
参数说明:offsets的步长取 1 码片,要看更细的对齐过程可以改成 0.25 码片步长,但那时需要码片级插值,circshift就不够了。-15:15这个写法本质就是linspace(-15,15,31)的等距采样,如果你习惯用linspace,两者结果完全一致,只是linspace更明确地表达「等分 31 个点」。
5. 让仿真跑得又快又稳的进阶做法
5.1 向量化与 parfor 加速蒙特卡洛
2000 个比特、10 个信噪比点,上面那段循环几秒钟就跑完。但如果报告要求误码率下探到 10⁻⁵,比特数得加到 10⁶,嵌套循环就会变成几分钟。两条优化路:
一是把噪声生成向量化,一次生成所有信噪比点的噪声矩阵,用隐式扩展代替内层循环。MATLAB 从 R2016b 起支持txu + sigma(:).*randn(numel(sigma), N)这种写法,直接把信噪比维度提到矩阵行上,一次算出所有点的判决量。
二是对信噪比循环用parfor:
parfor i = 1:numel(EbN0dB) EbN0 = 10^(EbN0dB(i)/10); sigma = sqrt(SF*sps/(2*EbN0)); rx = txu + sigma*randn(1,N) + jam; rxc = mean(reshape(rx, sps, []), 1); stat = sum(reshape(rxc .* pn, SF, []), 1); ber(i) = mean((stat < 0) ~= bits); endparfor的前提是循环体之间没有依赖,这里每个信噪比点独立,满足条件。用之前记得parpool起池,否则第一次运行会有十几秒的启动开销,反而不如for。另一个坑是parfor里不能对共享变量做累加,ber(i)这种按索引写回是允许的。
5.2 报告数据自检清单
仿真跑完之后,用下面这张表过一遍,能挡掉大部分「曲线好看但结论站不住」的情况。
| 检查项 | 期望现象 | 不达标时先查哪 |
|---|---|---|
| 无干扰 BER 曲线 | 与 BPSK 理论曲线重合 | sigma公式里的 Eb 定义 |
| 扩频前后频谱峰差 | 约 10·log₁₀(SF) dB | pwelch的窗口长度、归一化方式 |
| JSR 扫描交叉点 | SF=31 与 SF=1 曲线有交叉 | 干扰频率是否落在主瓣内 |
| 相位扫描三角峰 | 峰宽约 ±1 码片 | PN 码周期与比特边界是否对齐 |
| 换随机种子重跑 | BER 波动在统计误差内 | 比特数是否够,10⁻³ 至少 10⁴ 比特 |
最后一步可以把ber、EbN0dB、JSRdB一起save('dsss_result.mat'),报告里的图全部从这份数据重绘,改一次扩频因子只需要重跑一次脚本,不用重画图。答辩前一晚把 SF 从 31 改成 63 再跑一遍,把两组干扰容限的数并排写在报告结论页上,比单组数据更能说明处理增益和抗干扰能力之间的定量关系。
本文还有配套的精品资源,点击获取