news 2026/11/6 4:19:38

MATLAB DSSS直接序列扩频仿真链路补全与BER调试

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MATLAB DSSS直接序列扩频仿真链路补全与BER调试

简介:面向学习通信原理、扩频通信与MATLAB仿真的高校学生及工程入门者,这份文档围绕直接序列扩频系统的建模、解扩与抗干扰性能评估展开,可帮助读者理解DSSS从信源、PN码产生、BPSK调制到接收端相关解调的完整链路。压缩包仅含1个doc文件,大小约320KB,内容以实验原理、系统组成、伪随机码类型和仿真参数说明为主,并附部分程序代码与结果展示,便于对照m序列、GOLD码、WALSH码、OVSF码等知识点梳理实现思路。文档给出2kbps信源速率、扩频因子64、BPSK调制及高斯信道下的误码率随信噪比变化分析,适合课程设计、实验报告或扩频通信入门仿真参考。已有143人学习,可作为理解处理增益与抗干扰能力的轻量资料。

1. 课程设计里翻到一份 DSSS 仿真,别急着归档

课程设计资料里常能翻到这种文件:标题写着「基于 MATLAB 的直接序列扩频通信系统仿真设计」,打开八页纸,前三页讲扩频原理、m 序列、处理增益,后五页是一段跑不起来的代码加几张截图。多数人扫一眼就归档了。但把缺的函数补上、把参数调自洽,会发现它把 DSSS 从信源、扩频、BPSK 载波调制、AWGN 信道一路走到解扩判决、误码率统计的整条链路都搭出来了,只是被排版和缺失的 m 文件卡住。这份资源值钱的地方不在理论——教材里都有——而在于骨架完整、每一段都能单独拿出来验证。适合刚学通信原理要交课设的人,也适合想拿扩频做抗干扰验证底座的开发者。

2. 链路拆解:一条比特从信源走到误码率的六段路

先不碰代码,把信号流向捋清楚。这套仿真本质上是一条开环链路,没有同步模块、没有信道编码、没有射频前端建模,但它覆盖了 DSSS 最核心的六个环节。理解这六段各自的输入输出,后面看代码就不会迷路,改参数时也知道改哪一处会影响哪一段。

2.1 六个环节各自解决什么问题

第一段是信源。产生 0/1 随机比特,再映射成 ±1 的双极性符号。这个映射不是随手写的,双极性之后「模二加」和「乘法」等价,扩频可以直接写成一次逐点相乘,省掉一堆异或操作。

第二段是扩频。把一个比特乘上一串高码率的 PN 码片,时域上比特被切碎成 chip_per_bit 个码片,频域上带宽被展宽同样的倍数。文档里说的「将信源与 PN 码模二加」,落到代码就是s_bpsk = d .* k_code。

第三段是载波调制。扩频后的基带序列乘上cos(2*pi*fc*t),频谱搬移到载频附近。这一步在仿真里主要是为了让「相干解调」这个动作有地方落脚,真实系统里还有上变频、功放、天线,这里全用一个乘法代替。

第四段是信道。原文用awgn加高斯白噪声,'measured'参数表示按输入信号的实际功率来定标信噪比,这样你不用手算噪声方差。这一段的可替换性最强,后面做二次开发基本都在这里动手。

第五段是解扩。接收信号先乘同频同相的载波做相干解调,再乘本地 PN 码。信号因为码型匹配重新变回窄带,噪声和干扰因为不匹配被二次扩频、功率谱密度被压低,低通滤波器一刀切下去,带外的东西就没了。这就是处理增益的来源。

第六段是判决。每个比特周期内对解扩后的基带波形做积分(代码里是直接求和),取符号,得到 ±1,再和原始比特比对统计误码。整条链路到此闭合。

这六段里,第二段和第五段必须严格对称:发送端用什么码、什么相位、每个码片占几个采样点,接收端必须一模一样。这套代码后面所有的坑,八成以上都出在这个对称性上。

2.2 文件组织:一个主脚本加四个自造函数

