简介:源自维也纳大学的LTE链路级仿真平台,完整实现了LTE下行两路发射与接收的全过程,涵盖发端编码、信道建模、接收译码等完整信号链,适合通信专业学生、算法工程师及无线通信研究人员用作链路验证与算法调试的基准工具。资源包内共254个文件,其中99个m脚本构成仿真主流程,58个mat文件提供信道与参数数据,并含多平台mex动态库(mexw64/mexa64/mexmaci64)、C语言核心模块、Turbo码与CRC相关源码、PDF说明文档与txt参数配置等,整个压缩包约40.98MB。已有753人学习下载。该平台覆盖参数加载、信道矩阵生成、Rayleigh多普勒模型、Turbo速率匹配、CRC校验、SISO译码与Gold序列生成等关键环节,目录结构清晰,既便于分模块理解,也可直接运行并修改参数开展自定义实验,是入门LTE物理层仿真和高阶二次开发的高质量参考。
1. 维也纳大学的 LTE 链路级仿真平台:两路发射到底怎么跑通
跑过 LTE 链路级仿真的人见面第一句话常常是:你把两路天线那套整过程跑通没有。这里说的“两路”不是两根线,而是 eNodeB 侧的两个发射端口——从小区参考信号的位置、发射分集的编码方式,到接收端 MIMO 检测的维度,全都要跟着一起调。维也纳大学出品的这套 Matlab 平台,把传输块加 CRC、Turbo 编码、加扰、资源映射、OFDM 调制,到接收端同步、信道估计、检测译码的完整闭环做成了能直接改参数的脚本,不需要 USRP 或任何射频硬件,一台普通 PC 装好 Matlab 就能整过程复现。适合三类人:做 LTE 物理层课程设计的学生、要快速出 PDSCH 误块率曲线的工程师、以及想在 Matlab 里对比 MIMO 算法性能的研究者。
2. LTE 链路级仿真的整条链路:两路天线下的发射端和接收端拆解
链路级仿真和系统级仿真最大的区别在观察粒度:系统级看小区吞吐量和切换,链路级只看一个 eNodeB 到一个 UE 之间的物理层过程。这套平台把粒度压到了资源元素(RE)级别,所以发射端、信道、接收端是三个可以独立修改的模块。
2.1 发射机:传输块到发射波形的七个环节
平台对单个传输块的处理顺序是:CRC 附加、码块分段、Turbo 编码、速率匹配、加扰、调制、层映射与预编码、资源映射、OFDM 调制。其中“层映射与预编码”就是标题里“两路”的核心位置。两路发射不是简单地把一路数据复制成两份,而是先经过发射分集编码(SFBC),把两个符号映射到两个天线端口上,利用空间维度的冗余换取抗衰落能力。
% 小区级参数:决定了加扰序列和参考信号位置 cellPar.NCellID = 0; % 物理层小区标识 PCI,0~503 cellPar.CyclicPrefix = 'Normal'; % 常规循环前缀 cellPar.NDLRB = 50; % 10 MHz 带宽对应 50 个 PRB % PDSCH 传输格式:两路天线的入口配置 pdschPar.TxScheme = 'TxDiversity'; % 发射分集,对应两路端口 pdschPar.Modulation = 'QPSK'; % 可选 QPSK / 16QAM / 64QAM pdschPar.RNTI = 1; % 终端临时标识,参与加扰初始化 pdschPar.PRBSet = (0:49).'; % 占满整个 10 MHz 带宽这里的NCellID不是随便填的编号,它直接影响扰码生成器和参考信号的频域位置。NDLRB = 50必须和后面 OFDM 调制的 FFT 点数对应,否则波形带宽就是错的。TxDiversity是 LTE 里两路发射最标准的传输方案,平台默认用这个模式跑整条链路,实际对应的是 SFBC 编码,两个端口发送的是同一份信息的空频编码版本。改参数时最容易翻车的地方是Modulation和码率不匹配——64QAM 配高码率在衰落信道下 BLER 会直接抬到下不来。
2.2 信道与两路天线:SNR 到底定义在哪个端口
发射端输出了两路的 OFDM 波形,接下来进入信道模型。平台支持 AWGN 和多径衰落信道,多径常用 EPA、EVA、ETU 三种模型,分别对应低、中、高时延扩展场景。两路发射两路接收(2×2)的配置下,信道矩阵是 2×2 的,每个接收天线收到的都是两个发射端口信号的叠加。
% 信道模型配置(以 EVA 70 Hz 为例) channelPar.Model = 'EVA'; % Extended Vehicular A channelPar.DopplerFreq = 70; % 多普勒频移 70 Hz channelPar.MIMOCorrelation = 'Low'; % 两天线相关性设为低 channelPar.SNR = snrValue; % 接收天线端口上的 SNR(dB) % 加噪声时只看接收天线端口的信号功率,不要自己再乘天线数 rxWaveform = awgn(txWaveform, channelPar.SNR, 'measured');这里有个新手常踩的坑:SNR 的定义位置。平台里的 SNR 是“每个接收天线端口上的信号功率对噪声功率”,不是发射总功率对噪声。两路发射时,如果先算两路总功率再除以二,等效到每个端口上的 SNR 就不对了。我一般会先固定channelPar.SNR的定义方式,然后在不同发射天线数之间做对比时保持同一套定义,不然四条 BLER 曲线之间根本没有可比性。多普勒频率这里也建议按场景来设:低移动性测试用 5 Hz,车载场景用 70 Hz,高铁模拟才用到 300 Hz 以上,值设太大会让信道估计跟不上。
2.3 接收机:同步、信道估计、MIMO 检测三步走
接收端拿到波形后不是直接解调的,必须先做帧同步。平台用 PSS/SSS(主/辅同步信号)完成定时同步和小区 ID 确认,然后才是 OFDM 解调、导频位置信道估计、MIMO 检测和 Turbo 译码。信道估计这一步决定了整个接收机的上限:两路端口各自的参考信号位置交错分布,接收端要分别估计出两个端口到本天线的信道响应。
% 接收端核心调用:从波形到比特 rxBits = lteReceiver(rxWaveform, cellPar, pdschPar, channelPar); % 如果只想取中间变量,看信道估计结果 [hest, noiseEst] = lteDLChannelEstimate(rxWaveform, cellPar, pdschPar);检测器部分,平台在发射分集模式下默认用 MRC(最大比合并),把两路信号在频域做共轭加权后合并,复杂度低且性能好。如果改成空间复用模式,就要换 MMSE 或 ZF 检测器,两者在高阶调制下的性能差别很大:MMSE 在 64QAM 下优势明显,但矩阵求逆的运算量也上去了。这里不建议一上来就追求复杂检测器,先把发射分集的整条链跑通、BLER 曲线形状正常,再逐步加复杂度,排查起来会容易很多。
3. 把主循环跑起来:参数配置、SNR 点扫描与 BLER 统计
链路级仿真的主程序结构很固定,但参数之间的耦合关系比看起来多。这一章直接从主脚本出发,讲清楚每个循环变量、每档参数怎么改、结果怎么读。
3.1 主脚本执行顺序与双层循环
平台的主脚本是一个典型的双层循环:外层扫 SNR 点,内层跑子帧。每个子帧独立完成一次“传输块生成 → 发射 → 过信道 → 接收译码 → CRC 校验”的完整过程,最后统计误块率(BLER)。
% 主仿真循环:外层 SNR 点,内层子帧 numSubframes = 200; % 每个 SNR 点仿真的子帧数 snrVec = -5:5:25; % SNR 扫描范围,单位 dB blerVec = zeros(size(snrVec)); % 记录每个点的 BLER for sIdx = 1:length(snrVec) errSubframe = 0; % 错误子帧计数 for nIdx = 1:numSubframes % 随机生成一个传输块 txBits = randi([0 1], 1, trBlkSize); % 发射链路:比特 -> OFDM 波形 [txWaveform, txGrid] = lteTransmit(txBits, cellPar, pdschPar); % 信道:叠加噪声和多径 rxWaveform = lteFadingChannel(channelPar, txWaveform); % 接收链路:波形 -> 译码比特 rxBits = lteReceive(rxWaveform, cellPar, pdschPar); % CRC 校验:统计子帧是否出错 if ~isequal(rxBits, txBits) errSubframe = errSubframe + 1; end end blerVec(sIdx) = errSubframe / numSubframes; end这个循环里有三个变量需要解释清楚。trBlkSize是传输块大小,由带宽、调制阶数和码率共同决定,不能随便填,改错会直接导致速率匹配模块报错。numSubframes的取值决定曲线平滑度:每个点 100 个子帧时曲线抖得厉害,追求平滑就上 500 甚至 1000 个子帧,代价是单点仿真时间线性增长。snrVec建议先粗扫确认曲线整体位置,再在 BLER 从 1 掉到 0.01 的区间内加密采样点,这样曲线既平滑又省时间。
3.2 带宽、调制阶数、码率怎么改
平台的核心参数是带宽和调制方式,这两项决定了一个子帧能塞多少比特。LTE 标准里带宽和 PRB 数一一对应,改带宽必须同步改 FFT 点数和采样率,否则波形时间长度全是错的。
| 带宽 (MHz) | NDLRB | FFT 点数 | 采样率 (MS/s) |
|---|---|---|---|
| 1.4 | 6 | 128 | 1.92 |
| 3 | 15 | 256 | 3.84 |
| 5 | 25 | 512 | 7.68 |
| 10 | 50 | 1024 | 15.36 |
| 20 | 100 | 2048 | 30.72 |
改带宽时最容易漏掉的是采样率。代码里如果用15.36e6写死采样率,把带宽从 10 MHz 改成 5 MHz 后波形时长直接翻倍,频谱也整体压窄,BLER 曲线会高得离谱。我一般会把采样率、FFT 点数、NDLRB 三个值写在一处,改带宽时三个一起换。调制阶数和码率是另一对耦合参数:平台里调制改成 16QAM 后,如果不调整速率匹配的目标码率,等效比特率对不上,BLER 曲线会整体往右平移几个 dB,这属于配置错误不是信道问题。
3.3 结果怎么读:BLER 曲线和吞吐量换算
仿真跑完后,BLER 曲线是判断链路是否正常的第一依据。把blerVec对snrVec画成半对数坐标,正常情况下是单调下降的 S 形曲线,QPSK 在 AWGN 下 0.01 BLER 对应的 SNR 通常落在 0 到 5 dB 区间(具体随码率和 PRB 数变化)。
% 画 BLER 曲线的标准方式 figure; semilogy(snrVec, blerVec, 'b-o', 'LineWidth', 1.5); xlabel('SNR (dB)'); ylabel('BLER'); grid on; ylim([1e-3 1]);很多使用场景其实更关心吞吐量而不是 BLER。吞吐量 = 每子帧成功传输的比特数 × 每秒子帧数。一个子帧 1 ms,所以:
% 从 BLER 折算吞吐量(Mbps) throughput = (1 - blerVec) * trBlkSize * 1000 / 1e6;这个换算隐含一个前提:每一帧都独立重传成功,没有 HARQ 合并增益。如果想仿真带 HARQ 的吞吐量,平台需要额外加增量冗余合并模块,实际做下来发现同等 SNR 下吞吐量能比无 HARQ 高 20% 以上,这不是配置问题,是协议本身机制带来的差异,对比论文结果时要先确认对方是否包含 HARQ。
4. 从 cellid 到双端口参考信号:LTE 小区参数在仿真里怎么落地
链路级仿真虽然只关心一个小区,但小区参数不是摆设。这一章把cellid、PCI、TAC 这些热词逐一落到平台的配置项上,说清楚哪些真的参与物理层计算,哪些只是系统级概念。
4.1 cellid 在仿真平台里是“种子”而不是编号
cellPar.NCellID对应 LTE 的物理层小区标识,范围是 0 到 503,计算公式是PCI = 3 * NID1 + NID2,其中 NID1 是小区组号(0~167),NID2 是组内 ID(0~2)。这个值在平台里同时决定三件事:扰码序列的初始化、参考信号的位置偏移、以及 PSS/SSS 的序列索引。
% 修改小区 ID 会影响哪些函数 cellPar.NCellID = 7; % 改成一个非 0 小区测试 % 影响一:扰码发生器 pnSeq = ltePRBS(cellPar.NCellID, length(txBits)); % 影响二:参考信号频域偏移 refPos = lteCellRS(cellPar, pdschPar);改小区 ID 最直接的影响是参考信号位置变化。如果接收机里的信道估计函数写死了原来小区的导频位置,换了NCellID后估计出来的信道响应全错,BLER 曲线直接变成一条高电平直线。排查方法很简单:先回退到NCellID = 0确认曲线正常,再逐个修改参考信号相关代码。从 0 改到其他值时,参考信号的频域偏移按mod(NCellID, 6)变化,比如 0 和 6 的位置相同,0 和 1 就错开一个子载波。
4.2 双端口参考信号与发射分集的位置关系
两路发射在物理层对应端口 0 和端口 1。每个端口有自己的参考信号,位置在时频网格里交错分布:端口 0 的参考信号放在时隙的第 0 和第 4 个 OFDM 符号,端口 1 放在第 1 个符号,子载波位置也相对偏移。发射分集 SFBC 的编码矩阵是经典的 Alamouti 结构:
天线端口 0 发送: [s0, s1] 天线端口 1 发送: [-conj(s1), conj(s0)]接收端拿到两个端口的叠加信号后,通过两个端口各自的参考信号估计信道,再按 Alamouti 解码恢复s0和s1。平台上如果想查看当前端口的参考信号图样,可以直接调用参考信号生成函数并绘制资源栅格:
% 生成资源栅格并查看端口 0 的参考信号 grid = lteDLResourceGrid(cellPar, pdschPar); grid(lteCellRSIndices(cellPar)) = 1; imagesc(abs(grid)); axis xy;这个图能非常直观地看到两路参考信号在时频域的位置:端口 0 和端口 1 不在同一个 RE 上,保证了信道估计互不干扰。常见误操作是修改导频密度——为了提升信道估计精度把参考信号 RE 全部填满,结果可用数据 RE 减少,BLER 曲线变差。链路级仿真里导频密度和信道估计性能是一对 trade-off,调参前先想清楚当前场景是带宽受限还是估计精度受限。
4.3 TAC 和 eNodeB 参数:哪些跟链路级仿真有关
热词里还有tac(lte 跟踪区)。TAC 是跟踪区码,16 bit,属于系统级参数,和寻呼区、位置更新绑定,不参与物理层比特流计算,所以这套链路级平台里根本没有 TAC 的配置项。会提这个是因为很多人做系统级与链路级级联仿真时,把小区配置表一股脑搬过来,里面 TAC、小区重选参数、邻区列表全都在链路级代码里用不上。判断标准很简单:凡是影响NCellID、NDLRB、CyclicPrefix以外物理层行为的参数才需要保留,TAC和eNodeB名称只用于系统级模块关联场景,链路级仿真直接忽略它们不会影响任何结果。这也是边界意识的一部分:这份资源只管物理层怎么把比特可靠送过去,不管小区该不该被寻呼到。
5. 避坑与排查:五个让仿真结果失真或跑不完的问题
这一章是血泪经验。链路级仿真的代码逻辑不复杂,但环境、编码、参数定义这些小地方最容易让人原地打转,每一条都是我先踩过再绕开的。
5.1 Matlab 2023 之后打开脚本中文注释乱码
现象:下载的脚本函数名和代码正常,但注释部分全是乱码,直接看不了平台自带的中文说明。原因:早期平台脚本用 GBK 编码保存,Matlab R2023a 之后的编辑器默认按 UTF-8 打开,编码不匹配导致注释乱码。解决:在 Matlab 预设里把 MATLAB 源文件编码改成“系统区域设置”,或者用记事本打开脚本另存为 UTF-8 编码再放回平台。我一般是把整套平台脚本统一转成 UTF-8,这样换机器用新版 Matlab 也不会再乱码,别只改一个文件,因为函数之间的调用关系会在调试时来回跳转。
5.2 SNR 定义混乱:EsN0 和 SNR 差了几个 dB
现象:按论文里给的 EsN0 配置仿真,BLER 曲线整体比论文右移 5~6 dB。原因:论文里用的是每符号信噪比 EsN0,把调制阶数、码率和导频开销全部折算进去了;平台里的 SNR 是接收天线端口上信号功率对噪声功率的比值。解决:先确认参考结果的 SNR 定义。从 SNR 换算 EsN0 的公式是EsN0 = SNR + 10*log10(每符号比特数 * 有效码率),QPSK 每符号 2 bit,16QAM 每符号 4 bit,64QAM 每符号 6 bit,再把速率匹配码率和导频开销一起算进去。建议在任何脚本开头固定写明这次仿真用哪种定义,并且改调制方式时同步更新换算公式,这个坑属于不出错则已、出错全曲线作废的类型。
5.3 高速场景信道估计跟不上,BLER 出现地板
现象:ETU 300 Hz 信道下 BLER 曲线在 15 dB 附近突然不再下降,形成平层。原因:多普勒频移太高,导频位置的信道估计在 OFDM 符号间已经过时,LS 估计的精度不够。解决:一是把信道估计从简单的线性插值换成维纳滤波,利用信道的时频相关特性压低噪声;二是接受“链路级仿真在高多普勒下有性能上限”这个事实,不要指望无限堆 SNR。平台通常已经给了插值算法的切换入口,换成维纳滤波后计算量翻倍但 BLER 地板明显下移,这个是值得的。
5.4 双层循环跑几小时不出结果
现象:SNR 点设了 10 个,子帧数设 500,跑完一个点要 20 分钟。原因:纯串行循环,每个子帧都重新生成波形和做完整译码,Turbo 译码本身又是迭代算法,叠加起来时间爆炸。解决:把外层 SNR 循环改成并行循环,用parfor替代for。另外统计逻辑从“固定子帧数”改成“错误子帧达到一定数量就提前结束”——BLER 高时几十帧就能统计出来,BLER 低时多跑一点也正常,这样整体时长能砍掉一半以上。
% 提前终止条件:统计到足够错误就跳出内层循环 minErrs = 30; % 最少错误子帧数 errSubframe = 0; for nIdx = 1:numSubframes % 发射、信道、接收、校验... if frameError errSubframe = errSubframe + 1; if errSubframe >= minErrs break; end end end5.5 带宽改小之后频谱形状不对
现象:把NDLRB从 50 改成 25,跑出来的波形频谱只占预期带宽的一半,BLER 曲线整体恶化。原因:OFDM 调制模块里采样率和 FFT 点数还停留在 20 MHz 的配置,带宽和采样率不一致导致子载波间隔错位。解决:带宽、PRB 数、FFT 点数、采样率四个值必须成套修改,对照 3.2 节的表格一次性改全。从那以后我每次改带宽都先在代码里搜一遍采样率出现的所有位置,确保没有写死值残留在别的函数里。
6. 进阶验证:先用 AWGN 把 BLER 曲线跑成参考值,再改四天线
仿真的第一优先级不是加功能,而是先证明当前链路是对的。我建议把第 3 章的主脚本做一次最小化配置:AWGN 信道、QPSK、1/3 码率、10 MHz 带宽、两路发射分集、两路接收。之所以选这套配置,是因为它在理论上最简单、可解析验证:AWGN 下没有频率选择性衰落,BLER 曲线应该是一条平滑单调下降的 S 形曲线,且 0.01 BLER 对应的 SNR 应与 LTE 参考测量信道的理论范围一致。如果这条曲线形状异常,问题几乎一定出在发射端或接收端的某个配置上,而不是信道模型。
验证时要控制统计精度。BLER 为 0.01 的点至少需要统计到 50 个错误子帧,CLT 算下来相对标准差才在可接受范围。平台如果默认只跑 100 帧,高 SNR 下错误帧太少,画出来的曲线抖动得像噪声。改进方法是给内层循环加“最小错误计数 + 最大子帧数”双重条件,前一种情况先跑满条件再说,这也是第 5.4 节落地的一部分。对比时不要急着拿自己的曲线和别人论文的曲线硬蹭,先确认对方的链路配置——带宽、码率、天线数、信道模型任何一个不同,曲线差 2~3 dB 都非常正常。
验证通过后再做两路到四路的扩展。平台把天线数作为一个显式参数,四路发射分集的标准做法是 SFBC 加频率选择性发射分集(SFBC-FSTD),把两路 Alamouti 扩展到四个端口:在一半子载波上用端口 0 和 1 做 SFBC,另一半用端口 2 和 3,发射分集增益不变但抗深度衰落的频率分集增加了。改的时候需要同步修改参考信号位置,给端口 2 和端口 3 也分配独立的参考信号 RE,否则估计出来的四路信道全是零信道增益。这一步能跑通,平台的两路整过程才算真正扩展成了多天线整过程,后面再往空间复用、MU-MIMO 方向走就有了可靠的地基。从那以后我每跑一次 LTE 仿真,都会先在 AWGN 下用 QPSK 把整条链路自检一遍,确认 BLER 曲线正常再换信道模型和调制方式,这个顺序帮我省掉了大量排错时间,希望帮到你。
本文还有配套的精品资源,点击获取