mcpField
M
matlab-design-ofdm-system

matlab-design-ofdm-system

github.com/matlab其他1.2k62 次安装1 天前更新
代码仓库

关于 matlab-design-ofdm-system

面向 MATLAB 的非标准 OFDM 收发系统设计技能,涵盖子载波分配、信道仿真、同步与均衡,并要求先确认关键参数,避免常见实现错误。

主要功能

  1. 01设计自定义 OFDM 收发系统
  2. 02配置子载波与导频
  3. 03仿真 AWGN 和衰落信道
  4. 04实现同步与信道均衡
  5. 05编码前核对关键参数

适用场景

  1. 01在 MATLAB 中构建自定义 OFDM 链路
  2. 02仿真噪声或衰落影响
  3. 03添加导频并进行信道估计
  4. 04规划同步与编码参数

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/matlab/matlab-agentic-toolkit --skill matlab-design-ofdm-system

安装后在 Claude Code 中输入/matlab-design-ofdm-system确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

manifest.yaml
references/
ofdm-fading-channel.md
ofdm-ldpc-coding.md
ofdm-pilots-and-estimation.md
ofdm-synchronization.md
ofdm-system-guide.md

详细介绍

OFDM 系统设计

适用场景

当用户想要构建自定义(非标准)OFDM 发射机/接收机、配置子载波分配、向 OFDM 信号添加噪声、对经过衰落信道的 OFDM 信号进行均衡、实现同步或添加 LDPC 编码时,请使用此技能。

不适用场景

对于特定标准的 OFDM,请勿使用此技能(5G NR 使用 5G Toolbox,Wi-Fi 使用 WLAN Toolbox,4G/LTE 使用 LTE Toolbox,Bluetooth 使用 Bluetooth Toolbox,卫星链路使用 Satellite Communications Toolbox)。

GATE — 编码前先询问

如果用户的请求未明确指定或无法明确推断以下所有内容,请停下来,在生成任何代码前先询问。将不明确的项目列为编号清单,并询问用户希望:(a)指定具体值;(b)让你根据其他约束推导(例如,根据时延扩展确定 CP,根据多普勒确定 SCS);或(c)使用典型默认值。不要自行假设默认值。用户回复前不要继续。

  1. OFDM 参数 — FFT 大小、CP 长度、子载波间隔、子载波分配
  2. 信道模型 — 仅 AWGN、具有完美 CSI 的衰落,或基于导频估计的衰落
  3. 导频 — 是否需要用于信道估计或相位跟踪?
  4. 编码 — 不编码,或采用特定码率进行编码?
  5. 同步 — 完美同步,或包含定时/CFO 估计?

必须遵守的规则

  1. 始终使用 ofdmmod/ofdmdemod——绝不直接调用 ifft/fft 实现 OFDM — 这些函数会处理 CP 的插入/移除、用于居中频率排序的 ifftshift/fftshift,以及导频/空子载波管理。直接使用 IFFT/FFT 会导致子载波映射错误(且错误不会显现)。SISO 的输入为 [nDataSC × nSym]。输出为时域列向量。注意: 需要 DFT 顺序子载波映射的同步前导码(例如 Schmidl-Cox)可以直接使用 ifft,因为 ofdmmod 会应用 ifftshift,从而改变偶数/奇数频点的分配。加窗: ofdmmod 不支持加窗。要使用升余弦加窗,仅在发射机端使用 comm.OFDMModulator(解调时始终使用 ofdmdemod 函数)。有关功能对比,请参阅 references/ofdm-system-guide.md。

  2. 在 ofdmdemod 中始终使用 symOffset 变量 — 传入 nullidx(第 5 个参数)时,必须将 symOffset 显式设为第 4 个参数。默认的 symOffset 值为 cpLen(跳过整个 CP)。注意:当定时同步不完美时,cpLen/2 更稳健。

  3. 在 awgn 中使用明确的信号功率,绝不使用 'measured' 或 0 — ofdmmod 的输出功率不等于 1W。由于 MATLAB 的 1/N IFFT 归一化,其功率等于 nActiveSC / nFFT^2。使用 awgn(x, snr, 0) 会加入过多噪声。在任何循环之前计算一次 sigPow = 10*log10(nActiveSC / nFFT^2)——这是由 OFDM 参数决定的常量。

  4. 使用 ofdmChannelResponse 获取频域信道响应 — 不要直接计算 H = fft(h, nFFT)。ofdmmod 内部使用 ifftshift,因此直接通过 FFT 计算的信道响应会导致子载波映射错误。将结果传递给 ofdmEqualize 进行均衡。

  5. 根据物理规格推导 OFDM 参数时,用注释展示计算过程 — 不要只给出数值。应展示如何根据带宽、时延扩展和多普勒扩展计算子载波间隔、FFT 大小、CP 长度、采样率、保护子载波等。添加注释,解释每个推导步骤。

  6. 使用受版本限制的 API 前,先检查 MATLAB 版本 — ofdmChannelEstimate、ofdmPilotConfig(R2026a)和 ldpcPCM(R2025a)等函数在较旧版本中不可用。除非用户明确要求代码兼容旧版本,否则调用 version('-release') 获取当前 MATLAB 版本,然后选择合适的 API 路径。