原文只给了一段主流程代码,但里面调用了mseq、goldseq、T2F、lpf四个函数,一个都没附。这是课程设计文档的通病——作者本地有一整套 .m 文件,导出 Word 时只复制了主脚本。想跑起来,这四个必须自己补。

文件作用原文是否提供
dsss_main.m主流程:参数、信源、扩频、调制、信道、解扩、判决、BER 循环提供(有笔误)
mseq.m生成 m 序列,线性反馈移位寄存器缺失
goldseq.m由两个 m 序列优选对生成 Gold 码缺失
T2F.m时域转频域,返回频率轴和双边谱缺失
lpf.m频域理想低通滤波,返回时域波形缺失

我一般会把它们放在同一个目录下,主脚本用clear; close all; clc;开头,函数文件单独存。不建议写成脚本内的局部函数,因为mseq后面做 Gold 码对比时要在别的脚本里复用,独立文件更省事。

目录结构大致是这样:

dsss_sim/ ├── dsss_main.m % 主流程 ├── mseq.m % m 序列生成 ├── goldseq.m % Gold 码生成 ├── T2F.m % 时频变换 ├── lpf.m % 频域低通 └── result/ % 出图另存,避免覆盖

出图建议直接saveas(gcf, 'result/fig3_bpsk.png'),课程设计要交报告,图能直接拖进 Word,比截图清晰。

2.3 参数表:文档标称值和代码实际值对不上

这是必须先说清楚的一件事。文档开头写「信源速率 2kbps,扩频因子 64」,但代码里Rb = 0.25、stage = 3,m 序列长度是 2³−1 = 7,跟 64 差得远。这种不一致在课程设计里太常见了,作者可能是先写了一版参数,后来调代码时改了又忘了同步文档。

我不打算假装它们一致,直接把两套数摆出来,然后给一套自洽的:

参数文档标称原文代码自洽取值说明
信源速率 Rb2 kbps0.25(单位不明)1 kbps单位统一为 bit/s
比特周期 Tb—1/Rb1 ms由 Rb 推出
仿真比特数 M—100100太少 BER 统计不准
每比特码片数64隐含 10098必须是码长 7 的整数倍
每码片采样点—44决定波形平滑度
每比特采样点 L—40039298×4
基带采样率 fs—4000392 kHzL/Tb
载波频率 fc—40 kHz40 kHz需小于 fs/2
扩频码—m 序列,7 长m 序列,7 长stage=3,抽头 [1 3]
信道高斯awgnawgn可换多径或加窄带干扰
SNR 扫描—1:15 dB1:15 dB用于画 BER 曲线

两个关键点解释一下。第一,chip_per_bit取 98 而不是 100,是因为 98 = 7×14,正好 14 个完整的 m 序列周期,码的周期性不被破坏,自相关旁瓣干净;如果取 100,每个比特末尾会截断在码序列中间,跨比特边界处码相位滑移,解扩的相关峰高度每次都不一样,BER 曲线会抖。第二,fs必须明显大于2*fc,否则载波调制会出现混叠,解调出来的基带面目全非。

处理增益按10*log10(chip_per_bit)算是 19.9 dB,文档里说「处理增益达到 10 dB 以上」,对得上。如果你按 64 算,是 18 dB,也在那个区间内——但代码实际跑出来的增益只跟chip_per_bit有关,跟文档写的数字无关。

3. 核心代码逐段拆:信源、扩频、调制、解扩判决

这一章按信号流向把主脚本拆开,每段给一份改好的代码。原文代码里的变量名混乱(定义了BPSK却去plot(tq, QPSK))、注释符写成了##、z2=0for缺分号,这些我都顺手修了,修的原因在第五章的排查里会讲。

3.1 信源生成与比特展宽

