用Matlab从零搭建DS-CDMA仿真链路:扩频码、成形滤波与同步实践
2026/9/18 6:16:24 网站建设 项目流程

简介:基于Matlab的DSCDMA系统仿真与分析文档,面向通信工程、电子信息类学生及研究人员,系统讲解直接序列扩频码分多址系统原理并给出Matlab仿真实现。资源仅含1个doc文档,压缩包大小为481KB,内容覆盖CDMA基本概念、多址技术、DS-CDMA系统模型与仿真结果,从理论到验证形成完整闭环。文档重点展开PN码技术(m序列、Gold序列、Walsh序列)、信道编码(卷积码、交织编码)、QPSK调制解调以及PN码捕获与跟踪、抽样判决等关键环节;第五章给出基于Matlab的仿真模型与结果分析,便于学习者对照论文结构进行复现或二次开发。目前已有126人学习下载,适合需要快速掌握DS-CDMA系统设计脉络、完成课程设计或毕业设计的读者参考。

1. 为什么 DS-CDMA 仿真要用 Matlab 从零搭链路

很多工程师拿到 DS-CDMA 仿真任务,第一反应是拖 Simulink 的通信库。但直接拼模块会让扩频码、定时恢复、多径信道这些环节耦合在一起,误码率一旦偏离理论值,根本分不清是成形滤波器滚降系数设错,还是同步位置有了码片偏移。我习惯用纯 Matlab 脚本按“发射机-信道-接收机”三段搭链路,把码片级操作显性化。DSCDMA 即 DS-CDMA,直序列码分多址,核心是用高速扩频码将低速数据扩展到宽频带,接收端再解扩恢复。这套做法既能跑出可解释的误码率曲线,也能直接复用到多用户容量分析,适合需要写报告或快速验证算法的人。

2. DS-CDMA 系统仿真前的三个关键设计:扩频码、脉冲成形与同步

在写主循环之前,先把三件事定下来:扩频码决定多用户隔离能力,脉冲成形决定带宽占用,同步策略决定接收机复杂度和仿真耗时。很多第一次做 DS-CDMA 仿真的人,直接在随机数据后接代码片复制,然后加白噪声,最后误码率一直不降,问题大多出在“发端没成形滤波、收端没做匹配滤波”或者“同步位置差了半个码片”。下面按顺序说,每一步都给出可以抄走的 Matlab 写法。

2.1 扩频码怎么选:m 序列与 Gold 码

扩频码本质是周期序列。单用户 AWGN 信道里,m 序列和 Gold 码的误码率差别不大,但多用户场景下必须选互相关低的码族。m 序列是最大长度线性反馈移位寄存器序列,周期为 2^m - 1,自相关理想;Gold 码由两个 m 序列异或构成,族内任意两码的互相关峰值有上界,适合做 CDMA 的用户扰码。Matlab 里除了通信工具箱的comm.GoldSequence,手写生成也不复杂。

% 生成m序列:gen_poly 是本原多项式系数,n 是寄存器长度 function seq = mseq(gen_poly, n) m = length(gen_poly) - 1; reg = [1 zeros(1, m-1)]; % 初始状态非全零 period = 2^m - 1; seq = zeros(1, period); for k = 1:period seq(k) = reg(m); % 反馈抽头由 gen_poly 非首项决定 fb = mod(sum(gen_poly(2:end) .* reg), 2); reg = [fb reg(1:m-1)]; end end % 两个m序列循环移位后异或得到一条Gold码 function g = gold_code(seq1, seq2, shift) n = length(seq1); s2 = circshift(seq2, shift); g = mod(seq1 + s2, 2); end

逻辑说明:mseqgen_poly写成二进制系数,例如[1 1 0 0 1]表示 x^4 + x^3 + 1。reg的初始状态取[1 0 0 ...],避免全零状态锁死。反馈位fb等于所有抽头与对应寄存器值做模 2 和。Gold 码生成时,不能随便取两个 m 序列的移位组合,常见做法是固定一组本原多项式,把所有移位后的 Gold 码预生成好,放在一个矩阵里供多用户调用。

选码经验:处理增益 31 或 63 时,m 序列够用;用户数超过 5 个时换成 Gold 码,误码率曲线会稳定得多。另外,仿真结束前要检查扩频码周期是否和数据长度整除,否则最后一段会补零,人为引入错误。