主要功能

功能用途起始版本
ofdmmodOFDM 调制(IFFT + 插入 CP)R2018a
ofdmdemodOFDM 解调(移除 CP + FFT)R2018a
ofdmChannelResponse根据路径增益计算每个子载波的信道响应R2023a
ofdmEqualizeZF/MMSE 频域均衡R2022b
ofdmPilotConfig导频位置和符号配置R2026a
ofdmChannelEstimate基于导频的信道估计(LS + 降噪)R2026a
convertSNRSNR 转换(snrsc、snr、ebno)R2022a
awgn根据明确指定的信号功率添加白高斯噪声—

子载波分配

子载波索引使用中心频率顺序:索引 1 = 最负频率,索引 nFFT/2 + 1 = DC,索引 nFFT = 最正频率。

子载波索引规则:

  • nullIdx 和 pilotIdx 必须是 [1, nFFT] 范围内由 1 开始的整数向量
  • 两者不能重叠
  • DC 子载波 = nFFT/2 + 1(始终将其设为 null)
  • ofdmmod 输入 X 的大小为 [nDataSC x nSym] — 不包含 null 和导频子载波
  • ofdmdemod 输出的大小为 [nDataSC x nSym] — 已剔除 null 子载波,导频会单独返回

示例(类似 WiFi 的 64-FFT):

nullIdx = [1:6, 33, 60:64].';  % 12 nulls (6 lower guard + DC + 5 upper guard)
pilotIdx = [12; 26; 40; 54];   % 4 pilots
nActiveSC = nFFT - length(nullIdx);                    % 52 (data + pilots)
nDataSC   = nFFT - length(nullIdx) - length(pilotIdx); % 48

基本 OFDM Tx/Rx(AWGN)

% Parameters
nFFT = 64;  cpLen = 16;
nullIdx = [1:6, 33, 60:64].';  % 12 nulls
nActiveSC = nFFT - length(nullIdx);  % 52
nDataSC = nActiveSC;  % no pilots
M = 4;  % QPSK
nSym = 100;
snr_sc = 10;  % dB, per-subcarrier SNR

% Transmit
data = randi([0 M-1], nDataSC, nSym);
modData = pskmod(data, M, InputType="integer");
txSig = ofdmmod(modData, nFFT, cpLen, nullIdx);

% SNR conversion and noise
sigPow = 10*log10(nActiveSC / nFFT^2);  % OFDM signal power (dBW)
snr_wb = convertSNR(snr_sc, "snrsc", "snr", ...
    FFTLength=nFFT, NumActiveSubcarriers=nActiveSC);
rxSig = awgn(txSig, snr_wb, sigPow);

% Receive
symOffset = cpLen;
rxData = ofdmdemod(rxSig, nFFT, cpLen, symOffset, nullIdx);
demodData = pskdemod(rxData, M, OutputType="integer");
[numErr, ber] = biterr(data(:), demodData(:));

OFDM 的 SNR 处理

每个子载波的 SNR(SNR_sc)是 OFDM 仿真中标准的噪声参考值。 如果需要其他噪声指标,例如用于理论 BER 对比的 Eb/No,或用于 awgn 的宽带 SNR,请使用 convertSNR 从 SNR_sc 推导。有关 convertSNR 的完整用法模式和 awgn 用法,请参阅 matlab-add-awgn。

对于理论 BER: 使用 convertSNR(snr_sc, "snr", "ebno", BitsPerSymbol=log2(M)) 将 SNR_sc 转换为 Eb/No,然后调用 berawgn(ebno, 'qam', M)。切勿将 SNR_sc 直接传给 berawgn,也不要在 erfc/qfunc 闭式表达式中将其用作 Eb/No — SNR_sc ≠ Eb/No。