%% 1) 全局参数 Rb = 1e3; % 信源比特率 1 kbps Tb = 1/Rb; % 比特周期 1 ms M = 100; % 仿真比特数 chip_per_bit = 98; % 每比特码片数 = 14 个完整 m 序列周期 samp_per_chip = 4; % 每个码片的采样点数 L = chip_per_bit * samp_per_chip; % 每比特 392 点 N = M * L; % 总采样点 39200 fs = L / Tb; % 基带采样率 392 kHz fc = 40e3; % 载波 40 kHz SNR_list = 1:15; % 信噪比扫描范围 %% 2) 信源 rng(2024); % 固定随机种子,保证每次跑结果一样 x = sign(rand(1, M) - 0.5); % 0/1 映射为 -1/+1 的双极性比特 d = reshape(repmat(x, L, 1), 1, []); % 每比特展宽成 L 个采样点 t = (0:N-1) / fs; % 时间轴,单位秒,从 0 开始

rng(2024)这行看着不起眼,但做 BER 对比时非常重要。不加种子,两次运行的同一条 SNR 点会给出不同的误码数,曲线毛刺一片,你根本分不清是算法改了还是随机波动。固定种子之后,改一个参数只影响那一处,变量可控。

repmat(x, L, 1)把 1×100 的行向量复制成 L×100,再用reshape拉平成 1×N,得到的就是「每个比特占连续 L 个采样点」的矩形脉冲波形。这是最朴素的成型方式,没有升余弦、没有根升余弦,频谱旁瓣大,但课程设计够用,而且代码短、容易看懂。

时间轴我特意写成(0:N-1)/fs而不是原文的linspace(-T/2, T/2, N)。原因后面lpf返回的时间轴是从 0 开始的,两条轴不对齐画图会整体平移,看着像波形错位,实际只是坐标没对上。统一从 0 开始最省心。

3.2 PN 码生成与码片映射

%% 3) 扩频码 stage = 3; % 寄存器级数,码长 = 2^3-1 = 7 ptap = [1 3]; % 反馈抽头位置 reg = [1 1 1]; % 初始状态,不能全 0 code = mseq(stage, ptap, reg); % 返回 0/1 序列,长度 7 % 把 7 长码循环铺满 chip_per_bit 个码片 chip_idx = mod(0:chip_per_bit-1, length(code)) + 1; chip_seq = 2 * code(chip_idx) - 1; % 0 -> -1, 1 -> +1 % 每个码片重复 samp_per_chip 个采样点,再铺满 M 个比特 k_code = reshape(repmat(chip_seq, samp_per_chip, 1), 1, []); k_code = repmat(k_code, 1, M); % 1×39200 的本地码

mseq返回的是 0/1 序列,必须先映射成 ±1 才能直接相乘。原文用的是sign(code(mod(i,7))-0.5),效果一样但夹在循环里,可读性差;我改成向量化的2*code-1,一行搞定,也顺手避开了原代码mod(i,7)~=0/mod(i,7)==0两个分支走同一件事的冗余写法。

chip_seq的长度是 98,不是 7。这里刻意让码序列循环 14 圈填满一整个比特,保证每个比特的起始码相位都是序列的第 0 位,解扩时本地码和接收码的相位天然对齐。如果像原文那样用 100 个码片,第 15 圈只用了 2 个码片就跳到下一比特,每个比特的起始相位都不同,解扩时相当于在做滑动相关,相关峰高度忽高忽低,BER 曲线会异常抬升。

reshape(repmat(..., 4, 1), 1, [])是把每个码片拉长成 4 个采样点。这一步决定了波形的「台阶感」——4 点一个码片,画出来是明显的方波,能看清码片边界。想画得更平滑,把samp_per_chip提到 8 或 16,但总点数会跟着涨,fft的内存和时间也会涨,100 个比特、每比特 1568 点的话,单次 FFT 就是 15 万点,跑一轮 15 个 SNR 点会明显变慢。

3.3 BPSK 载波调制与加噪

%% 4) 扩频 + 载波调制 s_bpsk = d .* k_code; % 扩频:比特 × 码片 s_tx = s_bpsk .* cos(2*pi*fc*t); % BPSK:乘载波 %% 5) 信道 snr_db = 10; r = awgn(s_tx, snr_db, 'measured'); % 按信号实测功率加噪