2.2 脉冲成形与滚降因子对频谱的限制

DS-CDMA 的基带信号由码片脉冲叠加而成。如果直接发送 ±1 的矩形波,频域旁瓣衰减慢,带外辐射严重。实际系统会加升余弦或根升余弦滤波器,Matlab 中用rcosdesign生成 FIR 系数即可。

% 滚降因子0.22,span=6个码片,每个码片8个采样点 beta = 0.22; span = 6; sps = 8; h = rcosdesign(beta, span, sps); % 扩频后的码片序列先上采样再和 h 卷积 tx_filtered = upfirdn(chips, h, sps, 1);

参数说明:滚降因子beta控制带宽和时域拖尾。0.22 是蜂窝系统常用值,带外衰减快但码间干扰稍强;0.5 时脉冲更平滑,带宽相应增加。span决定滤波器截断长度,一般取 6 到 10。sps=8表示每个码片 8 个采样点,后文仿真中的定时分辨率就是 1/8 码片,足以观察定时偏移的影响。

这里要注意,upfirdn默认会把滤波器群延迟计算在内,解扩前要对齐。最简单的方式是发端和收端共用同一个滤波器,接收端再做一次匹配滤波,总频响为升余弦。如果发端做了成形滤波而收端不做匹配滤波,等效于用矩形窗去接收根升余弦信号,误码率会抬升一截。

2.3 接收端同步的仿真边界

同步在链路仿真里很耗时间。一个可复现的策略是:先假设理想定时,把主循环跑通,再加入固定码片偏移和频偏,用相关法估计。这样可以把“链路算法”和“同步模块”分开调试。

% 用滑动相关找码片级延迟 % rx 是接收到的基带信号,local_code 是上采样后的本地扩频码 [corr_vals, lags] = xcorr(rx, local_code); [~, idx] = max(abs(corr_vals)); delay_est = length(rx) - lags(idx);

这段代码的原理是利用扩频码的强自相关峰值定位整数倍采样点延迟。xcorr计算互相干,lags是延迟轴。如果最大值出现在中点附近,说明没有大延迟;如果偏移了多个码片,delay_est就是估计值。注意长序列上xcorr内存较大,可以先按码片分块滑动做粗同步,再做采样级细同步。多径信道下相关峰会分裂,单峰检测可能失效,需要改用能量最大径或前导相关。

实际仿真中,我一般预留一个timingOffset参数,默认设 0,做扫描时设成 0.5 码片,用来观察定时未校准的敏感度。这是报告里很容易加分的分析项。

3. 在 Matlab 中搭建 DS-CDMA 发射机到接收机的完整链路

下面给最小可运行的链路脚本,参数对应一个码片速率 1.2288 Mchip/s 的典型场景,但仿真中把码片周期归一化为 1 秒。核心参数先列成表。

参数取值说明
数据速率 Rb38.4 kbps码片速率 / 处理增益得到
处理增益 Gp32每比特扩频码片数
码片速率 Rc1.2288 Mchip/s仿真时归一化为 1
成形滤波RRC滚降 0.22,span 6,sps 8
调制方式BPSK便于算误码率
用户数1 ~ 8多用户测试时动态扩展

3.1 发射机:扩频、加扰、成形滤波的代码实现

发射机先把 bit 映射为 BPSK 符号,即 0 映射为 +1,1 映射为 -1。然后每个符号复制 Gp 次,逐码片乘上扩频码。多用户场景再乘一个用户扰码,这里先用随机 ±1 序列代替,跑通后再换成 m 序列或 Gold 码。

Gp = 32; % 处理增益 sps = 8; % 每码片采样点数 beta = 0.22; span = 6; numBits = 1e4; % 仿真比特数 rng(42); % 固定随机种子 % 数据源 data = randi([0 1], numBits, 1); sym = 1 - 2*data; % BPSK映射:0->1, 1->-1 % 扩频码:随机±1序列,实际应换成m序列或Gold码 code = 1 - 2*randi([0 1], Gp, 1); chips = reshape(sym, 1, [])' * code.'; % numBits x Gp chips = chips(:)'; % 串行码片流 % 成形滤波,并丢弃滤波器群延迟 h = rcosdesign(beta, span, sps); tx_full = upfirdn(chips, h, sps, 1); tx = tx_full(span*sps/2 + 1 : end);