OFDM 特有注意事项(AWGN 技能中未涉及):

  • 信号功率 = 10*log10(nActiveSC / nFFT^2) dBW — 这是由于 MATLAB 的 1/N IFFT 归一化
  • nActiveSC 包含数据和导频子载波
  • 始终从 awgn 的第二个输出中获取噪声方差: [rxSig, nVar] = awgn(...)。切勿根据 SNR 公式手动计算噪声方差 — 使用 awgn 返回的值,以确保与其内部舍入保持一致。
  • ofdmdemod 后的噪声方差 会按 nFFT 缩放:nVar_sc = nVar * nFFT(FFT 会累加 N 项)
  • convertSNR 的 "snr" 类型 = 从 Eb/No 或 Es/No 转换时的每子载波 SNR(每个 OFDM 子载波承载一个符号)。若要将每子载波 SNR 转换为宽带 SNR(用于 awgn),请使用 convertSNR(snr_sc, "snrsc", "snr", FFTLength=nFFT, NumActiveSubcarriers=nActiveSC)。

关键注意事项

awgn(x, snr, 0) 不适用于 OFDM

OFDM 信号功率取决于 nActiveSC 和 nFFT,而不是 1W。

% WRONG — assumes unit power (adds ~19 dB too much noise for 64-FFT)
rxSig = awgn(txSig, snr_wb, 0);

% CORRECT — explicit power, capture noise variance
sigPow = 10*log10(nActiveSC / nFFT^2);
[rxSig, nVar] = awgn(txSig, snr_wb, sigPow);
ofdmdemod 必须指定 symOffset
symOffset = cpLen;  % skip entire CP (or cpLen/2 for imperfect timing)
rxData = ofdmdemod(rxSig, nFFT, cpLen, symOffset, nullIdx);
单流时,调制器/解调器的输入必须是列向量

所有 Communications Toolbox 调制器(qammod、pskmod 等)都按列处理数据 — 每列是一个独立的信道/数据流。对于单流 OFDM,始终将比特或整数作为列向量传入:

% WRONG — [nSym x k] matrix: each column is treated as a separate stream
txData = reshape(txBits, nBitsPerSym, []).';
txSymbols = qammod(txData, M, InputType="bit", UnitAveragePower=true);

% CORRECT — column vector in, then reshape output to OFDM grid
txSymbols = qammod(txBits, M, InputType="bit", UnitAveragePower=true);
txSymbols = reshape(txSymbols, nActiveSC, nSym);

只有在同时调制相互独立的 MIMO 数据流或并行码字时,才使用多列。

DC 子载波索引

DC = nFFT/2 + 1。对于 nFFT=64,DC 的索引为 33。始终将其包含在 nullIdx 中。

ofdmEqualize 的 hEst 维度

使用默认的 DataFormat="3-D" 时,hEst 的维度为:

  • [nSC × NS × NR] — 静态:将相同的估计应用于所有 OFDM 符号
  • [(nSC*nSym) × NS × NR] — 时变:将每个符号的估计折叠到第一维中

直接传入 [nSC × nSym] 是错误的 — 第 2 维会被视为 NS,而不是 nSym:

% WRONG — hEst [nSC x nSym] misinterpreted as [nSC x NS=nSym]
eqData = ofdmEqualize(rxData, hEst_per_sym, nVar);

% CORRECT — collapse first dim to (nSC*nSym), keep stream/antenna dims
eqData = ofdmEqualize(rxData, reshape(hEst_per_sym, [], Ns, Nr), nVar);

带导频的 OFDM

要插入导频,请将 pilotIdx 和导频符号作为额外参数传递给 ofdmmod/ofdmdemod。信号功率基于 nActiveSC(数据 + 导频),而非仅基于 nDataSC。

完整的导频插入示例见 references/ofdm-pilots-and-estimation.md。

基于导频的信道估计(R2026a)

使用 ofdmPilotConfig + ofdmChannelEstimate 进行基于导频的信道估计,无需完美 CSI。如果尚未检查,请先获取当前 MATLAB 版本,再决定使用 R2026a 工作流还是 R2026a 之前的手动 LS 方法。

ofdmChannelEstimate 工作流与旧版 pilotIdx 方法不同:

  • nullIdx 仅包含保护带(不包含 DC) —— DC 会成为活动网格位置,需要手动将其置零。
  • ofdmmod/ofdmdemod 不需要 pilotIdx 参数 —— 构建包含数据、导频和 DC=0 的完整 [nActiveSC x nSym] 网格,然后直接传入。
  • 导频位置由 ofdmPilotConfig 指定,而非通过函数参数传入。
  • rxSym 是 3-D 数组:[nActiveSC x nSym x nRx] —— 即使是 SISO,也要添加接收天线维度。

完整代码示例见 references/ofdm-pilots-and-estimation.md。

衰落信道中的 OFDM