原文在这里做了一件让人迷惑的事:把d用reshape(d, 2, N/2)拆成两路,一路乘cos、一路乘sin,再相加,变量还起名叫QPSK。这实际上是把一个 BPSK 序列做了 2 倍上采样后当成 I/Q 两路用,既不是真正的 QPSK,也不是标准 BPSK,图上画出来的波形和理论对不上。

标准 BPSK 就是一路:比特先扩频,再乘一个余弦载波。信噪比的定义也干净——awgn的'measured'模式会先测s_tx的平均功率,再按snr_db算噪声方差叠加上去,你不需要自己推Eb/N0到SNR的换算。如果换成'linear'模式,第三个参数就变成噪声功率的线性值,单位完全不一样,这是很多人第一次用awgn时翻车的地方。

fc = 40 kHz相对于fs = 392 kHz大概是十分之一采样率,一个载波周期约 9.8 个采样点,画出来能看清正弦形状,也不会因为频率太高导致采样点不够、波形失真。

3.4 相干解扩与积分判决

%% 6) 相干解调 + 解扩 r_demod = r .* cos(2*pi*fc*t); % 乘同频同相载波,搬到基带 r_despread = r_demod .* k_code; % 乘本地同步码,解扩 %% 7) 低通滤波 [f, R] = T2F(t, r_despread); % 时域 -> 频域 [tl, y] = lpf(f, R, 2*Rb); % 截止频率取 2 倍比特率 %% 8) 积分判决 xo = zeros(1, M); for i = 1:M seg = y((1+(i-1)*L) : i*L); % 取出第 i 个比特的 L 个点 xo(i) = sign(sum(seg)); % 一比特内求和,取符号 end %% 9) 误码统计 err = sum(xo ~= x); ber = err / M;

乘法解调会产生基带分量和二倍频分量(cos² = 0.5 + 0.5cos(2ωt)),低通的作用就是把二倍频和带外噪声滤掉。截止频率取2*Rb是个经验值:信号主瓣宽度大约在Rb附近,取两倍能保住主瓣、又滤掉大部分高频残留。原文写的是lpf(f, rf, fc/10000),fc = 40e3算下来截止频率只有 4 Hz,比信号带宽还窄一个数量级,主瓣直接被砍掉一半,判决前波形已经严重失真。

sum(seg)就是积分清除的离散版本。一个比特内 L 个采样点求和,信号部分同相叠加变成 L 倍,噪声部分随机相位叠加,功率只涨 L 倍、幅度涨 √L 倍,所以信噪比提升 √L,换算成 dB 就是10*log10(L)的一半再乘 2——这跟10*log10(chip_per_bit)的处理增益是同一个来源,只是这里的 L 还包含每码片 4 个采样点的过采样冗余,实际有效增益按码片数算。

如果xo求出来全是 +1 或全是 −1,先别怀疑算法,去看sum(seg)的绝对值是不是接近 0——那说明本地码和接收码没对齐,解扩之后信号被摊平了,积出来全是噪声。

3.5 误码率循环与曲线

%% 10) SNR 扫描 ber_curve = zeros(1, length(SNR_list)); for a = 1:length(SNR_list) r = awgn(s_tx, SNR_list(a), 'measured'); r_demod = r .* cos(2*pi*fc*t); r_despread = r_demod .* k_code; [f, R] = T2F(t, r_despread); [~, y] = lpf(f, R, 2*Rb); xo = zeros(1, M); for i = 1:M xo(i) = sign(sum(y((1+(i-1)*L):i*L))); end ber_curve(a) = sum(xo ~= x) / M; end semilogy(SNR_list, ber_curve, 'o-'); xlabel('SNR (dB)'); ylabel('BER'); grid on;

semilogy是对数纵轴,BER 从 10⁻¹ 掉到 10⁻³ 这种量级变化只有对数轴才看得清。原文用axis([0,15,10^(-4),1])手动锁死范围,但实际跑出来的 BER 最低点往往到不了 10⁻⁴——100 个比特的样本量,误一个就是 0.01,统计分辨率根本撑不起 10⁻⁴ 的轴。想要曲线好看,M至少提到 10⁴,代价是整条链路的数组长度翻 100 倍,FFT 会明显变慢。折中方案是每个 SNR 点跑多次取平均,代码结构上再加一层循环。