代码说明:sym是数据符号,code是长度为 Gp 的扩频码。用外积把每个符号扩展成一行码片,再按行拉成串行码片流。upfirdn的第三个参数sps表示上采样率,每个码片插入sps-1个零再卷积。span*sps/2是发射端 RRC 滤波器的群延迟采样点数,截掉之后,tx的起始位置对应第一个有效码片。

注意reshape(sym, 1, [])这里转成了行向量,避免维度不一致。rng(42)保证可复现。如果不想依赖通信工具箱的rcosdesign,也可以用firrcos或自行设计滤波器,但不建议在链路验证阶段换,避免引入滤波器设计问题。

3.2 信道模型:AWGN 与多径衰落怎么加

AWGN 信道中,噪声方差需要按码片能量换算。由于每个比特由 Gp 个码片组成,且符号幅度归一化为 1,每比特能量 Eb = Gp。目标 EbN0 对应的单边噪声功率谱密度 N0 = Gp / 10^(EbN0_dB/10)。

EbNo_dB = 6; EbNo_lin = 10^(EbNo_dB/10); N0 = Gp / EbNo_lin; % 每个比特能量=Gp noise = sqrt(N0/2) * randn(size(tx)); rx = tx + noise;

参数说明:sqrt(N0/2)是实基带 BPSK 噪声标准差,经过匹配滤波和下采样后,解扩符号的信噪比会随 Gp 提高。这段代码把噪声叠加在采样级信号上,符合实际链路。如果直接对码片级序列加方差为1/(Gp*EbNo)的噪声,会在解扩后把信噪比算偏。

多径衰落用通信工具箱的comm.RayleighChannel实现,也可以手动加抽头延迟线:

rayleighChan = comm.RayleighChannel(... 'SampleRate', sps, ... 'PathDelays', [0 1.5], ... % 单位:码片 'AveragePathGains', [0 -6], ... % 相对增益 dB 'NormalizePathGain', true); rx_faded = rayleighChan(tx);

PathDelays的单位是码片,而SampleRate是每码片采样数,因此 1.5 码片延迟会换算成 12 个采样点。先不加多径,用 AWGN 验证链路正确性,再引入衰落,这是一种更稳妥的调试顺序。

3.3 接收机:匹配滤波、解扩与判决

接收端先用与发射端相同的 RRC 滤波器做匹配滤波,再下采样到码片率,截掉匹配滤波引入的群延迟,最后逐比特解扩。

% 匹配滤波,并下采样到每码片1个采样点 rx_f = upfirdn(rx, h, 1, sps); flushLen = span/2; % 匹配滤波群延迟,单位码片 rx_f = rx_f(flushLen + 1 : end); % 码片级解扩:每 Gp 个码片与本地码点乘累加 symRx = zeros(numBits, 1); for k = 1:numBits block = rx_f((k-1)*Gp+1 : k*Gp); symRx(k) = block * code; % 与本地扩频码点乘 end % 判决与误码统计 dataRx = symRx < 0; % BPSK判决,负值判为1 ber = sum(dataRx ~= data) / numBits;

逻辑说明:upfirdn的第三个参数为 1,第四个参数为sps,等效于先滤波再按sps抽取,输出就是码片率采样。flushLenspan/2,因为接收端 RRC 滤波器的群延迟在码片域为 span/2。解扩时把连续 Gp 个码片与本地码点乘后累加,得到恢复后的符号幅度。由于映射为 0->+1、1->-1,负值判为 1,与发射端对应。

如果ber离理论 BPSK 曲线差太多,优先检查三处:第一,flushLen是否设置正确;第二,发射端是否截掉了群延迟;第三,噪声方差是否按 Eb=Gp 换算。实践中最常见的错误是发射端没截群延迟,导致解扩窗口错位一个码片,误码率直接掉到 0.3 附近。

4. 多用户干扰与抗干扰分析:用 Matlab 脚本量化“谁抢了谁的信噪比”

