简介:这份资源是一套基于MATLAB实现的OFDMA收发仿真系统,面向通信工程、电子信息类专业学生及无线通信算法入门研究者,用于理解正交频分多址在4G LTE与5G NR中的复用机制。压缩包共2个文件,包含1个m脚本与1个txt许可说明,整体约4KB,其中m文件承载核心仿真逻辑,txt用于声明使用与分发条件。仿真内容覆盖子载波正交性、频域资源分配、QPSK/16QAM/64QAM调制与卷积或Turbo编码、IFFT/FFT变换、同步与信道均衡、接收端信道估计与解调解码,以及误码率、符号错误率、吞吐量等性能评估和星座图、误码率曲线可视化。已有213人学习下载,适合作为课程设计或算法验证的参考脚本,帮助读者在MATLAB环境下搭建基本OFDMA链路并观察多用户传输效果。1. OFDMA仿真系统:从“收发链路跑不通”到“误码率曲线能对齐”的那条路做无线通信基带仿真的工程师,几乎都绕不开 OFDMA 这个词。它是 Wi-Fi 6、5G NR、电力线通信里都在用的多址方式,核心思路是把一段宽带切成许多互相正交的子载波,再按需分配给不同用户。听起来不复杂,但真正动手写一套 OFDMA 收发 MATLAB 仿真时,很多人会卡在同一个地方:单用户链路明明能跑,一加多用户资源映射,星座图就散成一团,误码率曲线怎么调都对不上理论值。这套仿真系统要解决的,正是从“子载波分配、IFFT/FFT、循环前缀、信道估计到多用户解调”这一整条链路的可复现问题。它适合已经懂一点 OFDM、想进一步搞清多用户资源调度和接收端处理的同学,也适合需要快速搭一个验证平台、用来测试自己信道估计算法或同步算法的从业者。下面我按自己搭这套东西的顺序,把关键环节和踩过的坑讲清楚。2. 先把 OFDMA 收发链路的骨架搭对:子载波分配与 IFFT 的先后关系2.1 为什么不能直接把 OFDM 代码改成多用户很多人上手的第一反应,是拿一段单用户 OFDM 代码,把数据分成几份,分别做 IFFT,再把结果加起来。这个做法在数学上等价于把多个用户的信号在时域叠加,但接收端要分离它们就非常困难,因为每个用户经历的 IFFT 是独立的,子载波映射关系被打乱了。OFDMA 的正确做法是:所有用户在频域上共享同一个 IFFT 块,每个用户只占用分配给自己的那部分子载波,其余子载波置零。这样一次 IFFT 就完成了多用户复用,接收端做一次 FFT 后,按同样的子载波分配表就能把各用户的数据取出来。这个顺序不能反,反了后面所有同步和估计都会对不上。2.2 资源映射的最小可运行代码下面这段代码演示了 4 个用户、总共 64 个子载波、每个用户占 16 个连续子载波的资源映射过程。实际系统里可能是交织分配,但连续分配更容易看清映射逻辑。% 参数设置 N_fft = 64; % FFT 点数 N_cp = 16; % 循环前缀长度 N_user = 4; % 用户数 N_sc_per_user = N_fft / N_user; % 每用户子载波数 mod_order = 4; % QPSK bits_per_sym = log2(mod_order); % 生成每个用户的随机比特并调制 user_data = cell(1, N_user); user_sym = cell(1, N_user); for u = 1:N_user n_bits = N_sc_per_user * bits_per_sym; user_data{u} = randi([0 1], n_bits, 1); % 比特转整数,再 QPSK 调制 int_data = bi2de(reshape(user_data{u}, bits_per_sym, []).', 'left-msb'); user_sym{u} = pskmod(int_data, mod_order, pi/4); end % 频域资源映射:每个用户占用一段连续子载波 freq_grid = zeros(N_fft, 1); for u = 1:N_user idx = (u-1)*N_sc_per_user + (1:N_sc_per_user); freq_grid(idx) = user_sym{u}; end % 注意:这里没有做 DC 置零和边缘保护,实际系统要留 guard band % IFFT 变换到时域 time_signal = ifft(freq_grid, N_fft) * sqrt(N_fft); % 加循环前缀 tx_signal = [time_signal(end-N_cp+1:end); time_signal];这段代码里最容易出错的是ifft之后的归一化系数。MATLAB 的ifft默认除以 N,如果不乘sqrt(N_fft),发端功率会随 FFT 点数变化,后面加噪声时信噪比就对不上了。另一个点是pskmod的相位偏移参数,QPSK 用pi/4可以避免星座点落在坐标轴上,但接收端解调时必须用同样的偏移,否则星座图会整体旋转。资源映射的索引idx也要仔细检查,连续分配时第 u 个用户的起始位置是(u-1)*N_sc_per_user + 1,如果写成(u-1)*N_sc_per_user就会从 0 开始,MATLAB 会报索引错误。2.3 接收端 FFT 与用户数据提取接收端拿到的是经过信道和噪声的时域信号,先去 CP,再做 FFT 回到频域,然后按发端相同的分配表取每个用户的符号。% 假设 rx_signal 是接收到的时域信号,长度与 tx_signal 一致 rx_no_cp = rx_signal(N_cp+1:end); % 去循环前缀 rx_freq = fft(rx_no_cp, N_fft) / sqrt(N_fft); % 归一化 % 按用户提取频域符号 rx_sym = cell(1, N_user); for u = 1:N_user idx = (u-1)*N_sc_per_user + (1:N_sc_per_user); rx_sym{u} = rx_freq(idx); end % 信道均衡(这里假设已知理想信道,实际需要估计) % H_est 是每个子载波的信道响应,长度 N_fft % rx_freq_eq = rx_freq ./ H_est;去 CP 时索引从N_cp+1开始,是因为发端把 CP 加在了前面。如果 CP 长度设得比信道最大时延还短,就会引入符号间干扰,这时候误码率曲线会出现误码平台,怎么加发射功率都降不下去。一般 CP 长度取信道最大时延扩展的 2 到 4 倍,室内场景常用 16 或 32 个采样点。3. 信道估计与均衡:让多用户解调不再靠“猜”3.1 块状导频和梳状导频怎么选OFDMA 仿真里,信道估计的导频图案直接决定了估计精度和开销。块状导频是在某些 OFDM 符号的所有子载波上都放导频,适合信道变化慢的场景;梳状导频是在每个 OFDM 符号的固定间隔子载波上放导频,适合信道变化快的场景。我一般先在时不变信道下用块状导频把链路跑通,确认资源映射和同步没问题,再换成梳状导频去测时变信道下的跟踪性能。如果一上来就用梳状导频,同步稍微偏一点,导频位置的信道估计就会错,后面插值出来的整个信道响应都是歪的,排查起来非常费劲。3.2 LS 估计与 MMSE 估计的代码实现最小二乘(LS)估计最简单,直接用接收导频除以本地导频。最小均方误差(MMSE)估计需要知道信道统计特性,但性能更好。下面给出 LS 估计的代码,MMSE 在此基础上乘一个由信噪比和信道相关矩阵决定的滤波矩阵。% 假设导频位置索引为 pilot_idx,本地导频符号为 pilot_sym % rx_pilot 是接收到的导频位置符号 H_ls = rx_pilot ./ pilot_sym; % LS 估计 % 对数据子载波做插值,这里用线性插值 H_est = interp1(pilot_idx, H_ls, (1:N_fft).', 'linear', 'extrap'); % 均衡 rx_freq_eq = rx_freq ./ H_est;interp1的最后一个参数'extrap'很重要,因为边缘子载波可能没有导频覆盖,不 extrap 会返回 NaN,后面解调直接崩掉。LS 估计在低信噪比下噪声放大明显,如果发现均衡后星座图在低 SNR 时反而比高 SNR 还散,大概率就是 LS 估计的噪声被除进去了。这时候要么改用 MMSE,要么在 LS 之后加一个简单的滑动平均滤波。3.3 多用户之间的干扰怎么在仿真里体现OFDMA 的子载波是正交分配的,理论上用户之间没有多址干扰。但仿真里如果资源映射的索引有重叠,或者 IFFT 点数与子载波总数不匹配,就会人为引入干扰。我习惯在资源映射之后加一句检查:sum(freq_grid ~= 0)应该等于N_user * N_sc_per_user,如果不等,说明有子载波被重复写入或者漏写。另外,接收端提取用户数据时,如果索引和发端不一致,会把别人的数据当成自己的解调,误码率直接接近 0.5,这时候看星座图会发现点均匀散布,没有聚类。4. 避坑与排查:OFDMA 仿真里最容易翻车的五个地方4.1 误码率曲线在高信噪比下不降反升现象:SNR 超过 15 dB 后,误码率曲线开始往上翘。原因通常是定时同步偏差导致的符号间干扰,或者信道估计在低噪声下把导频处的噪声也估计进去了。解决方法是检查同步算法里的相关峰位置,确保 FFT 窗口没有偏移;同时在 LS 估计后加一个基于 DFT 的降噪,把时域信道响应截断到 CP 长度以内。4.2 星座图整体旋转现象:接收端解调前的星座图是一个旋转过的 QPSK,点与点之间角度不对。原因是发端和收端的相位偏移参数不一致,或者信道估计没有补偿公共相位误差。解决方法是确认pskmod和pskdemod的相位参数相同,并在均衡后加一个基于导频的公共相位校正。4.3 资源映射后子载波功率忽大忽小现象:不同用户的发射功率差异很大,导致弱用户误码率很高。原因是 IFFT 前没有做功率归一化,或者不同用户的调制阶数不同但符号功率没对齐。解决方法是在资源映射前把每个用户的符号功率归一化到 1,或者在接收端按用户做自动增益控制。4.4 循环前缀长度不够导致误码平台现象:无论怎么提高 SNR,误码率都卡在 1e-2 左右降不下去。原因是 CP 长度小于信道最大时延,符号间干扰无法消除。解决方法是把 CP 长度增加到信道时延扩展的 2 倍以上,或者改用频域均衡加更长的 CP。4.5 多用户数据提取时索引越界现象:MATLAB 报“索引超出矩阵维度”。原因是用户数乘以每用户子载波数不等于 FFT 点数,或者索引计算时用了从 0 开始的编号。解决方法是在资源映射前加断言assert(N_user * N_sc_per_user <= N_fft),并统一用从 1 开始的索引。5. 把仿真跑成可复用的验证平台:参数化与批量跑 SNR 点5.1 用结构体管理参数,避免脚本里到处是魔法数字一套 OFDMA 仿真系统要反复调参数,如果每个数字都硬编码在脚本里,改一个 FFT 点数就要翻遍全文。我习惯把所有参数塞进一个结构体,主脚本只负责调用。cfg.N_fft = 64; cfg.N_cp = 16; cfg.N_user = 4; cfg.mod_order = 4; cfg.SNR_dB = 0:2:20; cfg.N_frame = 1000; % 每个 SNR 点跑 1000 帧 ber = zeros(size(cfg.SNR_dB)); for i = 1:length(cfg.SNR_dB) err_cnt = 0; bit_cnt = 0; for f = 1:cfg.N_frame [tx, tx_bits] = ofdma_tx(cfg); rx = awgn(tx, cfg.SNR_dB(i), 'measured'); rx_bits = ofdma_rx(rx, cfg); err_cnt = err_cnt + sum(rx_bits ~= tx_bits); bit_cnt = bit_cnt + length(tx_bits); end ber(i) = err_cnt / bit_cnt; end semilogy(cfg.SNR_dB, ber, 'o-'); grid on; xlabel('SNR (dB)'); ylabel('BER');把收发封装成ofdma_tx和ofdma_rx两个函数,主脚本只做循环和统计。这样换调制方式、换用户数、换信道模型都只改cfg,不用动主逻辑。awgn的'measured'选项会先测量信号功率再按 SNR 加噪声,比手动算噪声功率更省事,但要注意它测量的是整个帧的功率,如果帧内有静默期,测出来的功率会偏低。5.2 用理论误码率曲线验证仿真是否正确QPSK 在 AWGN 下的理论误码率是ber_theory = 0.5 * erfc(sqrt(10^(SNR_dB/10) / 2))。把仿真曲线和理论曲线画在一起,如果差距在 1 dB 以内,说明链路基本正确;如果差 3 dB 以上,优先检查归一化系数和信道估计。我一般会在仿真脚本最后加一段对比代码,省得每次靠肉眼判断。snr_lin = 10.^(cfg.SNR_dB/10); ber_theory = 0.5 * erfc(sqrt(snr_lin / 2)); semilogy(cfg.SNR_dB, ber, 'o-', cfg.SNR_dB, ber_theory, 'k--'); legend('仿真', '理论');5.3 一个容易被忽略的细节:随机种子跑批量仿真时,如果每次randi的种子都不同,不同 SNR 点的比特流不一样,误码率曲线会有抖动。我习惯在循环开始前固定随机种子rng(42),这样每次跑出来的曲线可复现,调参数时也能排除随机性的干扰。等链路验证通过后,再换几个不同的种子跑几遍,确认曲线稳定。这套仿真系统本身不复杂,难的是每个环节的归一化、索引和同步细节。我自己的习惯是每加一个模块就先在理想信道下跑通,再加噪声,再加信道,最后加多用户。一次只改一个变量,出了问题才能定位到具体哪一行。希望帮到你。本文还有配套的精品资源,点击获取
简介:这份资源是一套基于MATLAB实现的OFDMA收发仿真系统,面向通信工程、电子信息类专业学生及无线通信算法入门研究者,用于理解正交频分多址在4G LTE与5G NR中的复用机制。压缩包共2个文件,包含1个m脚本与1个txt许可说明,整体约4KB,其中m文件承载核心仿真逻辑,txt用于声明使用与分发条件。仿真内容覆盖子载波正交性、频域资源分配、QPSK/16QAM/64QAM调制与卷积或Turbo编码、IFFT/FFT变换、同步与信道均衡、接收端信道估计与解调解码,以及误码率、符号错误率、吞吐量等性能评估和星座图、误码率曲线可视化。已有213人学习下载,适合作为课程设计或算法验证的参考脚本,帮助读者在MATLAB环境下搭建基本OFDMA链路并观察多用户传输效果。1. OFDMA仿真系统:从“收发链路跑不通”到“误码率曲线能对齐”的那条路做无线通信基带仿真的工程师,几乎都绕不开 OFDMA 这个词。它是 Wi-Fi 6、5G NR、电力线通信里都在用的多址方式,核心思路是把一段宽带切成许多互相正交的子载波,再按需分配给不同用户。听起来不复杂,但真正动手写一套 OFDMA 收发 MATLAB 仿真时,很多人会卡在同一个地方:单用户链路明明能跑,一加多用户资源映射,星座图就散成一团,误码率曲线怎么调都对不上理论值。这套仿真系统要解决的,正是从“子载波分配、IFFT/FFT、循环前缀、信道估计到多用户解调”这一整条链路的可复现问题。它适合已经懂一点 OFDM、想进一步搞清多用户资源调度和接收端处理的同学,也适合需要快速搭一个验证平台、用来测试自己信道估计算法或同步算法的从业者。下面我按自己搭这套东西的顺序,把关键环节和踩过的坑讲清楚。2. 先把 OFDMA 收发链路的骨架搭对:子载波分配与 IFFT 的先后关系2.1 为什么不能直接把 OFDM 代码改成多用户很多人上手的第一反应,是拿一段单用户 OFDM 代码,把数据分成几份,分别做 IFFT,再把结果加起来。这个做法在数学上等价于把多个用户的信号在时域叠加,但接收端要分离它们就非常困难,因为每个用户经历的 IFFT 是独立的,子载波映射关系被打乱了。OFDMA 的正确做法是:所有用户在频域上共享同一个 IFFT 块,每个用户只占用分配给自己的那部分子载波,其余子载波置零。这样一次 IFFT 就完成了多用户复用,接收端做一次 FFT 后,按同样的子载波分配表就能把各用户的数据取出来。这个顺序不能反,反了后面所有同步和估计都会对不上。2.2 资源映射的最小可运行代码下面这段代码演示了 4 个用户、总共 64 个子载波、每个用户占 16 个连续子载波的资源映射过程。实际系统里可能是交织分配,但连续分配更容易看清映射逻辑。% 参数设置 N_fft = 64; % FFT 点数 N_cp = 16; % 循环前缀长度 N_user = 4; % 用户数 N_sc_per_user = N_fft / N_user; % 每用户子载波数 mod_order = 4; % QPSK bits_per_sym = log2(mod_order); % 生成每个用户的随机比特并调制 user_data = cell(1, N_user); user_sym = cell(1, N_user); for u = 1:N_user n_bits = N_sc_per_user * bits_per_sym; user_data{u} = randi([0 1], n_bits, 1); % 比特转整数,再 QPSK 调制 int_data = bi2de(reshape(user_data{u}, bits_per_sym, []).', 'left-msb'); user_sym{u} = pskmod(int_data, mod_order, pi/4); end % 频域资源映射:每个用户占用一段连续子载波 freq_grid = zeros(N_fft, 1); for u = 1:N_user idx = (u-1)*N_sc_per_user + (1:N_sc_per_user); freq_grid(idx) = user_sym{u}; end % 注意:这里没有做 DC 置零和边缘保护,实际系统要留 guard band % IFFT 变换到时域 time_signal = ifft(freq_grid, N_fft) * sqrt(N_fft); % 加循环前缀 tx_signal = [time_signal(end-N_cp+1:end); time_signal];这段代码里最容易出错的是ifft之后的归一化系数。MATLAB 的ifft默认除以 N,如果不乘sqrt(N_fft),发端功率会随 FFT 点数变化,后面加噪声时信噪比就对不上了。另一个点是pskmod的相位偏移参数,QPSK 用pi/4可以避免星座点落在坐标轴上,但接收端解调时必须用同样的偏移,否则星座图会整体旋转。资源映射的索引idx也要仔细检查,连续分配时第 u 个用户的起始位置是(u-1)*N_sc_per_user + 1,如果写成(u-1)*N_sc_per_user就会从 0 开始,MATLAB 会报索引错误。2.3 接收端 FFT 与用户数据提取接收端拿到的是经过信道和噪声的时域信号,先去 CP,再做 FFT 回到频域,然后按发端相同的分配表取每个用户的符号。% 假设 rx_signal 是接收到的时域信号,长度与 tx_signal 一致 rx_no_cp = rx_signal(N_cp+1:end); % 去循环前缀 rx_freq = fft(rx_no_cp, N_fft) / sqrt(N_fft); % 归一化 % 按用户提取频域符号 rx_sym = cell(1, N_user); for u = 1:N_user idx = (u-1)*N_sc_per_user + (1:N_sc_per_user); rx_sym{u} = rx_freq(idx); end % 信道均衡(这里假设已知理想信道,实际需要估计) % H_est 是每个子载波的信道响应,长度 N_fft % rx_freq_eq = rx_freq ./ H_est;去 CP 时索引从N_cp+1开始,是因为发端把 CP 加在了前面。如果 CP 长度设得比信道最大时延还短,就会引入符号间干扰,这时候误码率曲线会出现误码平台,怎么加发射功率都降不下去。一般 CP 长度取信道最大时延扩展的 2 到 4 倍,室内场景常用 16 或 32 个采样点。3. 信道估计与均衡:让多用户解调不再靠“猜”3.1 块状导频和梳状导频怎么选OFDMA 仿真里,信道估计的导频图案直接决定了估计精度和开销。块状导频是在某些 OFDM 符号的所有子载波上都放导频,适合信道变化慢的场景;梳状导频是在每个 OFDM 符号的固定间隔子载波上放导频,适合信道变化快的场景。我一般先在时不变信道下用块状导频把链路跑通,确认资源映射和同步没问题,再换成梳状导频去测时变信道下的跟踪性能。如果一上来就用梳状导频,同步稍微偏一点,导频位置的信道估计就会错,后面插值出来的整个信道响应都是歪的,排查起来非常费劲。3.2 LS 估计与 MMSE 估计的代码实现最小二乘(LS)估计最简单,直接用接收导频除以本地导频。最小均方误差(MMSE)估计需要知道信道统计特性,但性能更好。下面给出 LS 估计的代码,MMSE 在此基础上乘一个由信噪比和信道相关矩阵决定的滤波矩阵。% 假设导频位置索引为 pilot_idx,本地导频符号为 pilot_sym % rx_pilot 是接收到的导频位置符号 H_ls = rx_pilot ./ pilot_sym; % LS 估计 % 对数据子载波做插值,这里用线性插值 H_est = interp1(pilot_idx, H_ls, (1:N_fft).', 'linear', 'extrap'); % 均衡 rx_freq_eq = rx_freq ./ H_est;interp1的最后一个参数'extrap'很重要,因为边缘子载波可能没有导频覆盖,不 extrap 会返回 NaN,后面解调直接崩掉。LS 估计在低信噪比下噪声放大明显,如果发现均衡后星座图在低 SNR 时反而比高 SNR 还散,大概率就是 LS 估计的噪声被除进去了。这时候要么改用 MMSE,要么在 LS 之后加一个简单的滑动平均滤波。3.3 多用户之间的干扰怎么在仿真里体现OFDMA 的子载波是正交分配的,理论上用户之间没有多址干扰。但仿真里如果资源映射的索引有重叠,或者 IFFT 点数与子载波总数不匹配,就会人为引入干扰。我习惯在资源映射之后加一句检查:sum(freq_grid ~= 0)应该等于N_user * N_sc_per_user,如果不等,说明有子载波被重复写入或者漏写。另外,接收端提取用户数据时,如果索引和发端不一致,会把别人的数据当成自己的解调,误码率直接接近 0.5,这时候看星座图会发现点均匀散布,没有聚类。4. 避坑与排查:OFDMA 仿真里最容易翻车的五个地方4.1 误码率曲线在高信噪比下不降反升现象:SNR 超过 15 dB 后,误码率曲线开始往上翘。原因通常是定时同步偏差导致的符号间干扰,或者信道估计在低噪声下把导频处的噪声也估计进去了。解决方法是检查同步算法里的相关峰位置,确保 FFT 窗口没有偏移;同时在 LS 估计后加一个基于 DFT 的降噪,把时域信道响应截断到 CP 长度以内。4.2 星座图整体旋转现象:接收端解调前的星座图是一个旋转过的 QPSK,点与点之间角度不对。原因是发端和收端的相位偏移参数不一致,或者信道估计没有补偿公共相位误差。解决方法是确认pskmod和pskdemod的相位参数相同,并在均衡后加一个基于导频的公共相位校正。4.3 资源映射后子载波功率忽大忽小现象:不同用户的发射功率差异很大,导致弱用户误码率很高。原因是 IFFT 前没有做功率归一化,或者不同用户的调制阶数不同但符号功率没对齐。解决方法是在资源映射前把每个用户的符号功率归一化到 1,或者在接收端按用户做自动增益控制。4.4 循环前缀长度不够导致误码平台现象:无论怎么提高 SNR,误码率都卡在 1e-2 左右降不下去。原因是 CP 长度小于信道最大时延,符号间干扰无法消除。解决方法是把 CP 长度增加到信道时延扩展的 2 倍以上,或者改用频域均衡加更长的 CP。4.5 多用户数据提取时索引越界现象:MATLAB 报“索引超出矩阵维度”。原因是用户数乘以每用户子载波数不等于 FFT 点数,或者索引计算时用了从 0 开始的编号。解决方法是在资源映射前加断言assert(N_user * N_sc_per_user <= N_fft),并统一用从 1 开始的索引。5. 把仿真跑成可复用的验证平台:参数化与批量跑 SNR 点5.1 用结构体管理参数,避免脚本里到处是魔法数字一套 OFDMA 仿真系统要反复调参数,如果每个数字都硬编码在脚本里,改一个 FFT 点数就要翻遍全文。我习惯把所有参数塞进一个结构体,主脚本只负责调用。cfg.N_fft = 64; cfg.N_cp = 16; cfg.N_user = 4; cfg.mod_order = 4; cfg.SNR_dB = 0:2:20; cfg.N_frame = 1000; % 每个 SNR 点跑 1000 帧 ber = zeros(size(cfg.SNR_dB)); for i = 1:length(cfg.SNR_dB) err_cnt = 0; bit_cnt = 0; for f = 1:cfg.N_frame [tx, tx_bits] = ofdma_tx(cfg); rx = awgn(tx, cfg.SNR_dB(i), 'measured'); rx_bits = ofdma_rx(rx, cfg); err_cnt = err_cnt + sum(rx_bits ~= tx_bits); bit_cnt = bit_cnt + length(tx_bits); end ber(i) = err_cnt / bit_cnt; end semilogy(cfg.SNR_dB, ber, 'o-'); grid on; xlabel('SNR (dB)'); ylabel('BER');把收发封装成ofdma_tx和ofdma_rx两个函数,主脚本只做循环和统计。这样换调制方式、换用户数、换信道模型都只改cfg,不用动主逻辑。awgn的'measured'选项会先测量信号功率再按 SNR 加噪声,比手动算噪声功率更省事,但要注意它测量的是整个帧的功率,如果帧内有静默期,测出来的功率会偏低。5.2 用理论误码率曲线验证仿真是否正确QPSK 在 AWGN 下的理论误码率是ber_theory = 0.5 * erfc(sqrt(10^(SNR_dB/10) / 2))。把仿真曲线和理论曲线画在一起,如果差距在 1 dB 以内,说明链路基本正确;如果差 3 dB 以上,优先检查归一化系数和信道估计。我一般会在仿真脚本最后加一段对比代码,省得每次靠肉眼判断。snr_lin = 10.^(cfg.SNR_dB/10); ber_theory = 0.5 * erfc(sqrt(snr_lin / 2)); semilogy(cfg.SNR_dB, ber, 'o-', cfg.SNR_dB, ber_theory, 'k--'); legend('仿真', '理论');5.3 一个容易被忽略的细节:随机种子跑批量仿真时,如果每次randi的种子都不同,不同 SNR 点的比特流不一样,误码率曲线会有抖动。我习惯在循环开始前固定随机种子rng(42),这样每次跑出来的曲线可复现,调参数时也能排除随机性的干扰。等链路验证通过后,再换几个不同的种子跑几遍,确认曲线稳定。这套仿真系统本身不复杂,难的是每个环节的归一化、索引和同步细节。我自己的习惯是每加一个模块就先在理想信道下跑通,再加噪声,再加信道,最后加多用户。一次只改一个变量,出了问题才能定位到具体哪一行。希望帮到你。本文还有配套的精品资源,点击获取