M = 100时 BER 曲线会在低 SNR 段贴着 0.5 走,高 SNR 段直接掉到 0(一个都没错),中间几乎没有过渡带,看起来像一条折线。这不是算法错,是样本量不够。这个坑我在第五章还会提一次。

4. 四个缺失函数:mseq、goldseq、T2F、lpf 自己补

主脚本能跑起来的前提是这四个函数存在。原文一个都没附,但每个的实现都不复杂,加起来不到 60 行。这一章给出实现,顺带说明各自的自测方法——补完不要直接扔进主流程,先单独验一遍,不然出了错根本分不清是函数写错了还是主流程接错了。

4.1 mseq:三级线性反馈移位寄存器

function code = mseq(stage, taps, init) %MSEQ 生成 m 序列 % stage : 寄存器级数,码长 n = 2^stage - 1 % taps : 反馈抽头位置,如 [1 3] 表示第 1 位和第 3 位异或 % init : 初始状态,长度等于 stage,不能全 0 n = 2^stage - 1; reg = init(:).'; % 保证是行向量 code = zeros(1, n); for k = 1:n code(k) = reg(end); % 输出最右端 fb = mod(sum(reg(taps)), 2);% 抽头异或 reg = [fb, reg(1:end-1)]; % 右移并填入反馈位 end end

抽头位置决定生成多项式,stage = 3时[1 3]对应 x³+x+1,是本原多项式,能生成满周期的 7 长序列。如果抽头选错,序列周期会缩短,扩频增益打折扣,而且自相关旁瓣抬头,接收端相关峰不再尖锐。

初始状态不能全零,全零状态下反馈恒为 0,寄存器永远出不来。这是 m 序列最经典的翻车点,代码里不会报错,只是code全 0,扩频之后信号变成一条直线,你要跑到判决那一步才看得出不对。

自测方法:跑code = mseq(3,[1 3],[1 1 1]),输出应该是1 1 1 0 1 0 0之类的一个周期序列,长度 7,里面 1 的个数比 0 多一个。这是 m 序列的性质,对不上说明抽头或移位方向有问题。

4.2 goldseq:优选对移位模二加

function g = goldseq(m1, m2, user) %GOLDSEQ 由两个 m 序列优选对生成 Gold 码 % m1, m2 : 两个等长 m 序列,且互为优选对 % user : 用户编号,决定相对移位量 shift = mod(user - 1, length(m2)); g = mod(m1 + circshift(m2, [0 shift]), 2); end

Gold 码的本质是把两个 m 序列做相对移位后模二加,每改变一个移位量就得到一条新码。它的互相关特性有界,地址数远多于 m 序列,所以 CDMA 里用得多。原文的switch seq分支里就是这么调的,seq=2走 Gold,seq=3在末尾补一个零——那个补零看着像笔误,实际可能是想对齐长度,但补完之后长度变成 8,跟chip_per_bit的整除关系就断了,建议直接删掉。

circshift的第二个参数是[0 shift],因为序列是行向量,第一位是行偏移、第二位是列偏移。写成circshift(m2, shift)在行向量上效果一样,但换成列向量就错位了,显式写清楚更稳。

自测方法:固定m1,遍历user = 1:7,生成 7 条 Gold 码,两两算互相关,最大值应该不超过某个上界(7 长码大约是 5)。如果某两条完全一样,说明你的m1和m2不是优选对。

4.3 T2F 与 lpf:频域理想低通那套

function [f, X] = T2F(t, x) %T2F 时域转频域,返回双边谱 N = length(x); dt = t(2) - t(1); X = fftshift(fft(x)) / N; % 归一化,幅度不随 N 变 f = (-N/2 : N/2-1) / (N*dt); % 频率轴,单位 Hz end function [t, y] = lpf(f, X, fc) %LPF 频域理想低通,fc 为截止频率 N = length(X); df = f(2) - f(1); H = double(abs(f) <= fc); % 矩形窗,通带内 1,带外 0 y = real(ifft(ifftshift(X .* H))) * N; % 乘 N 补偿 T2F 的归一化 t = (0:N-1) / (N*df); % 时间轴从 0 开始 end

