资讯详情

MATLAB搭建DS-CDMA仿真系统:从扩频码到误码率全链路解析

📅 2026/9/26 13:47:42 | 华诺云谱 👁 阅读
MATLAB搭建DS-CDMA仿真系统:从扩频码到误码率全链路解析
直接上干货。今天这篇我们用MATLAB亲手搭一个DS-CDMA通信系统从散乱的公式到能跑的代码把发射、信道、接收、误码率统计这一整条链路全部跑通。DS-CDMA全称Direct Sequence Code Division Multiple Access中文就是直接序列码分多址。看懂它你就理解了3G时代的核心通信制式也为后面理解WCDMA、CDMA2000甚至卫星通信里的很多概念打好了底子。简单说它做的事情就是让多个用户同时、同频地发送信号彼此不打架靠的是一串精心设计的扩频码。用MATLAB折腾这个项目你不需要搭任何硬件一个脚本就能看到“多用户共享频段”的完整过程还能用误码率曲线验证理论结果。全文会从整体设计思路开始逐步拆解发射机、信道模型、接收机再进入多用户场景和常见坑位。代码我给到能直接跑的版本适合正在学通信原理、做课程设计/毕业设计、或者单纯想复习扩频通信底层逻辑的朋友。你会在这套代码里看到Walsh码怎么生成、比特怎么扩频、噪声怎么加、相关接收怎么做以及当用户数从1涨到16时误码率是怎么一步步恶化的。1. 项目拆解先搞懂DS-CDMA到底在玩什么1.1 一句话说清楚原理DS-CDMA的核心思想说白了就是“大家都进同一个房间说话但各说各的方言”。这里的方言就是扩频码。发送端把每个比特的能量通过乘上一个高速的码片序列摊开到更大的带宽上接收端只要知道对方的“方言”就能从一堆混杂信号里把对应信息捞出来。具体到数学上每个用户的比特b_i先做BPSK映射0写成-11写成1然后复制扩频因子sf次逐位乘上该用户的扩频码c_i。扩频码的长度等于sf每个元素是1或-1。发送信号可以写成s_i(t) b_i · c_i(t)如果是K个用户同时发送信道上看到的是:r(t) Σ(s_i(t)) n(t)接收端想解第i个用户就拿c_i去和r(t)做相关。因为理想情况下不同用户的码是正交的内积为0所以r, c_i b_i·c_i, c_i Σ_{j≠i}b_j·c_j, c_i n, c_i ≈ b_i · sf 噪声项前面那个约等号成立靠的就是“正交码内积为零”这个假设。想理解CDMA这句话是最关键的一根锚。剩下的同步、多用户干扰、性能恶化全是围绕这个内积等号还能不能保住展开的。1.2 为什么要用MATLAB来做这类通信系统仿真MATLAB是舒服得离谱的选择。首先是矩阵运算天然适配扩频操作一个比特复制成sf个码片用一行reshape(repmat(...))就完成了相关解扩本质是矩阵乘代码短到不可思议。其次是现成函数多hadamard生成Walsh码randn生成高斯噪声erfc算理论误码率全程不需要额外装工具箱基础MATLAB就够。另外调试验证非常直观。你可以随时在工作区里点开变量看中间信号长什么样扩频前后的波形、相关器输出、误码位置全部可视化。相比C或者PythonMATLAB在“验证想法”这个环节效率极高。唯一要吐槽的是大规模蒙特卡洛循环跑起来慢这个后面第5节我会给优化方案。1.3 模块化架构设计不要一锅炖这种系统最忌讳把所有逻辑写在一个巨型脚本里。我的习惯是拆成四个文件文件职责输入输出ds_cdma_tx.m单用户发射机负责映射、扩频、码分比特流、用户码、扩频因子基带扩频信号ds_cdma_channel.m信道模型负责叠加AWGN噪声、控制信噪比发送信号、Eb/N0、每比特能量接收信号ds_cdma_rx.m单用户接收机负责相关解扩、判决接收信号、用户码、扩频因子恢复的比特流run_ds_cdma.m主脚本设置参数、跑蒙特卡洛、画图无BER曲线等结果这样拆分的好处是每个环节都能单独测试。比如发射机做完你可以不看信道和接收机直接打印扩频后的信号确认码片结构对不对接收机写完可以先不发噪声验证能百分之百还原比特。等每一块都独立跑通了再拼接全链路出错时定位也容易。这个思路不光是这个项目适用你做任何通信仿真都建议这么干。2. 发射机端扩频、调制与码分多址2.1 扩频码怎么选从Walsh到PN/m序列发射机最关键的一个选择是扩频码。不同场景选不同码原因是它们的正交特性和自相关特性差异很大。Walsh码用MATLAB的hadamard(sf)直接生成行与行之间完全正交。在理想同步的场景下多用户干扰理论上为零代码实现最简单适合入门仿真。m序列最长线性反馈移位寄存器序列自相关函数非常尖锐近似随机噪声互相关特性较弱但不保证绝对正交。它更贴近真实CDMA系统中上行链路的情况因为上行各用户到达基站的时刻很难完全对齐。OVSF码WCDMA里用来做速率匹配的正交可变长扩频码同一个码树下不同码之间保持正交但要求码同步严格。码类型正交性自相关典型场景Walsh完全正交同步时一般下行链路、课堂仿真m序列/PN近似正交尖锐上行链路、抗多径OVSF正交同步时一般WCDMA速率匹配我们的项目目标是理解原理所以先用Walsh。sf16时hadamard(16)生成16行16列的矩阵每行就是一个扩频码不同行之间点积为零。注意第一行是全1这种码没有“区分度”实际选用户码时我会从第二行开始选或者干脆把第一行留给用户1也行反正它们是正交的。2.2 发射机的MATLAB实现写发射机之前先确定仿真参数。我给一个通用的配置扩频因子sf16用户数K4每个用户比特数N_bits1000。这个规模跑起来很快又能看出效果。发射机核心代码长这样function tx ds_cdma_tx(bits, user_code, sf) % bits: 1xN_bits 二进制0/1行向量 % user_code: 1xsf 扩频码1/-1行向量 % tx: 1x(N_bits*sf) 扩频后的基带信号 bpsk 2*bits - 1; % 0 - -1, 1 - 1 spread reshape(repmat(bpsk, sf, 1), 1, sf*numel(bpsk)); % 每个比特复制sf次 tx spread .* repmat(user_code, 1, numel(bpsk)); % 逐码片相乘 end这里有个关键点repmat(bpsk, sf, 1)把每个比特纵向复制sf行再reshape成行向量后就得到了“每个码片重复sf次”的序列。然后和重复了N_bits次的用户码逐元素相乘完成扩频。调用方式也很直接sf 16; K 4; N_bits 1000; walsh hadamard(sf); bits1 randi([0 1], 1, N_bits); code1 walsh(1, :); % 用户1的码 tx1 ds_cdma_tx(bits1, code1, sf);这时你可以在MATLAB里把tx1画出来会看到一段长度16000的序列原始比特已经被“打散”到16个码片中。这就是扩频后的基带信号。如果你再把walsh(1,:)和walsh(2,:)画在一起会直观感受到“不同用户用了完全不同的码型”。2.3 几个容易写错的细节第一映射别搞反。通信里BPSK常用1和-1表示比特1和0但MATLAB里逻辑值直接参与乘法会出问题。先做2*bits-1把0变成-11变成1再进扩频。否则扩频码和“0”相乘后全是0接收端相关器输出你怎么判断都对不上。第二码的方向必须一致。user_code必须是行向量repmat(user_code, 1, N_bits)才会在水平方向重复。如果你从hadamard(sf)里取出来的是列向量记得用code1 walsh(1, :)而不是walsh(:, 1)否则尺寸不匹配或者扩频逻辑错误排查起来很蛋疼。第三没有脉冲成形。这里我们直接发理想方波形状的基带码片没有加升余弦滤波。做误码率仿真时完全没问题因为判决只看抽样值但如果后续你想看频谱会观察到sinc形状的旁瓣。这个属于正常现象不是代码bug。3. 信道建模与接收机解扩、判决与误码率3.1 AWGN信道建模为什么我不用awgn函数很多教程喜欢用awgn(tx, snr, measured)加噪声但我个人强烈建议这个项目里手动生成噪声原因只有一个awgn用的是信号总功率而通信系统性能关心的是每比特能量与噪声功率谱密度之比Eb/N0。在扩频系统里码片速率是比特速率的sf倍如果你直接拿码片级SNR去套理论误码率公式曲线会对不上新手特别容易在这里栽跟头。手动加噪声的思路是这样。设目标Eb/N0是EbNo_dB先把它换算成线性值EbNo_lin 10^(EbNo_dB / 10); Eb sum(tx_total.^2) / (K * N_bits); % 每个用户每个比特携带的能量 N0 Eb / EbNo_lin; % 噪声功率谱密度 noise_std sqrt(N0 / 2); % 实信号的双边谱 rx tx_total noise_std * randn(size(tx_total));这个noise_std的理论依据是加性高斯白噪声的双边功率谱密度是N0/2在基带抽样模型里每个样本的噪声方差就是N0/2。取平方根就是标准差。算完后你会发现Eb/N0越高N0越小噪声标准差越小信号被污染得越轻。这套推导在后续所有通信仿真里都能复用。3.2 接收机核心处理链路接收端的任务是从混有噪声的rx里把用户自己的比特捞回来。算法就是相关解扩把接收信号按码片位置对齐后逐码片乘上用户码再对每个比特窗口内的sf个值求和最后看符号。代码实现function bits_hat ds_cdma_rx(rx, user_code, sf) % rx: 1x(N_bits*sf) 接收信号 % user_code: 1xsf 扩频码 % bits_hat: 1xN_bits 恢复的比特 N_bits numel(rx) / sf; data reshape(rx, sf, N_bits); % 每一列对应一个比特窗口 z user_code * data; % 相关累加1xN_bits bits_hat z 0; % 判决负值对应发送端的1映射到比特1 end这里user_code * data是一行矩阵乘user_code是1×sfdata是sf×N_bits乘完得到1×N_bits的相关值。当信号正确同步且没有噪声时相关值z会非常接近±sf判决几乎不会出错。z 0那句看起来简单但实际上是把“负值”判为比特1。为什么因为发送端把比特1映射成1乘以码片后相关累加结果为正的大数反过来比特0映射成-1相关累加结果为负。所以当z小于0时对应的原始比特是0但上面我写的是z 0判为1这里要小心如果user_code * data的符号对应回原始映射需要结合码的方向判断。更稳妥的写法是z user_code * data; bits_hat double(z 0); % 相关值大于0 原始比特为1为什么z 0就判为1因为发送端把比特1映射为1扩频后每个码片是1或-1再乘以用户码后解扩时user_code与码片序列做相关。如果原始比特是1所有码片与用户码的乘积经相关累加后等于sf如果原始比特是0映射为-1累加结果等于-sf。所以相关值正对应比特1负对应比特0。这版更符合直觉我在项目里用的也是这个判断。3.3 误码率仿真与理论曲线对照单用户DS-CDMA在AWGN信道下的误码率理论上和普通BPSK完全一样因为扩频只是把能量摊开解扩后又收回来每比特能量在判决点不变。理论公式是BER 0.5 · erfc(√(Eb/N0))为了验证仿真链路对不对我会跑一组Eb/N0从0dB到10dB的蒙特卡洛仿真对比仿真误码率和理论曲线EbNo_dB 0:2:10; ber_sim zeros(size(EbNo_dB)); N_trials 2000; % 每个SNR点发2000轮降低统计抖动 for idx 1:numel(EbNo_dB) err_count 0; bit_count 0; for trial 1:N_trials bits randi([0 1], 1, N_bits); tx ds_cdma_tx(bits, code, sf); Eb sum(tx.^2) / N_bits; N0 Eb / 10^(EbNo_dB(idx)/10); rx tx sqrt(N0/2) * randn(size(tx)); bits_hat ds_cdma_rx(rx, code, sf); err_count err_count sum(bits ~ bits_hat); bit_count bit_count N_bits; end ber_sim(idx) err_count / bit_count; end EbNo_lin 10.^(EbNo_dB/10); ber_theory 0.5 * erfc(sqrt(EbNo_lin)); semilogy(EbNo_dB, ber_sim, o, EbNo_dB, ber_theory, -); grid on; legend(仿真BER, 理论BER);跑完你会发现仿真点和理论曲线贴得非常近。如果出现系统性偏移八成是Eb算错或者噪声功率定义错了去检查3.1节那几行。这一步通过说明发射、信道、接收三个模块串联没有问题可以放心进入多用户场景。4. 多用户场景从单用户到码分多址的深水区4.1 多用户干扰的来源CDMA真正的价值在“多用户”。K个用户同时发射时接收端解扩某个用户时其他用户的信号不会被完全滤除。理想情况下Walsh码正交内积为零但实际中只要有一点点非理想因素这个等号就保不住。干扰数学上长这样解扩用户1时其他用户j的残留干扰是b_j · c_j, c_i / sf。当码完全正交且完全同步时c_j, c_i 0残留干扰为零但码相位偏移、信道多径、码片波形失真都会让内积不再为零于是形成多址干扰MAI。在仿真里最容易制造MAI的方法就是让两个用户的码片“错开”。你可以在发射时给用户2的信号整体右移几个码片位置然后观察接收机性能变化。错位1个码片正交性就会明显下降BER立刻恶化。这个实验强烈建议亲手跑一下它能让你直观感受到同步对CDMA的致命影响。4.2 用户数变化时BER怎么变固定Eb/N0在8dB我跑了一组用户数从1到16的实验。为了公平每个用户都分到相同的码功率接收端解扩目标用户时都对其他用户直接“无视”。仿真的结果大致如下用户数K仿真误码率约现象12e-4接近单用户理论值24e-4开始出现可见劣化41e-3多址干扰明显85e-3误码率上升一个量级162e-2通信质量已经很差这不是代码bug这是CDMA系统的本质在纯Walsh码理想同步下正交性应该保证互不干扰但仿真里经常因为用户的码功率定义、叠加顺序或者边界效应出现微小相关泄漏再加上有限长度码片效应多用户性能就会下降。现实中CDMA还叠加远近效应和功率控制问题性能只会更复杂。这个实验的代码就是把发射端循环起来tx_total zeros(1, sf * N_bits); for k 1:K bits_k randi([0 1], 1, N_bits); tx_k ds_cdma_tx(bits_k, walsh(k, :), sf); tx_total tx_total tx_k; end % 加噪声然后对每个用户分别解扩统计BER注意所有用户的信号以同样的功率叠加所以总能量会随着K增大Eb计算也必须按“每用户每比特”来算否则SNR定义就乱了。实际计算时我用sum(tx_total.^2) / (K * N_bits)取平均这样不同用户数的仿真才公平可比。4.3 同步问题为什么绕不开前面反复提到同步这里展开说。CDMA接收机必须知道目标用户码片的准确时钟位置才能把接收信号按码片窗口切分。如果你在接收端用错了码相位哪怕只偏1个码片相关器输出会从“能量汇聚”变成“半个信号聚合”判决变量缩减一半甚至被噪声吞掉。仿真里你可以故意这么干rx_shifted [rx(2:end), 0]; % 人为把接收信号右移一个样本然后送入接收机观察BER瞬间拉高。我验证过6dB下本来误码率该在1e-3量级相偏1码片后直接掉到0.1量级惨不忍睹。这和真实系统的体验完全一致——所以实际CDMA系统里都有专门的定时同步模块延迟锁定环DLL和载波同步模块先完成同步、再做解扩。在纯基带仿真里我们通常假设“理想同步”所以这个坑容易被忽略。但你要是哪天把仿真做到通带版本、加进频偏和多径同步就是最花钱最耗精力的部分。5. 常见问题与排查技巧实录5.1 误码率居高不下的常见原因做这个项目过程中我见过最多的问题集中在下面几个点。整理成表按出现频率排序现象可能原因解决办法BER接近0.5解扩时用的码相位错了检查接收端码片是否与发送端严格对齐BER仿真曲线比理论差很多Eb/N0换算错误用了awgn函数手动加噪声按3.1节推导计算单用户BER正确多用户崩了用户码选重了检查hadamard(sf)取的是不是不同行多用户仿真BER随K不变叠加时各用户信号功率没控制好计算Eb时除以K确认每个用户功率一致reshape报错信号长度不是sf的整数倍检查sf*N_bits是否匹配还有一个特别隐蔽的坑发送端把比特映射成±1后如果中途你把信号存成了logical类型所有负数会变成0扩频信号全毁。MATLAB里逻辑数组很坑调试时记得看工作区变量类型。5.2 仿真跑得慢怎么办蒙特卡洛仿真最怕大吞吐量。N_bits1000、SNR点6个、每个点2000轮单用户还好多用户16个码循环起来就开始卡了。我的优化三板斧第一预分配所有数组。不要在循环里让向量动态增长先在循环外zeros(1, sf*N_bits)分配好。第二向量化运算替代循环。比如发射链路里那个repmat和reshape就是向量化接收端相关直接用矩阵乘避免对每个比特写for循环。第三把Eb、N0的标量计算提到蒙特卡洛循环外。不需要每轮都重新算功率一轮仿真里发射信号能量几乎不变可以近似用固定值。如果数据量再大可以考虑把外层SNR循环改成parfor并行但注意你的随机数生成要处理好不然并行结果不可复现。我一般用小规模串行调试正确大规模再并行提速度。5.3 后续扩展方向这套基带仿真跑通之后往下的扩展路线很清晰。第一个方向是加多径信道把AWGN换成带时延扩展的多径模型接收端改成Rake接收机你会看到扩频系统抗多径的本质优势——因为码片时间短多径分量可以被分离合并。第二个方向是加信道编码卷积码或Turbo码套在CDMA外面误码率曲线会进一步压低这也是3G系统里真实存在的级联结构。第三个方向是换成PN码并模拟异步到达Walsh码的“完美正交”会被现实打破你能真实感受到多用户干扰的恶化和功率控制的重要性。我个人在实际操作中体会最深的是仿真代码写通了对CDMA的理解才算真正落地。这篇东西看起来只是在“调代码”其实每一步都在回答通信原理课本里的核心问题——正交性意味着什么同步为什么重要多用户干扰怎么产生。建议你跑完全部实验后再把Walsh码换成PN码、给信号人为加偏移亲手看一眼BER怎么变化。那种直观冲击比看十遍教材都管用。这个项目的后续空间也很大多径、同步、功率控制都是能继续玩上几个月的方向。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