
简介面向无线通信研究与工程实践的资料包围绕802.11ax核心特性OFDMA展开配套MATLAB仿真代码适合需要理解多用户正交频分多址原理、子载波分配与调度机制的学生和工程师。压缩包共11个文件其中9个m脚本覆盖多用户发射/接收、QAM符号映射、多相调制解调及循环前缀性能对比等关键模块1个mat文件保存仿真所需信道或参数数据1个md文档提供使用说明整体仅29KB轻量易用。已有319人学习浏览。代码以场景化方式演示了子载波资源划分、多用户调度、256-QAM调制、信道估计与均衡、多用户同步以及功率分配等核心环节通过调整参数可直观比较不同配置下的误码率与系统吞吐量脚本按功能拆分便于按需修改与复用。整体上这份资料为理解802.11ax中OFDMA双向多用户传输机制提供了可运行、可扩展的实验基础也可作为相关课程设计或论文验证的起点。1. 从 OFDM 到 OFDMA802.11ax 真正改变的不是波形而是用户调度方式很多人初看 802.11ax 的 OFDMA以为它只是把 OFDM 换了个名字。实际差别在于传统 OFDM 一个时刻只服务一个终端整个 20MHz 带宽上所有子载波全给这个终端用而 802.11ax 的 OFDMA 把子载波划分成一个个资源单元Resource UnitRU不同 RU 可以同时分配给不同终端。这个改动让 Wi-Fi 6 在高密度场景下能同时给几十个终端发小包而不是让它们排队抢信道。用 MATLAB 仿真 OFDMA 链路最关键的是吃透 RU 映射、FFT 尺寸和导频位置三件事。这篇博文直接从 64 点 FFT 的最小可运行链路讲起逐步做到多用户资源分配和 BER 验证适合正在做 Wi-Fi 6 物理层开发、协议测试或毕设的同学。2. 802.11ax OFDMA 的 RU 结构与 MATLAB 最小发送链路的 64 点 FFT 实现2.1 RU 尺寸、子载波数与导频位置对照表802.11ax 定义了五种 RU 尺寸26、52、106、242、484分别对应 2MHz、4MHz、8MHz、20MHz、40MHz 带宽。RU 越大单个用户可用的数据子载波越多但可同时调度的用户数越少。以 20MHz 信道为例整条信道可以分成 9 个 26-RU或者 4 个 52-RU 加 1 个 26-RU也可以 2 个 106-RU 加 1 个 26-RU。RU 内部不是所有子载波都能传数据每个 RU 都预留了导频子载波用于相位跟踪。RU 类型总子载波数数据子载波数导频子载波数对应带宽26-RU26242约 2MHz52-RU52484约 4MHz106-RU1061024或 6约 8MHz242-RU242234820MHz做仿真时最容易犯的错是把 26-RU 当成 26 个数据子载波直接用。协议里 24 个数据 2 个导频才是完整的 26-RU。教学链路为了突出 OFDMA 原理可以暂时忽略导频但一旦要算吞吐量或者做信道估计就得严格遵守这个分配比例。导频的插入位置也不是随意选的802.11ax 的 OFDMA 由 OFDM 继承来的导频值本质上就是一组伪随机序列接收端知道这组序列后可以快速计算残余相位偏差。2.2 用 MATLAB 构造带 4 个用户的 OFDMA 发送帧我一般先把问题缩小到 64 点 FFT。虽然真实 802.11ax 在 20MHz 下是 256 点 FFT但 64 点结构更容易看清子载波索引的来龙去脉。下面是完整的发送端代码它把 4 个用户分别放到两个 26-RU 的左右分段上每个用户实际只用 13 个数据子载波方便演示多用户映射逻辑。% ax_ofdma_tx_demo.m % 802.11ax OFDMA 发送端最小示例64 点 FFT4 用户共享 4 段 RU clear; clc; nFFT 64; % FFT 点数教学用真实 20MHz 为 256 cpLen 16; % 短 GI 对应 0.8us长 GI 时改成 32 nUsers 4; % 同时发送的用户数 % MATLAB 的 ifft 输入频率顺序是 0, 1, ..., 31, -32, -31, ..., -1 freqGrid [0:nFFT/2-1, -nFFT/2:-1]; % 26-RU 教学版左半边低频和右半边高频各取 26 个子载波 % 实际 802.11ax 的 26-RU 内还包含 2 个导频这里先省略 ru26_left (-31:-6); % 低频侧 26 个子载波 ru26_right (6:31); % 高频侧 26 个子载波 % 两个 26-RU 各拆成 1313模拟 4 个用户 ruPools {ru26_left(1:13), ru26_left(14:26), ... ru26_right(1:13), ru26_right(14:26)}; % 每个用户随机产生 BPSK 符号 for u 1:nUsers nDataSc length(ruPools{u}); txBits{u} randi([0 1], nDataSc, 1); syms{u} 2 * txBits{u} - 1; % BPSK: 0 - -1, 1 - 1 [~, binIdx{u}] ismember(ruPools{u}, freqGrid); end % 频域组帧把 4 个用户的符号映射到各自子载波 fdFrame zeros(nFFT, 1); for u 1:nUsers fdFrame(binIdx{u}) syms{u}; end % IFFT 变换乘 sqrt(nFFT) 保证频域时域功率一致 tdSym ifft(fdFrame, nFFT) * sqrt(nFFT); txSig [tdSym(end-cpLen1:end); tdSym]; % 加循环前缀代码里最关键的是freqGrid和ismember的配合。MATLAB 的ifft认为输入数组第一个元素对应频率 0第 33 个元素对应频率 -32。如果直接把负频率子载波塞到数组开头IFFT 后会得到错误时域波形。通过ismember把真实子载波编号转成数组下标可以回避这类索引混乱问题。tdSym * sqrt(nFFT)是功率归一化很多初学 OFDM 的仿真都在这里出错导致 AWGN 信噪比计算偏大或偏小。2.3 用前导 LTS 作为整包信道估计的参照符号OFDMA 接收机要做均衡就得知道每个子载波经历了什么幅度和相位变化。802.11ax 沿用 OFDM 的长训练序列LTS思想发送端在数据符号前插入一段收发双方都知道的序列接收端把收到的 LTS 与本地 LTS 相除就得到信道响应的最小二乘估计。下面这段代码生成 64 点 LTS并把它插在数据符号前面。% 生成 64 点 LTS只在数据子载波上放序列其他位置补 0 ltsFreq zeros(nFFT, 1); for u 1:nUsers ltsFreq(binIdx{u}) 1; % 占位实际应放已知 BPSK 序列 end ltsTime ifft(ltsFreq, nFFT) * sqrt(nFFT); ltsWithCp [ltsTime(end-cpLen1:end); ltsTime]; % 组帧LTS 数据符号 txFrame [ltsWithCp; txSig];这里 LTS 的值全部置 1 只是为了演示真实系统要使用协议规定的序列值。LTS 的高价值在于它的频域幅度恒定与数据符号使用相同的 FFT 网格所以信道估计不需要额外的插值逻辑。要注意的是 LTS 必须和数据符号用同一个nFFT和cpLen否则频域子载波对应关系会错位。在 802.11ax 中前导部分还区分了传统 LTS 和 HE-LTF后者支持多流和 OFDMA 多用户估计但原理都是同一套 LS 估计思想。3. 接收机的同步与 LS 信道估计MATLAB 中完成 FFT 窗口对齐、频域均衡与用户解映射3.1 用滑动相关确定 FFT 窗口起点发送端加循环前缀之后时域信号变成了连续流。接收端要做的第一件事不是 FFT而是找到每个符号从哪里开始。常见做法是利用 LTS 的重复性把收到的信号与本地 LTS 做滑动互相关相关峰出现的位置就是符号起点。MATLAB 里处理这个问题的典型写法如下。% rxSig 为收到的时域信号假设已经完成基本的自动增益控制 ltsLocal ltsWithCp; % 本地完整的 LTS 波形 corrLen length(rxSig) - length(ltsLocal) 1; corr zeros(corrLen, 1); for n 1:corrLen seg rxSig(n : n length(ltsLocal) - 1); corr(n) abs(sum(seg .* conj(ltsLocal))); end [~, peakIdx] max(corr); % 相关峰位置 startIdx peakIdx cpLen; % 从 LTS 数据起点开始取数据符号滑动相关的计算量是 O(N*M)在大型仿真里不建议直接循环可以把循环改成xcorr或矩阵化卷积。startIdx的推导逻辑是相关峰落在 LTS 第一个采样点上循环前缀占cpLen个采样点所以数据符号的起点在峰位置之后跳过 CP。实际系统中还会有采样频偏和定时漂移802.11ax 接收机通常在相关峰附近做抛物线插值来获得小数倍时延估计但数学链路里用最大值搜索已经够用。3.2 LS 信道估计函数及其频域平滑处理收到 LTS 后把它做 FFT 得到频域响应与本地 LTS 的频域响应逐点相除即可得到每个数据子载波的信道估计值。这一步叫最小二乘估计公式是 H_hat Y / X。它的优点是简单直接缺点是在低信噪比时噪声会被直接放大。解决方法是把相邻子载波的估计做滑动平均因为真实信道的频域响应通常是缓慢变化的。function H ax_estimate_channel(rxLts, ltsTime, binIdx, nFFT) % 输入接收到的 LTS 时域波形和本地 LTS 时域波形 % 返回所有数据子载波位置的信道估计 fdRx fft(rxLts(1:nFFT), nFFT) / sqrt(nFFT); fdTx fft(ltsTime(1:nFFT), nFFT) / sqrt(nFFT); H zeros(nFFT, 1); H(binIdx{:}) fdRx(binIdx{:}) ./ fdTx(binIdx{:}); % 频域滑动平均窗口长度 3 个子载波 Hf H; for k 2:nFFT-1 Hf(k) mean(H(k-1:k1)); end H Hf; end这段代码里binIdx{:}把所有用户的下标一次性传入方便在多用户场景下复用。滑动平均窗口建议取奇数3 或 5 都行窗口太大会把频率选择性信道的深衰落抹平导致均衡后残留误差变大。如果你的仿真信道模型只有两条径窗口取 3 比较安全如果做 5 条径以上的多径信道可以取 1也就是不做平滑用原始 LS 估计直接均衡。3.3 单个用户子载波提取与 BPSK 判决的完整流程信道估计完成后把数据符号做 FFT再按每个用户的binIdx把对应子载波取出来逐点除以信道估计值就完成了频域单抽头均衡。整个接收链路代码继续接在前面发送端代码后面运行。% 构造两径信道验证接收机有效性 chTaps [1, 0.5 * exp(-1j * 0.8)]; rxSigNoNoise filter(chTaps, 1, txFrame); rxSig awgn(rxSigNoNoise, 20, measured); % 用 LTS 做粗同步后面以 startIdx 为准取数据符号 startIdx 1; % 实际由 3.1 的相关峰给出 % 接收数据符号 FFT rxSym rxSig(startIdx length(ltsWithCp) : startIdx length(ltsWithCp) nFFT - 1); fdRx fft(rxSym, nFFT) / sqrt(nFFT); % 提取 LTS 并估计信道再均衡 rxLts rxSig(startIdx : startIdx nFFT - 1); H ax_estimate_channel(rxLts, ltsTime, binIdx, nFFT); for u 1:nUsers rxUserSym fdRx(binIdx{u}) ./ H(binIdx{u}); rxBits{u} real(rxUserSym) 0; % BPSK 硬判决 ber(u) mean(rxBits{u} ~ txBits{u}); end fprintf(各用户 BER: %s\n, mat2str(ber, 3));均衡后的rxUserSym理论上应该落在实数轴 ±1 附近所以 BPSK 直接取实部判断正负即可。这里startIdx length(ltsWithCp)的写法假设 LTS 与数据符号紧挨着且整个帧在信道中经历的延迟已经包含在同步结果里。两径信道chTaps的第一个抽头增益为 1第二个抽头幅度 0.5、相位偏移 0.8 弧度这已经足够产生明显的频率选择性衰落能检验均衡算法是否真的在工作。如果你的 BER 算出来接近 0.5优先检查 FFT 窗口是否取错而不是怀疑信道估计代码。4. RU 资源分配策略与比例公平算法用 MATLAB 在多用户 OFDMA 中验证调度性能4.1 为什么固定 RU 分配会浪费 OFDMA 的多用户增益如果 4 个用户的信道质量完全一样随便分配 RU 都行。但真实场景里每个用户的位置不同某个用户可能在低频段信号好、高频段信号差另一个用户恰恰相反。频选信道意味着 RU 的优劣是随用户变化的。固定分配一个用户永远占某个 RU 的策略会把这个用户在最差频段的劣势放大。OFDMA 的调度增益就来自这里的自由度每个 RU 送给当前信道条件最好的用户。这种自由度在 OFDM 时代是不存在的因为整条信道只服务一个用户。所以做 802.11ax 仿真时不能只仿真物理层误码率还要仿真资源分配算法。资源分配的目标函数一般是最大化系统吞吐量但只最大化吞吐量会导致信道差的用户永远得不到资源。实际系统需要平衡吞吐量和公平性比例公平算法就是在这个背景下被广泛使用的方案。4.2 比例公平 RU 分配的贪心实现与 MATLAB 代码比例公平的核心思想是每个用户维护一个历史平均速率 T(t)每次调度计算当前信道增益与平均速率的比值选择比值最大的用户。这个比值大说明这个用户现在信道好或者历史速率低应该被补偿。下面的代码实现了一个基于信道增益矩阵的贪心 RU 分配器。function ruAlloc ax_pf_allocate(chGain, alpha) % 比例公平 RU 分配 % chGain: nUsers x nRU 矩阵表示每个用户在每条 RU 上的信道增益 % alpha: 公平性参数0 表示纯吞吐量优先1 表示完全比例公平 [nUsers, nRU] size(chGain); ruAlloc zeros(1, nRU); avgRate ones(nUsers, 1); % 历史平均速率的初始值 for r 1:nRU score (chGain(:, r).^alpha) ./ avgRate; [~, uBest] max(score); ruAlloc(r) uBest; % 用当前 RU 的增益累积更新平均速率 avgRate(uBest) avgRate(uBest) chGain(uBest, r); end endalpha是调节公平性倾向的旋钮。alpha 0时chGain.^0恒为 1评分只看谁的历史速率低这时候每个用户轮流获得 RU完全公平但不保证吞吐量alpha 1时信道好的用户更容易拿到 RU但也要考虑历史速率。实际仿真中可以先从alpha 0.8开始调。更新avgRate的公式没有引入时间窗这是一种简化真实系统会用指数加权移动平均比如avgRate (1 - 1/T) * avgRate 1/T * currentRate其中 T 是窗口长度。调用示例rng(1); nUsers 4; nRU 4; chGain abs(randn(nUsers, nRU)).^2; % 模拟频选信道增益 ruAlloc ax_pf_allocate(chGain, 0.8); disp(ruAlloc);分配结果会随着chGain的变化而变化。跑通这个函数后可以把它接回第 2 章的发送端遍历ruAlloc把第 r 个 RU 的数据子载波填上对应用户的符号。这样你就把物理层和媒体接入层的仿真串成了一条完整的 OFDMA 系统链路。4.3 用蒙特卡洛循环统计 BER 与吞吐量验证 OFDMA 调度效果有了资源分配算法评估链路好坏的手段就是蒙特卡洛仿真。在每个信噪比点上跑几百帧统计所有用户的平均误码率。下面是这套验证流程的整体框架它把前面几段代码组合成了一个可重复的实验。% 主实验脚本遍历 SNR统计多用户 OFDMA 平均 BER snrList 0:5:25; berAll zeros(length(snrList), nUsers); for s 1:length(snrList) berSum zeros(1, nUsers); trials 500; for t 1:trials chGain abs(randn(nUsers, nRU)).^2; ruAlloc ax_pf_allocate(chGain, 0.8); % 根据 ruAlloc 把用户符号填入对应 RU代码省略 rxBits ax_orun_frame(ruAlloc, snrList(s)); for u 1:nUsers berSum(u) berSum(u) mean(rxBits{u} ~ txBits{u}); end end berAll(s, :) berSum / trials; end这段伪代码把帧构造、发送和接收封装成了一个名为ax_orun_frame的函数方便重复调用。验证时有两个观察要点一是所有用户的 BER 曲线应该接近且随 SNR 单调下降二是如果某个用户 BER 特别高检查该用户是否总被分配到深衰落频段这往往是调度算法公平性不足的体现。OFDMA 的调度优势从曲线形状能看得很直观固定分配策略下深衰落用户的 BER 曲线会卡在某个平台期比例公平分配则可以把它拉下来。5. 从 MATLAB 仿真到工程验证三个最值得检查的 OFDMA 实现细节最后落在工程验证方法上。OFDMA 仿真最容易出现“单用户能通、多用户就乱”的问题根源往往是以下三个细节。第一循环前缀长度与 GI 类型必须配套。802.11ax 在 20MHz 下短 GI 是 0.8us对应 16 个采样点256 点 FFT长 GI 是 1.6us 或 3.2us对应 32 或 64 个采样点。如果你的仿真把 FFT 从 256 点缩成 64 点CP 长度也必须按比例缩成 16否则符号间干扰会直接淹没 OFDMA 的子载波正交性。验证方法是去掉信道中的多径只加高斯噪声此时 BER 应该只由噪声决定如果加的是无噪声单径信道BER 必须是 0。第二多用户功率归一化要按总功率算不能每个用户单独归一化。发送端把 4 个用户叠加在同一帧里IFFT 输入的总功率是所有用户子载波功率之和。如果每个用户做完归一化后再叠加进fdFrame整体功率会被放大 4 倍接收端的 AWGN 信噪比计算就全错了。我在 2.2 节用sqrt(nFFT)做整体归一化如果把每个用户单独归一化最后必须再乘1/sqrt(nUsers)做修正。第三验证 OFDMA 链路正确性的最快方法是先测一用户的理想信道。把nUsers改成 1信道设为单位响应如果 BER 不是 0说明 FFT 网格和索引映射有 bug通过后再改成多用户逐步加入信道、加噪声、加资源分配算法。这样每个环节都有明确的通过标准不至于到最后分不清是信道估计的问题还是调度算法的问题。用 MATLAB 的bertool打开模拟误码率参考曲线对比也能快速定位实现偏差。本文还有配套的精品资源点击获取