fft之后除N、ifft之后乘N,这两步是为了让幅度保持物理意义。不除的话,频谱幅度会随采样点数线性增长,你换个M值,图上的纵轴刻度就全变了,没法横向对比。

fftshift把零频挪到中间,频率轴才能写成-N/2 : N/2-1的对称形式。如果漏掉fftshift,abs(f) <= fc这个条件会把正频率的一半和负频率的一半混在一起,滤出来的波形是错的,而且错得不明显——时域上看只是幅度小了一点,你要对比 BER 才会发现性能莫名其妙差了几个 dB。

lpf返回的时间轴从 0 开始,跟主脚本里t = (0:N-1)/fs完全对齐,所以可以直接拿tl去画图。如果主脚本还用linspace(-T/2, T/2, N),图会整体左移半个窗口,看着像波形提前了,实际只是坐标原点不同。

自测方法:给一个x = cos(2*pi*100*t),T2F之后应该在 ±100 Hz 处看到两根谱线,lpf截止频率设 200 Hz,滤完ifft回来应该跟原信号几乎重合;把截止频率改成 50 Hz,输出应该接近 0。这个测试能一次性验出归一化、频率轴、滤波窗三处问题。

4.4 补完后先做单元自测

四个函数补完,不要直接扔进主流程跑 15 个 SNR 点。按上面的自测方法单独验一遍,每个函数给一个已知输入的期望输出,对上了再串起来。我吃过这个亏:lpf的归一化系数写错,主流程跑出来的 BER 曲线整体抬高了两个数量级,查了一下午才定位到是ifft少乘了N。

自测的顺序建议是:先mseq(输出短,肉眼可查),再goldseq(依赖mseq),然后T2F(单频测试),最后lpf(依赖T2F)。依赖关系顺着来,出错时能立刻知道是新写的那个有问题,还是上游传下来的。

5. 避坑排查:跑不出图、误码率不降、增益对不上

这一章是血泪经验合集。这套代码从原文状态到能跑出合理曲线,我踩过的坑基本都在下面五条里,每条按「现象 → 原因 → 解决」写,你遇到类似症状可以直接对号入座。

5.1 报错 Undefined function 'mseq'

现象:把原文代码粘进编辑器,点运行,第一行就红,提示未定义函数或变量mseq。注释掉这行,下一行goldseq又报,再注释,T2F报。

原因:文档只导出了主脚本,四个函数文件没跟着走。课程设计文档的通病,作者本地目录里是齐的,复制到 Word 时只选了一段。

解决:按第 4 章的四段代码补齐.m文件,放在与主脚本同一目录下。如果不想建文件,也可以把四个函数体直接贴在主脚本末尾,MATLAB 从 R2016b 开始支持脚本内局部函数,但这样就没法在其他脚本里复用了,做 Gold 码对比时还得再抄一遍。

5.2 画出来全是直线或者满屏毛刺

现象:figure(2)弹出来是一条水平直线,或者密密麻麻看不出周期。原代码里plot(tq, QPSK)还会直接报QPSK未定义。

原因有两个。一是变量名不一致,上文定义的是BPSK,画图时写成了QPSK,这是原文的笔误。二是axis的范围设得太窄,原文写axis([20,25,-1.5,1.5]),横轴只截了 20 到 25,如果时间轴单位是秒,这 5 秒里可能只有几个比特,画出来就像一条直线。

解决:统一变量名,axis改成按实际比特数取,比如axis([0, 5*Tb, -1.5, 1.5])只看前 5 个比特。想看码片细节就再缩到[0, 2*Tb, ...]。另外画图前先whos看一眼数组长度,长度是 1 或者 NaN 的话,问题在生成阶段不在画图阶段。

5.3 误码率恒在 0.5 附近不下降

现象:SNR 从 1 dB 扫到 15 dB,BER 曲线几乎是一条水平线,贴在 0.5 上下小幅波动。

