用MATLAB实现CDMA链路仿真:扩频、RAKE接收与误码率验证
简介CDMA技术说明及仿真压缩包面向通信专业学生、3G移动通信学习者与MATLAB仿真入门者围绕码分多址这一3G核心技术将原理讲解和代码仿真有机结合帮助读者系统理解扩频通信、码片序列及多址接入机制。包内共9个文件压缩后仅626KB包含doc格式技术文档、m格式仿真脚本、c源码与dll动态库以及dat数据文件覆盖卷积编码、维特比译码、下行多径Rake接收等功能模块可支撑完整的CDMA链路仿真。配套技术文档对CDMA的原理、抗干扰优势、容量特性做了梳理MATLAB脚本涉及码片序列生成、调制解扩、信道衰落模拟和误码率统计等典型步骤适合课程设计、通信原理实验或毕业设计直接参考。目前已有119人学习查看小巧实用便于快速搭建从发射端到接收端的CDMA仿真框架。1. CDMA不只是一堆公式想真正看懂3G就得手推一遍仿真网上下到的“CDMA技术说明及仿真.rar”这类包打开后往往是几页技术说明加几个.m脚本能完整跑通的人不多但跑通了的人对3G和通信的理解会比看十遍书都深。CDMA码分多址是3G多址技术的根WCDMA和CDMA2000都从这一套扩频原理演化出来用MATLAB做链路级仿真是把这个原理从“看懂公式”变成“能自查结论”最直接的手段。这篇笔记按“原理→仿真代码→参数避坑→结果验证”展开适合通信专业学生、刚转3G方向的开发者和所有想把CDMA仿真真正跑起来的人。2. CDMA仿真先立原理扩频、正交码、多径接收怎么一步步落到代码2.1 直接序列扩频为什么所有用户挤在同一个频段还能分清彼此CDMA和FDMA、TDMA最大的区别是频率不分成、时隙不分所有用户在同一频段同时发。区分用户靠的是扩频码——每个用户拿一组互不相同的码片序列把自己的信息比特展开成码片来发。这里的关键参数是扩频因子SFSpreading Factor。一个“1”比特原本占1个符号周期经过64倍扩频就变成64个码片每个码片周期是原来的1/64带宽摊成64倍。接收端知道该用哪一组码把收到的混合信号与目标码片序列做相关运算就能把想解的那个用户“提”出来其他用户的码和目标码不相关在相关运算里被摊平成了类似噪声的残差。把这一步落到MATLAB是整条链路最核心的运算% 参数定义 N 2000; % 发送比特数 SF 64; % 扩频因子码片长度 user_code hadamard(SF); % 64阶Walsh矩阵 walsh_user1 user_code(2, :); % 用户1的扩频码 % 生成0/1比特并映射为双极性 1/-1 data_bits randi([0, 1], 1, N); data_bipolar 2 * data_bits - 1; % 扩频每个比特复制SF次再逐码片乘上Walsh码 tx_spread kron(data_bipolar, walsh_user1);randi([0,1],1,N)生成N个随机0/12*data_bits-1把0变成-1、1变成1因为扩频码是±1序列信息也必须是±1才能直接乘。kron在这里把每个信息比特展开成长度为SF的码片块例如data_bipolar[1,-1]会变成[1*walsh_user1, -1*walsh_user1]展开后的总长度是N*SF。SF越大抗噪声和多址干扰能力越强但占用带宽也越大仿真里取16到128都比较合理64是3G仿真里最常见的默认值。如果数据量很大kron会额外申请一块尺寸为N*SF的大矩阵。改成reshape(repmat(data_bipolar, SF, 1), 1, N*SF) .* repmat(walsh_user1, 1, N)可以得到完全相同的扩频序列内存表现更稳。先用kron跑通正确性再考虑优化内存这是比较稳妥的顺序。CDMA还有一个让运营商心动的地方——软容量。GSM一个载频8个时隙容量是硬上限CDMA里的干扰就是容量用户多一点信噪比就降一点没有清晰的“断崖”边界这就是所谓“软容量”。对应到仿真里这种性质表现为多用户干扰项引发的性能缓慢下降而不是系统突然发不出数据。3G时代的WCDMA和CDMA2000正是这条思路的工程化产物WCDMA是5MHz带宽、3.84Mcps码片速率CDMA2000是1.25MHz带宽、1.2288Mcps码片速率。仿真里的SF可以自由选择但带宽不同直接决定了多径时延能不能被分辨开。2.2 Walsh码正交与PN扰码用户区分的两把钥匙扩频不等于正交。真正把用户分开的是Walsh码同一组Walsh码里任意两行逐点相乘再求和等于0这就是“正交”。MATLAB里hadamard(SF)直接生成Walsh矩阵第一行全是1从第二行开始彼此正交我给用户1取第二行给用户2取第三行两个用户在同一时刻同一频段发接收端各自做相关对方信号贡献为零。正交有一个严厉的前提码片必须严格对齐。仿真里所有用户由同一个时钟驱动这个前提自然满足真实网络要靠基站的时间提前量来对齐上行信号。一旦对齐被破坏Walsh码的正交优势会迅速衰减这也是后面避坑章节里最典型的问题。Walsh码只管区分同一基站下的用户还要区分基站于是引入PN扰码。PN序列是伪随机序列常用m序列或Gold码自相关尖锐、互相关接近0且数量远多于Walsh码适合给不同基站分配。通信工具箱的comm.PNSequence可以生成工程级m序列但如果你用的MATLAB版本没有通信工具箱直接生成随机±1序列也能用% 用随机±1作为简化PN扰码不依赖通信工具箱 pn_seq (randi([0, 1], 1, N*SF) * 2 - 1); % 加扰扩频后的码片流乘以PN序列 tx_scrambled tx_spread .* pn_seq;randi换成comm.PNSequence的计算差别不大但概念上有区别随机±1没有固定周期严格的PN序列有周期且自相关特性是设计过的。单小区单用户仿真里两种结果几乎一样多小区仿真必须换真PN序列因为小区间干扰靠PN的互相关来均匀化。pn_seq的长度必须是N*SF才能和tx_spread对齐在哪个位置生成就在哪个位置乘接收端同样要用这一把“钥匙”解扰。把Walsh码和PN序列放在一起看差别很清晰码型作用数量对齐要求Walsh码区分同一小区内的用户SF个有限码片必须严格同步PN扰码区分小区/扇区数量远大于SF需要周期对齐2.3 RAKE接收与多径合并多径不是故障是多出来的性能无线信道里的反射、绕射会带来多径同一信号沿不同路径到达接收机幅度、时延各不相同。窄带系统把多径视为符号间干扰CDMA因为码片速率高时延差可以大于一个码片周期接收机就能把每条路径“单独捞出来”再合并。这就是RAKE接收机名字取“耙子”的意思每根齿对准一条路径。仿真里我常用一个两径模型第一径直达幅度1时延0第二径反射幅度0.5时延3个码片。接收端开两个相关器各自解扩后加权合并。合并算法有三类选择合并、等增益合并和最大比合并MRCMRC按各径幅度给权重理论信噪比最大。% 两径信道直达径 延迟3个码片的反射径 path1 tx_scrambled; path2 0.5 * [zeros(1, 3), tx_scrambled(1:end-3)]; rx_multi path1 path2; % 加噪声在信道部分统一处理 % RAKE两抽头相关 corr1 reshape(rx_multi .* pn_seq, SF, N); corr2 reshape(([rx_multi(4:end), zeros(1, 3)]) .* pn_seq, SF, N); x1 sum(corr1 .* repmat(walsh_user1, 1, N), 1); x2 sum(corr2 .* repmat(walsh_user1, 1, N), 1); % MRC合并权重0.5对应第二径幅度估计 x_combine x1 0.5 * x2; recv_bits double(x_combine 0);path2的移位拼接把反射径放在3个码片之后[rx_multi(4:end), zeros(1, 3)]则是把同一段时延补偿回来好让相关器对准第二径。相关值x1和x2都是N维向量每个元素对应一个比特的判决度量。repmat(walsh_user1, 1, N)把Walsh码复制成SF×N矩阵与码片矩阵逐列相乘再列求和完成相关积分。RAKE最容易出问题的地方是抽头数抽头比实际路径多多出来的抽头只是在积分噪声性能不升反降抽头时延对不准路径也等于白开一路噪声。仿真里先做“已知信道参数”的理想RAKE再改成估计信道曲线形状会逐步接近真实收发机。2.4 功率控制与近远效应仿真里不做这一条就等着误码率飙升CDMA还有一个绕不开的机制功率控制。原因在于所有用户同时同频如果基站近端的手机用大功率发远端手机的信号会被完全淹没这就是近远效应。GSM靠频率规划规避这个问题CDMA必须靠功率控制。3G系统里有开环和闭环两条路开环是手机根据测量到的下行信号强度估计发射功率反应快但不准闭环是基站测量上行信噪比下发功率控制命令手机按步长通常0.5dB或1dB调整。两者对比功控方式依据反应速度精度开环下行信号强度测量快粗闭环基站下行信令反馈慢细仿真里不会实现整套信令流程但至少要建模“功率补偿”这个行为。一个简化的理想闭环功控% 近端用户A幅度1.0远端用户B幅度0.3 tx_A kron(data_A_bipolar, walsh_user1) .* pn_seq; tx_B kron(data_B_bipolar, walsh_user1); % 无功率控制B几乎被淹没 rx_no_pc tx_A tx_B; % 理想功控按接收能量比例把B抬到与A接近 E_A sum(abs(tx_A).^2); E_B sum(abs(tx_B).^2); gain_B sqrt(E_A / E_B); % 相对增益 tx_B_pc tx_B * gain_B;gain_B是理想化的能量比补偿真正闭环功控有步长、有延迟、有调整周期动态信道下会来回震荡甚至发散。仿真里建议先跑理想功控确认曲线上限再加2dB步长、10ms周期逼近实际。这一条如果不做多用户CDMA仿真的误码率曲线往往没法看。功率控制在协议实现里几乎是个黑匣子没有直观公式但仿真落点反而是最简单的能量关系。建议在新手阶段先接受理想模型理解“为什么需要功控”再去看实际系统的收敛性能。3. 用MATLAB写CDMA链路从比特到误码率的最小可运行代码3.1 发射端比特→Walsh扩频→PN扰码→BPSK调制现在把第二章的原理合成一个能跑的链路。整个脚本分三段发射端生成波形信道注入噪声和多径接收端解扩并统计误码率。先看发射端。% CDMA单用户AWGN链路仿真 —— 发射端 N 2000; % 每试次比特数 SF 64; % 扩频因子 EbN0_dB 0:2:12; % 扫描的比特信噪比 num_trials 50; % 每个信噪比下的重复次数 walsh_code hadamard(SF); walsh_user1 walsh_code(2, :); pn_seq (randi([0, 1], 1, N*SF) * 2 - 1); % 简化PN序列 for idx 1:length(EbN0_dB) err_total 0; for trial 1:num_trials % 随机比特生成与双极性映射 data_bits randi([0, 1], 1, N); data_bipolar 2 * data_bits - 1; % 扩频 加扰 tx_spread kron(data_bipolar, walsh_user1); tx tx_spread .* pn_seq; % tx是等效基带BPSK信号码片幅度为±1 end end这里“等效基带BPSK”的意思是±1序列本身就是基带符号不需要真的去乘一个载波。如果要表现脉冲成型可以用rcosdesign加根升余弦滤波器但那个只改变频谱形状不改变AWGN下的判决结果初学阶段可以省掉——大多数资料包里的仿真也是这么处理的。发射端的关键就是kron加扰它等价于“每个比特模二加Walsh码再模二加PN码”只是把0/1映射成±1后模二加变成了乘法。参数选择上N太小时统计抖动大N太大内存开销大2000比特加50次重复足够把误码率压到1e-3附近的统计噪声水平。3.2 信道注入AWGN与两径衰落怎么选参数信道这一段的核心是噪声功率的换算。Eb/N0表示每比特能量和噪声功率谱密度的比值扩频以后1个比特的信息被拆成SF个码片所以码片能量是Eb/SF。接收端相关解扩会把SF个码片能量累加回Eb噪声也按带宽被“去扩”了。如果直接拿码片能量当Eb误码率曲线会整体右移约10*log10(SF)dB也就是64倍扩频时右移18dB——这是新手最常踩的坑。% 加噪前先算当前比特信噪比对应的噪声方差 Eb SF * mean(abs(tx).^2); % 每比特能量 N0 Eb / (10^(EbN0_dB(idx) / 10)); noise_var N0 / 2; % 实数基带方差 % AWGN信道 rx tx sqrt(noise_var) * randn(1, N*SF);Eb之所以要乘SF是因为mean(abs(tx).^2)算的是每个码片的平均能量而1个比特含SF个码片。noise_var N0/2对应实数基带模型下噪声功率N0/2如果改用复数基带模型IQ两路噪声要做成(randn 1i*randn)*sqrt(N0/2)。多径信道在这个基础上用卷积实现% 两径信道冲激响应主径 延迟3个码片的反射径幅度0.5 h [1, zeros(1, 2), 0.5]; % zeros数量为delay-1这里对应3码片时延 rx_multi conv(tx, h); rx_multi rx_multi(1:N*SF); % 截断到原始长度 rx rx_multi sqrt(noise_var) * randn(1, N*SF);conv会输出长度为N*SF3的序列第4个采样开始才有反射径的能量所以截掉末尾3个点让主径位置保持对齐。h [1, zeros(1, 2), 0.5]表示时延为3个码片别把数组长度和时延数字搞混。如果只跑AWGN把rx_multi直接赋成tx即可。码片时延必须至少是1——如果设为0两径在采样上完全重叠RAKE也无法区分。这里给出一个常用的参数范围表照着设置可以少走弯路参数推荐范围说明N2000~10000太小BER抖动大太大会消耗内存SF16~128越大抗干扰能力越强但带宽占用越高num_trials20~100控制BER统计稳定性多径时延1~5 chips过小分不开过大截断误差增大功控步长0.5~2 dB越小越精准但收敛时间更长3.3 接收端解扰解扩判决误码率统计接收端是发射端的逆过程先乘回PN序列做解扰再把码片块和Walsh码做相关最后硬判决。代码可以进一步封成函数方便扫EbN0曲线。function ber simulate_cdma_awgn(EbN0_dB, N, SF, num_trials) % simulate_cdma_awgn 单用户CDMA链路AWGN信道误码率仿真 % EbN0_dB : 标量或向量目标比特信噪比(dB) % N : 每试次发送比特数 % SF : 扩频因子Walsh码长度 % num_trials: 每个信噪比下的重复试次 walsh_code hadamard(SF); walsh_user1 walsh_code(2, :); pn_seq (randi([0, 1], 1, N*SF) * 2 - 1); ber zeros(size(EbN0_dB)); for i 1:length(EbN0_dB) err_total 0; bit_total 0; for trial 1:num_trials data_bits randi([0, 1], 1, N); data_bipolar 2 * data_bits - 1; tx kron(data_bipolar, walsh_user1) .* pn_seq; Eb SF * mean(abs(tx).^2); N0 Eb / (10^(EbN0_dB(i) / 10)); noise_var N0 / 2; rx tx sqrt(noise_var) * randn(1, N*SF); % 解扰 derand rx .* pn_seq; % 解扩重排成SF×N矩阵每列是一个比特的码片 chip_block reshape(derand, SF, N); corr walsh_user1 * chip_block; % 1×N相关值 recv_bits double(corr 0); err_total err_total sum(recv_bits ~ data_bits); bit_total bit_total N; end ber(i) err_total / bit_total; end endwalsh_user1 * chip_block是1×SF向量乘SF×N矩阵一步完成所有比特的相关——比逐比特循环快也省去repmat那步内存操作。corr每个元素是某个比特的判决度量为正判成比特1为负判成0。调用函数画曲线EbN0_vec 0:2:10; ber simulate_cdma_awgn(EbN0_vec, 2000, 64, 50); semilogy(EbN0_vec, ber, o-); hold on; EbN0_lin 10.^(EbN0_vec/10); theory 0.5*erfc(sqrt(EbN0_lin)); semilogy(EbN0_vec, theory, -); legend(仿真, 理论BPSK);erfc是补误差函数0.5*erfc(sqrt(EbN0))就是BPSK在AWGN下的理论BER。仿真曲线如果贴在理论曲线附近偏差在0.5dB以内链路模型就是可信的。想跑多用户或RAKE把信道和发射端按第二章片段替换即可。4. CDMA仿真常见问题与避坑Eb/N0换算、正交性失效和RAKE抽头4.1 误码率曲线比理论值偏很多先查Eb/N0换算现象AWGN信道下仿真BER曲线整体向右偏差出来的dB数还正好等于10*log10(SF)比如SF64时约18dB。原因把mean(abs(tx).^2)当成了每比特能量而它只是每码片能量。1个比特摊到SF个码片Eb SF * Ec噪声功率按码片带宽计算信噪比因此被低估。举个例子SF64码片能量为1那么Eb64如果目标Eb/N0是10dBN0应该是6.4而不是1/100.1。差出来的这部分正好通过BER曲线整体平移表现出来。解决加噪前先用Eb SF * mean(abs(tx).^2)算出比特能量再做功率匹配。这个问题几乎每次新写一个CDMA仿真脚本都会遇到属于最典型的一类翻车现场。建议在代码里单独写一行注释把“码片能量”和“比特能量”的换算锁死别省这一步。4.2 用户间干扰怎么都压不下去Walsh码的正交前提失效了现象用户A和用户B用了正交Walsh码可B一发射A的误码率明显抬升而且B的功率越大影响越大不像理论推导那样完全不相关。原因Walsh码正交的前提是码片对齐。如果两个用户的发射时刻相差哪怕一个码片正交性就开始裂开对应到仿真代码常见原因是一个用户带PN扰码、一个不带或者两个用户用了不同时刻生成的PN序列导致码片图案失去对齐关系。解决多用户仿真里发射端必须让所有用户从同一个码片时钟出发扩频用同一套Walsh矩阵的不同行加扰用同一段PN序列的不同偏移。需要模拟不同用户时延差异时要明确做“时延偏移”而不是让索引错位若追求对抗异步场景应该改用Gold码靠互相关特性抗干扰而不是死守正交性。4.3 RAKE抽头数和多径数不匹配合并出来的性能反而更差现象本身两径信道结果不加RAKE性能还行加了两个抽头反而更差。原因RAKE抽头对准的位置不是真实路径把噪声当信号给合并进来了。仿真里常见原因是时延对齐错了——比如h [1, zeros(1, 2), 0.5]是3个码片时延但接收端却用[rx(3:end), zeros(1, 2)]去对齐偏移了一个码片。解决先打印信道冲激响应确认路径位置再逐一核对抽头索引。一个简单做法是% 检查信道抽头位置 h [1, zeros(1, 2), 0.5]; idx find(abs(h) 0.1); % idx[1, 4]接收端抽头必须对准1和4不能想当然用1和3RAKE抽头数的原则是“不多于实际可分辨路径数”不确定路径数时用信道估计相关器输出能量来筛选能量低于阈值比如相对主径-10dB的不要并入。合并权重用估计幅度而不是固定值这样信道一变性能也不会崩。4.4 仿真慢到没法调参码片率、采样率和等效基带的取舍现象一个EbN0点跑几分钟10个点下来半天没了有些脚本把载波、下变频、脉冲成型全部带上数据量膨胀好几个数量级。原因直接把真实发射机的流程“照抄”进MATLAB每个码片又采8个点序列长度膨胀到NSF8再加滤波器卷积整个仿真变成一次大计算。解决链路级仿真应使用等效基带模型——实数基带符号±1就是一个码片一个码片一个采样点。多径时延用整数码片表示脉冲成型先省掉。两者对比建模方式波形形式每码片采样数相对耗时BER结论一致性带通波形已调载波采样8~16高一致等效基带±1实数/复数1低一致这样计算量能降几十倍而BER结论不变。等链路正确之后再按需加带通细节这才是仿真速度和解bug之间的平衡。4.5 同一段代码跑两次结果不一样PN序列和随机种子的锅现象参数一个没改跑两次误码率曲线曲线却明显不同甚至某一两个点像“断了一截”。原因脚本里PN序列和噪声都用randi/randn生成每次运行都是新的随机序列PN序列一变等效于换了一套扰码和种子在小样本下统计波动被放大。解决在脚本开头固定随机种子比如rng(2024)或者把PN序列作为参数传入函数只在信道噪声层保留随机性。设计上建议固定PN序列让唯一的随机源是信道噪声这样同一条BER曲线可以复现调试也容易。5. 验证CDMA仿真结论值得信赖的四个习惯仿真跑通和结论可信是两回事。我自己验证一条CDMA仿真曲线会按四个习惯来把关。习惯一永远先画理论曲线对照。AWGN单用户BPSK的理论BER是0.5*erfc(sqrt(EbN0_lin))MATLAB里一行就能画出来。如果仿真和理论偏差超过0.5dB先怀疑Eb/N0换算和噪声方差而不是怀疑CDMA模型本身。理论上匹配后再动多径、多用户这些变体心里才有底。习惯二固定随机种子、保持单一随机源。我通常rng(2024)置顶PN序列固定只有信道噪声随机。这不是为了“好看”而是为了把变量拆干净——改动一个参数时曲线变化只由这个参数引起而不是随机波动淹没结论。习惯三单用户到多用户分级验证。多用户仿真的失败原因往往同时叠加了功率控制、非正交干扰、信道估计算法等一旦翻车很难说清是哪一环。我习惯先跑通不带功控的单用户AWGN然后加多径、加RAKE最后加多用户和功控。每加一层都要保证上一层结论对得上。习惯四看解扩后的判决统计而不是只看BER。用histogram(corr)画相关值分布它会给我瞬时反馈相关值分布应该围绕0对称正负两簇分得越开判决越可靠。如果分布出现整体偏移多半是双极性映射做错了如果两侧拖尾不对称就要回头查PN序列对齐。这四个习惯做完CDMA仿真才算真正立住了。回到开头的CDMA技术说明及仿真.rar技术说明给你的是公式和图纸仿真给你的是可复现的结论——两者的价值在工程里必须互相验证。我自己的原则是任何新改动都先从“关闭功控、关闭多径”的最低配置跑一遍再做增量这条原则帮我省掉了大量解bug的时间希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →