简介:本资源面向通信工程、无线网络方向的学习者与研究者,提供LTE下行链路系统级仿真的完整代码实现,帮助理解从eNodeB到UE的物理层处理、信道建模、资源分配与多用户调度等核心机制。压缩包共40个文件,全部为m脚本文件,整体约35KB,涵盖物理层编解码、调制解调、信道估计与均衡、CRC校验、OFDM收发、资源块映射以及SISO链路参数配置等模块,脚本间调用关系清晰,便于按流程逐步调试与二次开发。资源围绕单输入单输出场景展开,涉及PDSCH与PDCCH信道处理、CQI反馈、干扰影响及链路级与系统级仿真的差异对比,适合作为课程设计、毕业设计或算法验证的参考工程。目前已有330人学习下载,可帮助读者快速搭建仿真环境、复现下行链路处理流程,并在此基础上评估调度策略与功率控制对网络性能的影响。
1. SISO.zip 里的 LTE 下行链路仿真:从链路到系统级到底在算什么
打开一个名为 SISO.zip 的 LTE 仿真工程,很多人第一反应是找 main 函数,结果翻遍目录发现入口散落在链路级和系统级两套脚本里。这个标题其实指向一条完整的 LTE 下行仿真链路:SISO 单入单出天线配置、链路级误块率曲线、系统级小区吞吐与调度、以及下行共享信道的资源映射。它解决的是「没有商用基站和终端,怎么在本地把 LTE 下行从调制到调度跑通并拿到可信曲线」这个问题。适合通信专业学生、基带算法工程师、以及需要做链路预算和调度策略验证的从业者。链路级关心的是单条链路的 BLER 与 SNR 关系,系统级关心的是多用户、多小区下的资源分配和干扰。两者共用一套参数配置,但时间尺度和抽象层次完全不同。把这两层混在一起跑,是新手最常见的翻车点。
2. LTE 下行链路级仿真:从传输块到 BLER 曲线的最小闭环
2.1 为什么先做链路级而不是直接上系统级
链路级仿真的价值在于它把物理层的每个环节都显式展开:CRC 附着、码块分割、Turbo 编码、速率匹配、加扰、调制、层映射、预编码、RE 映射、OFDM 符号生成,再到接收端的解调、解扰、解速率匹配、Turbo 译码。每一步都可以单独打点看星座图、看软比特、看误块率。系统级仿真则把这些环节压缩成一个链路到系统映射表,用 SINR 查 BLER 的方式替代完整物理层。如果链路级没跑通,系统级查表就是查一个错误的表,后面所有调度结论都不可信。
SISO 配置下,层映射和预编码退化为恒等操作,这反而让初学者能把注意力放在编码链路和信道估计上。常见做法是先用 AWGN 信道验证编译码链路,再换 EPA、EVA、ETU 等多径信道看频率选择性衰落的影响。
2.2 用 MATLAB 搭一条 SISO 下行链路的最小代码
下面这段代码覆盖从传输块到 BLER 统计的核心流程,参数按 20 MHz 带宽、正常 CP、FDD 帧结构设置。实际工程里我会把每个模块拆成独立函数,这里为了看清数据流放在一起。
% SISO LTE 下行链路级仿真最小闭环 % 参数:20MHz带宽,正常CP,FDD,传输块大小按MCS索引查表 clear; clc; % ---- 1. 基本参数 ---- N_rb = 100; % 资源块数,20MHz对应100个RB N_sc = 12; % 每个RB的子载波数 N_sym = 7; % 每个时隙的OFDM符号数(正常CP) N_cp = 4.7e-6; % 正常CP长度(秒) fs = 30.72e6; % 采样率 MCS = 10; % 调制编码等级,对应QPSK 1/3左右 TBS = 2792; % 传输块大小,按MCS和RB数查36.213表 SNR_dB = 0:2:20; % 信噪比扫描范围 % ---- 2. 生成传输块 ---- tx_block = randi([0 1], TBS, 1); % ---- 3. CRC附着 ---- poly_crc = comm.CRCDetector('Polynomial','CRC24A'); tx_crc = poly_crc(tx_block); % 附加24位CRC % ---- 4. 码块分割与Turbo编码 ---- % 简化处理:假设单码块,实际需按6144分割 turbo_enc = comm.TurboEncoder('TrellisStructure', ... poly2trellis(4,[13 15 17]), 'InterleaverIndicesSource','Input port'); K = length(tx_crc); intrlvr = randperm(K)'; tx_enc = turbo_enc(tx_crc, intrlvr); % ---- 5. 速率匹配(简化:截断或重复到目标比特数)---- G = N_rb * N_sc * N_sym * 2 * 2; % 可用比特数,QPSK每符号2比特 if length(tx_enc) >= G tx_rm = tx_enc(1:G); else tx_rm = repmat(tx_enc, ceil(G/length(tx_enc)), 1); tx_rm = tx_rm(1:G); end % ---- 6. 加扰 ---- c_init = 0; % 简化小区ID为0 scramble_seq = randi([0 1], G, 1); % 实际用Gold序列生成 tx_scr = xor(tx_rm, scramble_seq); % ---- 7. QPSK调制 ---- tx_sym = 1/sqrt(2) * ((1-2*tx_scr(1:2:end)) + 1j*(1-2*tx_scr(2:2:end))); % ---- 8. 资源映射与OFDM ---- grid = zeros(N_rb*N_sc, N_sym); grid(1:length(tx_sym)) = tx_sym; tx_ofdm = ifft(grid, N_rb*N_sc); % ---- 9. 信道与接收 ---- ber = zeros(size(SNR_dB)); for i = 1:length(SNR_dB) snr_lin = 10^(SNR_dB(i)/10); noise = (randn(size(tx_ofdm)) + 1j*randn(size(tx_ofdm)))/sqrt(2*snr_lin); rx_ofdm = tx_ofdm + noise; % 接收端:FFT、均衡(AWGN下理想)、解调 rx_grid = fft(rx_ofdm, N_rb*N_sc); rx_sym = rx_grid(1:length(tx_sym)); rx_bits = zeros(G,1); rx_bits(1:2:end) = real(rx_sym) < 0; rx_bits(2:2:end) = imag(rx_sym) < 0; % 解扰、解速率匹配、Turbo译码 rx_desc = xor(rx_bits, scramble_seq); % 简化:直接比较比特,实际需软判决译码 ber(i) = sum(rx_desc ~= tx_rm) / G; end semilogy(SNR_dB, ber, 'b-o'); xlabel('SNR (dB)'); ylabel('BER'); grid on; title('SISO LTE 下行链路 BER 曲线');这段代码的逻辑说明:CRC 附着用 CRC24A 是 LTE 下行传输块的标配;Turbo 编码的网格结构[13 15 17]对应 LTE 规定的生成多项式;速率匹配这里做了简化,实际工程要按 36.212 的循环缓冲和冗余版本处理。参数方面,TBS必须和MCS、N_rb匹配,查 36.213 的 Table 7.1.7.2.1-1,随便填会导致码率异常。SNR_dB扫描范围建议从 -5 到 25,步长 1 到 2 dB,太粗看不出瀑布区。
2.3 信道模型怎么选:EPA、EVA、ETU 的适用边界
LTE 链路级仿真常用的多径信道有三类:EPA 对应低时延扩展,EVA 对应中等,ETU 对应高时延扩展。SISO 配置下,频率选择性衰落会让某些子载波深衰落,均衡器必须能跟踪。常见做法是在接收端加 MMSE 均衡,用估计的信道频响做补偿。如果只做 AWGN,曲线会过于乐观,系统级查表时 SINR 到 BLER 的映射会偏。
| 信道模型 | 时延扩展 | 典型场景 | 最大多普勒 |
|---|---|---|---|
| EPA | 低 | 室内热点 | 5 Hz |
| EVA | 中 | 城市宏小区 | 70 Hz |
| ETU | 高 | 恶劣城市 | 300 Hz |
选信道时要注意:多普勒频移决定信道估计的插值密度,高速场景下导频符号间隔要更密,否则信道估计误差会吃掉编码增益。
3. 系统级仿真:多小区调度与 SINR 到吞吐的映射
3.1 系统级仿真的抽象层次与链路到系统映射
系统级仿真不跑完整物理层,而是把链路级得到的 BLER-SINR 曲线做成查找表。每个用户在每个资源块上计算 SINR,查表得到 BLER,再结合 HARQ 和调度算法决定实际传输速率。SISO 配置下,小区间干扰是主要限制因素,特别是下行共享信道在小区边缘的 SINR 会明显恶化。
系统级仿真的时间尺度是 TTI 级,每个 TTI 做一次调度。常见调度算法有轮询、比例公平、最大 C/I。比例公平在吞吐和公平性之间折中,工程上最常用。实现时要维护每个用户的平均速率,调度优先级按瞬时速率除以平均速率排序。
3.2 用 MATLAB 搭一个多小区下行调度循环
下面这段代码模拟 7 个小区、每小区 10 个用户的下行调度,核心是 SINR 计算和比例公平调度。
% LTE 系统级下行调度仿真:7小区,每小区10用户 % 抽象层次:TTI级,SINR查BLER表,比例公平调度 clear; clc; % ---- 1. 仿真参数 ---- N_cell = 7; % 小区数,中心1个加周围6个 N_ue_per_cell = 10; % 每小区用户数 N_rb = 100; % 总资源块数 N_tti = 1000; % 仿真TTI数 ISD = 500; % 站间距(米) P_tx = 46; % 基站发射功率(dBm) BW = 20e6; % 带宽 noise_dBm = -174 + 10*log10(BW) + 7; % 噪声功率(含噪声系数) % ---- 2. 用户位置生成 ---- ue_pos = zeros(N_cell, N_ue_per_cell, 2); for c = 1:N_cell % 小区中心坐标(六边形近似) angle = (c-1)*pi/3; center = ISD * [cos(angle), sin(angle)]; if c == 1, center = [0, 0]; end for u = 1:N_ue_per_cell r = ISD/2 * sqrt(rand()); theta = 2*pi*rand(); ue_pos(c,u,:) = center + r*[cos(theta), sin(theta)]; end end % ---- 3. 大尺度路损 ---- alpha = 3.5; % 路损指数 PL = @(d) 128.1 + 37.6*log10(max(d,35)/1000); % 36.942路损模型 % ---- 4. 比例公平调度主循环 ---- avg_rate = ones(N_cell, N_ue_per_cell) * 1e6; % 初始平均速率 cell_throughput = zeros(N_tti, N_cell); for tti = 1:N_tti for c = 1:N_cell % 计算每个用户的瞬时SINR(简化:取中心RB) sinr = zeros(N_ue_per_cell, 1); for u = 1:N_ue_per_cell d_serv = norm(squeeze(ue_pos(c,u,:))); sig_power = P_tx - PL(d_serv); % 干扰来自其他小区 interf = 0; for cc = 2:N_cell if cc == c, continue; end d_int = norm(squeeze(ue_pos(c,u,:)) - squeeze(ue_pos(cc,1,:))); interf = interf + 10^((P_tx - PL(d_int))/10); end noise_lin = 10^(noise_dBm/10); sinr(u) = 10*log10(10^(sig_power/10) / (interf + noise_lin)); end % 比例公平:优先级 = 瞬时速率 / 平均速率 inst_rate = log2(1 + 10.^(sinr/10)); % 简化香农公式 priority = inst_rate ./ avg_rate(c,:)'; [~, idx] = sort(priority, 'descend'); % 分配RB(简化:前几个用户各分若干RB) rb_per_ue = floor(N_rb / N_ue_per_cell); for k = 1:N_ue_per_cell u = idx(k); rate = rb_per_ue * inst_rate(u) * 180e3; % 每RB 180kHz avg_rate(c,u) = 0.99*avg_rate(c,u) + 0.01*rate; cell_throughput(tti,c) = cell_throughput(tti,c) + rate; end end end % ---- 5. 结果 ---- avg_cell_tp = mean(cell_throughput, 1); fprintf('平均小区吞吐: %.2f Mbps\n', mean(avg_cell_tp)/1e6); fprintf('边缘用户吞吐: %.2f Mbps\n', ... mean(avg_rate(:))/1e6);逻辑说明:路损模型用 36.942 的宏站模型,alpha和常数项决定信号衰减斜率。SINR 计算里干扰只取了其他小区的第一个用户位置做近似,实际工程要遍历所有干扰用户。比例公平的优先级公式里,avg_rate的更新用滑动平均,时间常数 0.01 对应约 100 TTI 的窗口。参数方面,ISD设 500 米是密集城区,设 1732 米是标准六边形网格。N_tti至少 1000 才能让平均速率收敛。
3.3 链路到系统映射表怎么生成和插值
链路级跑出来的 BLER 曲线是离散的 SNR 点,系统级需要连续查表。常见做法是把 BLER 对 SNR 做线性插值,或者拟合一个 S 曲线。注意 BLER 在 0.1 附近变化最快,插值点要密。如果链路级只跑了 AWGN,系统级查表时要加一个衰落余量,通常 2 到 4 dB,具体看信道模型。
提示:链路级和系统级的 SNR 定义要一致。链路级常用每接收天线 SNR,系统级常用 SINR,两者差一个干扰项。映射表里要注明。
4. 避坑与排查:SISO LTE 仿真里最容易翻车的五个点
4.1 传输块大小和 MCS 不匹配导致 BLER 恒为 1
现象:不管 SNR 多高,BLER 始终接近 1,译码全错。原因:TBS 查表时用错了 MCS 索引或 RB 数,导致实际码率远超信道容量。解决:按 36.213 Table 7.1.7.2.1-1 逐项核对,TBS 和 MCS、N_prb 必须严格对应。我一般会在代码里加一个断言,检查 TBS 是否在合法集合里。
4.2 速率匹配的冗余版本搞反导致 HARQ 合并无效
现象:HARQ 重传后 BLER 不下降,和初传一样。原因:每次重传的冗余版本 RV 没变,或者循环缓冲的起始位置算错。解决:按 36.212 的 RV 定义,RV0 从缓冲开头取,RV1 从偏移处取,RV2、RV3 依次。检查k0的计算公式,和N_cb、RV的关系不能错。
4.3 系统级 SINR 计算漏掉天线增益和穿透损耗
现象:小区边缘吞吐偏高,和实际路测差很多。原因:SINR 计算只用了路损,没加天线方向图增益、馈线损耗、建筑物穿透损耗。解决:在路损模型里补上这些项。天线增益按 3GPP 的定向天线模型,穿透损耗按 20 dB 估算。这些参数在链路预算里都有,系统级仿真不能省。
4.4 OFDM 符号定时偏移导致星座图旋转
现象:接收星座图整体旋转,解调误码率偏高。原因:CP 长度和 FFT 窗口没对齐,或者多径时延超过了 CP。解决:检查N_cp和采样率的关系,确保 CP 长度大于信道最大时延扩展。ETU 信道下 CP 要够长,否则加窗或缩短 FFT 窗口。
4.5 比例公平的平均速率更新过快导致调度震荡
现象:调度器在不同用户间频繁切换,吞吐不稳定。原因:avg_rate的滑动平均系数太大,比如用了 0.5,导致平均速率跟踪瞬时值。解决:系数取 0.01 到 0.05,对应 20 到 100 TTI 的窗口。窗口太短公平性差,太长响应慢。
5. 进阶技巧:用链路级曲线反推系统级参数边界
链路级和系统级不是孤立的。我习惯在链路级跑完 BLER 曲线后,提取两个关键点:BLER=0.1 对应的 SNR,以及 BLER=0.01 对应的 SNR。这两个点决定了系统级调度时 MCS 的选择边界。如果系统级某用户 SINR 低于 BLER=0.1 的 SNR,就不该给它分配高阶 MCS,否则 HARQ 重传次数会爆。
具体做法是建一张映射表,行是 SINR 区间,列是可用 MCS 集合。每个 TTI 根据用户 SINR 查表选 MCS,再查 TBS。这张表可以用链路级仿真自动生成:对每个 MCS 跑一遍 BLER-SNR,记录 BLER=0.1 的 SNR 作为该 MCS 的最低工作点。
% 从链路级BLER曲线生成MCS-SINR映射表 mcs_list = 0:28; % LTE MCS索引范围 sinr_threshold = zeros(size(mcs_list)); for m = 1:length(mcs_list) % 对每个MCS跑链路级,得到BLER-SNR曲线 % 这里用简化模型代替实际仿真 snr = 0:0.5:25; bler = 0.5 * erfc((snr - (m*0.8 - 2))/2); % 示意 idx = find(bler <= 0.1, 1); if ~isempty(idx) sinr_threshold(m) = snr(idx); else sinr_threshold(m) = Inf; end end % 系统级调度时:选满足SINR >= sinr_threshold的最高MCS参数说明:mcs_list覆盖 LTE 的 0 到 28,实际调度只用到部分。sinr_threshold是每个 MCS 的最低 SINR,系统级查表时用find(sinr >= sinr_threshold, 1, 'last')选最高可用 MCS。注意这个表依赖信道模型,EPA 和 ETU 的表不能混用。
另一个技巧是验证系统级吞吐是否合理。用香农公式算一个理论上界:C = BW * log2(1 + SINR),再乘以一个效率因子,通常 0.3 到 0.5。如果系统级仿真结果远高于这个上界,说明 SINR 计算或调度有误。我一般会在仿真结束后打印这个对比,作为 sanity check。
最后说一个习惯:每次改参数只改一个,跑完记录曲线。LTE 仿真参数太多,一次改多个,出了问题根本不知道是哪个引起的。这个习惯帮我省了很多后悔药。希望帮到你。
本文还有配套的精品资源,点击获取