原因:本地扩频码和接收信号的码没对齐。解扩的本质是相关,码相位差一个码片,相关值就从 +1 掉到 −1/n,等于把信号当噪声处理了。原文里k_code是重新生成的,如果中途改过chip_per_bit而没同步改接收端,或者码序列长度不能整除每比特采样点导致跨比特滑移,都会出现这个症状。

解决:接收端直接用发送端那个k_code变量,不要重新生成。如果非要重新生成(比如模拟同步过程),必须保证stage、taps、init、chip_per_bit、samp_per_chip五个参数完全一致。验证方法是在解扩后立刻算一下mean(abs(r_despread)),正常应该在 0.5 以上;如果接近 0,说明码没对上。

5.4 扩频因子和处理增益对不上

现象:文档说扩频因子 64,你按 64 算处理增益是 18 dB,但仿真跑出来的 BER 曲线跟理论值差了七八个 dB。

原因:原文stage = 3生成的是 7 长 m 序列,代码里每码片重复 4 个采样点,L = 400,400/4 = 100 个码片。100 不是 7 的整数倍,每个比特的起始码相位都在滑动,实际参与相关的有效码片数远小于 100,处理增益只有十几 dB 而不是 18 dB。文档写的 64 和代码里的 7 根本是两个东西。

解决:chip_per_bit取码长的整数倍。7 长码就取 98(14 个周期)或 105(15 个周期),配samp_per_chip = 4,L相应改成 392 或 420。改完再算处理增益10*log10(98) ≈ 19.9 dB,跟 BER 曲线的位置就对得上了。

5.5 低通截止频率拍脑袋,判决前波形已经变形

现象:解扩后的波形看着有包络,但每个比特内的极性不明显,判决出来的比特错得很随机,BER 比理论值高一截。

原因:原文lpf(f, rf, fc/10000),fc = 4e4算出来截止频率 4 Hz。而比特率Rb = 0.25(按代码单位),信号主瓣宽度就在这个量级,截止频率 4 Hz 把主瓣砍掉了大半,码间串扰严重。数字低通不是越窄越好,窄到把信号带宽也滤掉就本末倒置了。

解决:截止频率取1~2倍比特率。Rb = 1e3就取1e3或2e3。改完画一下解扩后的时域波形,每个比特周期内应该能看到明显的正负台阶,台阶中间平滑、边界处有小过渡带,这个形状就对了。如果台阶界限模糊、相邻比特互相渗透,说明截止频率还是偏低。

6. 二次开发:把课设改成能出对比曲线的实验平台

主流程跑通之后,这套代码的骨架其实可以撑起一个小型实验平台。我改过几版,最有用的是下面四条路。

第一条,参数化。把主脚本封成function ber = dsss_sim(cfg),cfg是一个结构体,装Rb、chip_per_bit、snr_list、code_type这些字段。这样对比不同参数只要换结构体,不用改代码。结构大概长这样:

cfg.Rb = 1e3; cfg.chip_per_bit = 98; cfg.samp_per_chip = 4; cfg.code_type = 'm'; % 'm' 或 'gold' cfg.snr_list = 1:15; ber = dsss_sim(cfg);

第二条,换码对比。把code_type切到'gold',同一条 BER 曲线上叠两条线。Gold 码的互相关特性比 m 序列好,但在单用户场景下两者性能接近,差异要到多用户干扰下才明显。这个对比做出来,你对「为什么 CDMA 用 Gold 不用 m」会有直观感受,比背结论强。

第三条,加干扰。在信道那段插一个单音干扰r = s_tx + A*cos(2*pi*fi*t) + noise,fi落在扩频带宽内但避开信号主瓣。扩频系统对这个干扰的处理方式很直观:解扩时干扰被二次扩频,功率谱密度下降,低通之后只剩一小部分。把A从 0 扫到 1,看 BER 怎么变,抗干扰能力就有了量化曲线。这条改造最有说服力,答辩时被问「扩频到底抗什么干扰」,直接把图甩出来。