从单用户到多用户,DS-CDMA 的特点才真正显现。多用户同时发送时,各用户的扩频码不严格正交,进入接收机后会产生多址干扰。仿真多用户比单用户麻烦的一点,是所有用户的叠加信号不能简单当作更高噪底来处理,因为干扰功率与扩频码相关。

4.1 多用户干扰的建模方式

最简单的模型是同步多用户:所有用户码片对齐,只有相对幅度差异。Matlab 中直接把所有用户发射信号按功率加权后相加,再统一加噪声。

% codes 是 K x Gp 矩阵,每行一个用户的扩频码 % tx_signals 是 K x length(tx1) 矩阵,每行一个用户的发射波形 tx_signals = zeros(K, length(tx1)); tx_signals(1,:) = tx1; tx_signals(2,:) = tx2; tx_signals(3,:) = tx3; % 功率控制:用户2/3比用户1高3 dB tx_power = [1, 2, 2]; rx_signal = sum(tx_signals .* tx_power', 1);

这个叠加方式本质是线性叠加。通过调整tx_power可以模拟功率控制不准带来的“远近效应”。代码里隐含假设所有用户符号边界对齐。非同步场景需要在每个用户信号后补随机延迟,但那样仿真时间会显著增加,建议先跑同步模型,稳定后再逐步异步。

4.2 误码率曲线的横坐标到底用什么

多用户误码率曲线上直接用某用户的 Eb/N0 作为横轴,是不准确的。多用户下应该关注信号对干扰加噪声比,但在仿真中更容易复现的做法是:横坐标仍保持 Eb/N0,每条曲线对应一个干扰用户功率比,从而说明“噪声降低不再成比例获益”。

我一般组织结果的方式是:设定ebnoVec = 0:2:10,在同样 Eb/N0 下,分别设置用户数 1、2、4、8,画出误码率曲线。曲线的水平分离程度就是多址干扰的影响。若想判断系统容量,直接看误码率 1e-3 处需要额外增加多少 dB,这个值就是多用户损失。

berVec = zeros(length(ebnoVec), 4); for idx = 1:length(ebnoVec) berVec(idx, 1) = runDscdmaSim(ebnoVec(idx), 1); berVec(idx, 2) = runDscdmaSim(ebnoVec(idx), 2); berVec(idx, 3) = runDscdmaSim(ebnoVec(idx), 4); berVec(idx, 4) = runDscdmaSim(ebnoVec(idx), 8); end

这里runDscdmaSim是前面发射机、信道、接收机的封装函数,返回单次误码率。量化干扰时,最好在接收端先测量每个用户的实际接收功率,否则不同功率下曲线无法解释。注意记录功率时要在叠加前保存,不能用解扩后的符号幅度近似。

4.3 仿真常见错误:定时偏差和频偏怎么排除

误码率曲线高得不正常,先排除定时偏差。加一个参数timingOffset,在接收端抽取码片时整体偏移若干采样点:

n_off = round(0.5 * sps); % 偏移0.5个码片 rx_sliced = rx_f(1 + n_off : end);

如果让偏移在 0 到 7 之间扫描,会发现误码率随偏移呈碗状变化,最深点在 0 附近。另一种常见错误是忽略载波频偏。基带仿真中可以人为注入一个复指数旋转:

freqOffset = 0.01; % 归一化频率,单位:周期/码片 rx_off = rx .* exp(1j*2*pi*freqOffset*(0:length(rx)-1)/sps);

这种手工注入可以验证相关峰是否衰减。注意在实际 CDMA 接收机中,频偏对解扩相关峰的影响是包络衰减,不是完全错位,所以观察到的误码率曲线会先缓慢上升再急剧劣化。

5. 从仿真到分析:用 Matlab 结果验证 DS-CDMA 扩频增益与系统容量

仿真不是只为了画一条误码率曲线,还要能回推扩频增益,并解释容量与处理增益的折中。

5.1 扩频增益的实测方法

扩频增益的经典定义是 Gp = Rc / Rb,等于每比特码片数。在 Matlab 仿真里也可以直接测量:分别测量解扩前和解扩后的信噪比,解扩后应高约 10*log10(Gp) dB。

% 测量解扩前后SNR snr_pre = mean(abs(rx_chip).^2) / var(noise_chip); snr_post = mean(abs(symRx).^2) / var(noise_sym); gain_meas = 10*log10(snr_post / snr_pre);

这里rx_chip是匹配滤波后、未解扩的码片采样,noise_chip是同一个位置的噪声样本,symRx是解扩后的符号。注意必须使用同一段噪声样本,不能重新生成随机数。如果gain_meas和理论增益差超过 0.5 dB,说明噪声方差换算有问题,或者成形滤波器引起的码间干扰没有完全消除。

5.2 容量与处理增益的折中

增大 Gp 会让每个符号携带更多码片,抗干扰能力强,但带宽占用同步增大。在 Matlab 中用同一个链路分别跑 Gp=16、32、64,保持数据速率不变,观察同一误码率对应的 Eb/N0 变化。

Gp单用户所需 EbN08用户所需 EbN0容量因子
166.8 dB9.5 dB1.0
326.7 dB7.8 dB2.0
646.7 dB7.0 dB4.0

这个表的数值只是示例,真实结果会随滚降系数、扩频码互相关和用户分布变化。容量因子表示同样带宽下可容纳的用户数,它与 1/Gp 成反比,但多用户损失会让实际容量低于理想比例。当 Gp 很小时,多用户干扰陡然增大,因为码片速率提高会让干扰泄漏更严重。

5.3 用半解析法交叉验证

Monte Carlo 仿真在误码率低于 1e-4 时需要的比特数很大,跑得很慢。可以用半解析法,只仿真信道和干扰统计,误码率按 Q 函数近似。Matlab 中直接用qfunc

ber_th = qfunc(sqrt(2 * Gp * EbNo_lin / (1 + (K-1)/3)));

其中 K 是用户数。该近似假设用户异步且扩频码均匀随机,在 K 较大时与实际仿真吻合。公式中的 (K-1)/3 来自随机扩频码干扰的方差近似,不是所有码字都成立。用半解析法可以快速扫描 K 从 1 到 32 的变化,节省大量 Monte Carlo 时间,曲线趋势也能用来反推仿真中的异常点。

6. 落地技巧:让 DS-CDMA 仿真跑得更快、结果更可信

最后一章聚焦三个让仿真工程化的技巧,也方便把结果直接写进报告。

6.1 用并行计算加速 Monte Carlo 循环

当仿真比特数超过 1e5,每个 Eb/N0 点都要跑完整链路。把普通for循环改成parfor是最直接的加速手段:

ebnoVec = 0:2:10; berVec = zeros(size(ebnoVec)); parfor i = 1:numel(ebnoVec) berVec(i) = runDscdmaSim(ebnoVec(i), Gp, sps); end

parfor要求每个迭代内不能读写共享变量,runDscdmaSim内部生成的随机数流必须独立。如果直接调用全局rng,不同 worker 可能生成相同序列,导致误码率曲线异常。我一般把随机种子写成迭代号,并在函数内使用RandStream隔离。

6.2 随机种子与流的设置

并行环境里,rng默认的全局状态不可靠。建议在链路函数里创建独立随机流:

function ber = runDscdmaSim(ebno, Gp, sps, stream) if nargin < 4 stream = RandStream('mt19937ar', 'Seed', 7); end data = rand(stream, [numBits, 1]) > 0.5; noise = sqrt(N0/2) * randn(stream, size(rx)); end

mt19937ar是梅森旋转算法,周期长,适合长仿真。固定种子的目的是让同一组参数多次运行结果可复现,尤其在对比不同滚降系数或扩频码时,随机数一致才能看到指标差异。

6.3 误码率的置信区间验证

当 BER 在 1e-4 量级时,只跑 1e6 比特,统计波动很大。经验上需要至少 10/BER 个比特测一个误码点。要判断结果可不可信,可以用二项分布置信区间:

nBits = 1e6; errs = 83; phat = errs / nBits; ci = binofit(errs, nBits, 0.1); % 90%置信区间

binofit返回置信区间,如果上下界跨度过大,说明需要更多比特数。画图时给误码率曲线加误差棒,比单条平滑曲线更有说服力。最后提醒一句:仿真曲线和理论曲线放在同一张图里,用不同线型区分,导出.eps.png放进.doc报告时,比截图像素图清晰得多。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询