信道均衡有两种方法:

  1. 完美 CSI(R2023a)——使用 ofdmChannelResponse 和来自 comm.RayleighChannel 的路径增益(需要设置 PathGainsOutputPort=true)。从 info(channel).ChannelFilterCoefficients 获取 pathFilters。对于时变 SISO,将 H(:) 传递给 ofdmEqualize。

  2. 基于导频的估计(R2026a)——使用 ofdmPilotConfig + ofdmChannelEstimate。

两种方法都使用 ofdmEqualize 并设置 Algorithm="mmse" 进行最终均衡。在 awgn 中始终使用显式信号功率(而非 'measured')——衰落会改变瞬时功率。

信道配置(Rayleigh/Rician、多普勒、3GPP 配置文件、ofdmEqualize 维度)见 references/ofdm-fading-channel.md。

衰落信道设置

配置 comm.RayleighChannel 或 comm.RicianChannel 时,将 PathGainsOutputPort=true。关键规则:

  • 始终设置 SampleRate(默认值为 1 Hz)
  • 最大路径时延必须小于 CP 时长:max(PathDelays) < cpLen/SampleRate
  • 根据速度计算多普勒频移:fd = (velocity * carrierFreq) / physconst('LightSpeed')

完整设置说明、3GPP 配置文件和准静态衰落见 references/ofdm-fading-channel.md。

同步

处理流程:粗定时 → CFO 估计 → CFO 校正 → 精细定时 → OFDM 解调 → 相位跟踪

关键规则:

  • 使用 frequencyOffset 应用/校正 CFO(不要手动使用 exp(-1j*2*pi*...))
  • 使用 timingEstimate 进行互相关定时检测
  • 在跨符号应用校正前,始终对导频相位估计使用 unwrap

Schmidl-Cox、基于 CP 的定时、Zadoff-Chu 前导码和完整的 Rx 示例见 references/ofdm-synchronization.md。

LDPC 编码

关键规则:

  • 使用配置对象搭配 ldpcEncode/ldpcDecode(不要使用已移除的 comm.LDPCEncoder/comm.LDPCDecoder)
  • 仅从奇偶校验矩阵创建配置: H = ldpcPCM(648, 324); encCfg = ldpcEncoderConfig(H); decCfg = ldpcDecoderConfig(H)。不要传入标量或名称-值对——ldpcEncoderConfig(648)、ldpcEncoderConfig(648, 1/2) 和 ldpcDecoderConfig(BlockLength=648) 都会报错。如果尚未检查,请先获取当前 MATLAB 版本,以决定使用 ldpcPCM(R2025a+)还是 ldpcQuasiCyclicMatrix(R2021b+)。
  • 为编码系统计算 Eb/No 时,在 convertSNR 中包含 CodingRate
  • 将 NoiseVariance 传递给解调器(如 qamdemod),以正确缩放 LLR;换算到频域:nVar_sc = nVar * nFFT
  • 使用 OutputType="approxllr" 计算 LLR

OFDM + LDPC 工作流和多码字成帧见 references/ofdm-ldpc-coding.md。

高级功能

请参阅 references/ofdm-system-guide.md,了解:

  • 可变 CP 长度 — 通过行向量 cpLens 设置每个符号的 CP
  • 常见 OFDM 配置 — 类似 WiFi、LTE 和 5G 的参数集
  • SNR 转换 — 有关所有 convertSNR 用法模式,请参阅 matlab-add-awgn 技能
  • 软解调的噪声方差 — ofdmdemod 后的缩放

交叉引用

  • matlab-add-awgn — 需要添加噪声并计算 SNR 转换时,请使用此技能。它涵盖所有 convertSNR 用法模式、awgn 的用法,以及噪声方差的捕获。

参考资料加载

当用户询问导频插入、基于导频的信道估计(R2026a ofdmChannelEstimate)或 R2026a 之前的 LS 估计时,加载 references/ofdm-pilots-and-estimation.md。

当用户询问衰落信道配置、Rayleigh/Rician 设置、多普勒计算、3GPP 延迟配置文件或 ofdmEqualize 的维度处理时,加载 references/ofdm-fading-channel.md。

当用户询问定时同步、CFO 估计/校正、Schmidl-Cox、前导码设计或基于导频的相位跟踪时,加载 references/ofdm-synchronization.md。

当用户询问 LDPC 编码、前向纠错、ldpcEncode/ldpcDecode、软解码的 NoiseVariance 或编码 BER 仿真时,加载 references/ofdm-ldpc-coding.md。

当用户询问 SNR 转换详情、常见 OFDM 配置、软解调的噪声方差或可变 CP 长度时,加载 references/ofdm-system-guide.md。

版权所有 2026 The MathWorks, Inc.

评论

支持 Markdown