第四条,加码相位同步。现在的代码是收发共用k_code,属于理想同步。真实接收机不知道码相位,要做滑动相关搜索:本地码每次移一个码片,与接收信号相关,相关峰出现的位置就是码相位。代码上就是一层for shift = 0:chip_per_bit-1的循环,记录每个移位量下的相关值,画出相关峰图。这一步做出来,整个仿真的完整度会上一个台阶。

验证方法上,我习惯用理论 BER 做基准:BPSK 在 AWGN 下的误码率是Q(sqrt(2*Eb/N0)),把扩频增益折算进Eb/N0之后,仿真曲线应该跟理论曲线平行、间距等于处理增益。间距对不上就去查chip_per_bit是否真的等于实际参与相关的码片数,这个数对不上,整条曲线的位置都是错的。

后面每次改扩频因子或者换码型,我都强制先跑一遍理论对比,两条线不平行就不往下改。这个习惯是从一次答辩前夜开始的——当时 BER 曲线怎么调都比理论值高 6 dB,最后发现是chip_per_bit设成了 100、码长 7,跨比特滑移把有效增益吃掉了。从那以后我每次动参数都先把理论曲线画在旁边当标尺。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/6 4:17:31

AI营销落地作战地图:可拆解、可复用、可验证的六大能力模块

简介&#xff1a;本资源为《2025年中国数字营销行业人工智能应用趋势研究报告》终版PDF&#xff0c;面向市场营销从业者、企业决策者、AI技术爱好者及行业研究人员&#xff0c;系统解答AI如何从效率工具升级为营销战略资产、垂直场景落地路径、以及算法治理与中小企业技术普惠等…

作者头像 李华
网站建设 2026/11/6 4:16:31

BJTU计算机视觉期末考点解析:定义咬文嚼字与底层原理

简介&#xff1a;本资源是北京交通大学2024年计算机视觉课程核心考点精要汇总&#xff0c;面向高校本科生、考研备考学生及CV入门学习者&#xff0c;系统梳理课程中高频必考的40个关键知识点&#xff0c;覆盖图像滤波原理&#xff08;相关/卷积/内积&#xff09;、Canny与Harri…

作者头像 李华
网站建设 2026/11/6 4:15:33

互联网资产梳理实战:火器插件打造可持续资产台账

互联网资产梳理这件事&#xff0c;说穿了就是把"我们到底在网上暴露了些什么"这个问题回答清楚。听起来简单&#xff0c;真做起来&#xff0c;一个中等规模的业务方&#xff0c;域名、子域名、IP、端口、证书、Web 指纹、归属部门&#xff0c;光是字段就能拉出二三十…

作者头像 李华
网站建设 2026/11/6 4:15:33

pandas DataFrame高频操作与数据清洗避坑指南

我在处理表格类数据时有个习惯&#xff1a;拿到一份新数据&#xff0c;先不着急画图、不着急建模&#xff0c;而是打开一个 Python 会话&#xff0c;用一串 DataFrame 操作把这份数据"摆平"——看清楚每一列到底是什么类型、有多少缺失、哪些字段是重复的键、哪些值明…

作者头像 李华
网站建设 2026/11/6 4:14:29

Claude Code实时协作编程:提升团队效能的实践与思考

1. 项目概述"Claude Code"这个术语在技术社区中通常指代一种新兴的协作编程范式或工具链。作为一名经历过多次技术变革的团队技术负责人&#xff0c;我亲眼目睹了这类工具如何重塑开发团队的协作方式。不同于传统的版本控制系统或简单的代码审查工具&#xff0c;Clau…

作者头像 李华
网站建设 2026/11/6 4:14:26

局域网大文件分块上传与断点续传技术详解

1. 局域网大文件分块上传方案概述在局域网环境中实现大文件上传&#xff0c;WebUploader是一个经过验证的可靠方案。与公网环境相比&#xff0c;局域网传输具有带宽高、延迟低的优势&#xff0c;但也面临一些特殊挑战&#xff1a;浏览器兼容性&#xff1a;企业内网常存在老旧浏…

作者头像 李华