☰
802.11a链路仿真:6M与24M速率的误码率与调制编码解析
2026/9/26 5:31:36 网站建设 项目流程

简介:基于802.11a标准的链路级仿真工程,面向无线通信与OFDM系统学习者,用以分析6Mbps与24Mbps传输速率下的误码率变化。工程完整覆盖物理层发射与接收链路,包括数据加扰解扰、卷积编码与译码、星座图映射与解映射(deconstellation map)、FFT/IFFT变换、循环前缀添加删除、同步及误码率统计等模块,通过对比不同速率下的星座图与误码结果,直观评估系统在衰减信道下的稳健性。同时,脚本支持修改调制方式与速率参数,便于扩展至QPSK或其他速率的对比仿真。资源共18个m文件,压缩包约10KB,均为MATLAB脚本,代码按发射、接收与公共处理模块拆分,便于局部替换参数或单独调试。已有232人学习下载,适合通信专业学生或工程师快速搭建802.11a仿真平台,加深对调制方式与速率权衡的理解,也可作为课程设计或课题验证的参考基线。

1. 基于802.11a链路仿真与误码率分析:先从 6Mbps 与 24Mbps 说起

做无线通信物理层仿真的人,手里多半都攒过几套“能跑通但不敢细看”的代码。这份名为 6M24M 的 MATLAB 工程就是典型的一套:它把 802.11a 的完整收发链路摊开在 9 个关键函数里,从加扰、卷积编码、交织、星座映射、导频插入、IFFT、加循环前缀,一路做到接收端的同步、FFT、解交织、Viterbi 解码和解扰,并且在 6Mbps(BPSK 1/2 编码)和 24Mbps(16QAM 3/4 编码)两种速率下分别统计误码率。对正在做 OFDM 课设、WIFI 物理层毕业设计,或者想手搓一个 802.11a 基带仿真链路的人来说,这份资源最大的价值不是“能出图”,而是它把 BPSK 和 16QAM 两种调制方式在同一个工程里做了对照——你能直接看到星座映射、解映射、误码率统计这几个环节在不同速率下的行为差异。本文把这套工程的模块分工、关键参数、误码率计算逻辑和最常见的翻车点逐一拆开讲。

2. 发射链路与接收链路:main.m 怎么把 17 个函数串成一条流水线

2.1 从 main.m 入手:先看清楚数据流再动手改参数

拿到工程第一步永远是打开 main.m,把数据流捋清楚,而不是先跑仿真。这套 802.11a 链路的发射端流程是:随机比特生成 → scramble(加扰)→ conv_encode(卷积编码)→ interweave(交织)→ ConstellationMap(星座映射)→ Add_Pilot(插入导频)→ IFFT64(OFDM 调制)→ Add_CP(加循环前缀)→ preamble(插入前导序列)。接收端则是严格逆序:synchronization(符号同步)→ Del_CP(去循环前缀)→ FFT64(OFDM 解调)→ DeConstellationMap(星座解映射)→ deinterweave(去交织)→ conv_decode(Viterbi 译码)→ descramble(解扰)→ count(统计误码率)。

% main.m 的核心调用顺序(发射链路) tx_bits = randi([0 1], 1, data_len); % 生成随机二进制数据 scrambled = scramble(tx_bits); % 802.11a 扰码器,多项式 x^7+x^4+1 encoded = conv_encode(scrambled, rate); % 卷积编码,rate 决定 1/2 还是 3/4 interleaved = interweave(encoded, rate); % 按 802.11a 标准做两级交织 mapped = ConstellationMap(interleaved, mod_type); % BPSK 或 16QAM 符号映射 [pilot_added] = Add_Pilot(mapped); % 插入导频子载波(索引 -21,-7,7,21) ifft_out = IFFT64(pilot_added); % 64 点 IFFT,得到时域 OFDM 符号 tx_signal = Add_CP(ifft_out, cp_len); % 加 16 采样点循环前缀 tx_frame = preamble(tx_signal); % 组帧,加前导用于接收端同步

这里的rate和mod_type是决定 6M 与 24M 差异的关键变量:6M 对应 BPSK 加 1/2 卷积编码,24M 对应 16QAM 加 3/4 卷积编码。data_len是单帧承载的比特数,在 6M 模式下通常是 24 个数据子载波 × 48(OFDM 符号数)× 1(每子载波比特数)× 1/2(编码率),计算时要保证能被交织矩阵整除。很多人在这一步翻车:data_len设置得和交织器的矩阵维度不匹配,运行直接报维度错误。

2.2 IFFT64 与 Add_CP:OFDM 符号成型的基本功

IFFT64.m 的作用是把频域符号数组变换到时域。802.11a 的 OFDM 符号是 64 点 IFFT,其中 48 个数据子载波、4 个导频子载波、1 个直流空载波,其余为保护子载波。这个子载波编号不是从 -31 到 32 的顺序排列,而是按 IEEE 802.11a-1999 标准映射到特定索引上。代码里需要把这些位置关系摆对,否则接收端做 FFT 后频域数据顺序与发射端不一致,后续解映射全是乱码。

% IFFT64.m 的核心逻辑 function tx_ofdm = IFFT64(freq_symbols) % freq_symbols 长度 64,已按 802.11a 子载波索引排布 % 索引 -26~-22, -20~-8, -6~-1, 1~6, 8~20, 22~26 为数据子载波 % 索引 -21, -7, 7, 21 为导频,索引 0 为空 tx_ofdm = ifft(fftshift(freq_symbols), 64); % fftshift 把负频率移到左侧 end

fftshift这一步是必须的:MATLAB 的ifft默认把索引 0 放在第一个元素,而 802.11a 的子载波规划里直流分量在正中间。不做fftshift的话,时域波形没错,但接收端对不上频率位置。Add_CP.m 则是把 IFFT 输出的最后 16 个采样点复制到符号开头,组成 80 采样点的完整 OFDM 符号。16 个采样点对应 0.8μs 的循环前缀,而 64 点对应 3.2μs 的有效符号时长,加起来刚好是 4μs 的标准 802.11a OFDM 符号长度。这块代码本身简单,但cp_len这个参数要跟接收端的 Del_CP 保持一致,否则同步做完后抽出的符号窗口是歪的。

2.3 preamble.m 与 synchronization.m:接收端先从噪声里找到符号起点

发射端把数据符号组帧后,在最前面插入前导序列。preamble.m 生成的是 802.11a 的 PLCP 前导,包含 10 个短训练序列(用于粗同步)和 2 个长训练序列(用于细同步和信道估计)。这个工程里 preamble 的主要作用是给接收端的 synchronization.m 提供相关峰,让接收端能定位到数据符号的开始位置。

% synchronization.m 的粗同步思路 function sync_idx = synchronization(rx_signal, threshold) % 用短训练序列的周期重复特性做自相关 % 接收信号延迟 16 采样点后与当前信号做共轭相关 window_len = 160; % 短前导总长度 160 采样点 corr = zeros(1, length(rx_signal) - window_len); for n = 1:length(rx_signal) - window_len a = rx_signal(n:n+15); % 当前 16 采样点块 b = rx_signal(n+16:n+31); % 延迟 16 采样点块 corr(n) = abs(sum(conj(a) .* b))^2 / (sum(abs(a).^2) * sum(abs(b).^2) + eps); end % 找到相关值超过阈值的一段平台区,取中点作为符号起点 sync_idx = find(corr > threshold, 1, 'first') + 16*8; end

自相关同步的优势在于不需要知道信道响应,只要短训练序列的重复周期在接收信号里保持不变,相关峰就会出现。threshold一般取 0.5~0.7,太小会把噪声误判成同步点,太大则可能漏检。一个常见的坑是:如果信道里叠加了大延迟的多径,自相关平台会被拉宽,直接用find(..., 'first')找起点会偏早,导致 FFT 窗口截到符号边界之外的样本。做 6M 和 24M 对比仿真时如果用的是 AWGN 信道,这个问题不突出,但一旦换到多径信道,同步点偏移对误码率的影响会立刻显现。

3. 星座映射和解映射:DeConstellationMap 的硬判决边界与软判决准备

3.1 ConstellationMap.m:BPSK 和 16QAM 的查表实现

ConstellationMap.m 做的事情本质上是查表:把交织器输出的比特流按每 1 个比特(BPSK)或每 4 个比特(16QAM)一组,映射成对应的复数符号。802.11a 的星座映射遵循 IEEE 标准里的灰度编码规则,相邻星座点之间只差一个比特,这样在解调出错时,误码大多是单比特错,不会出现一个符号错引发多位错误的情况。

% ConstellationMap.m 核心段(BPSK 与 16QAM 分支) function symbols = ConstellationMap(bits, mod_type) switch mod_type case 'BPSK' % 6Mbps 模式 % BPSK: 比特 0 -> 1, 比特 1 -> -1 symbols = 1 - 2 * bits; % 归一化功率为 1 case '16QAM' % 24Mbps 模式 % 每 4 个比特映射一个符号:b0b1 决定实部,b2b3 决定虚部 bits_reshaped = reshape(bits, 4, []).'; % 实部:b0b1 -> -3, -1, 3, 1(灰度码顺序) I = map_4bit(bits_reshaped(:,1:2)); Q = map_4bit(bits_reshaped(:,3:4)); symbols = (I + 1i*Q) / sqrt(10); % 除以 sqrt(10) 归一化平均功率 end end

注意 16QAM 前面的sqrt(10)归一化:16QAM 星座点坐标取 ±1、±3,平均功率是 (4×(1²+1²) + 8×(1²+3²) + 4×(3²+3²)) / 16 = 10,所以除sqrt(10)后平均功率跟 BPSK 一样是 1。不归一化的话,同一份噪声功率下 16QAM 的 SNR 表现会比理论值偏高,做出来的误码率曲线是假的。

3.2 DeConstellationMap.m:从复数符号倒推比特的三种做法

接收端的 DeConstellationMap.m 是这个工程里值得花时间读的模块。它接收 FFT 之后恢复出的频域复数符号,再根据调制方式反解出比特。代码里的硬判决逻辑很直接:BPSK 看实部正负,16QAM 则分别判决实部和虚部,按四象限划分映射回灰度码。

% DeConstellationMap.m 的硬判决核心 function rx_bits = DeConstellationMap(rx_symbols, mod_type) switch mod_type case 'BPSK' rx_bits = real(rx_symbols) > 0; % 实部大于 0 判为 1,否则为 0 rx_bits = 1 - rx_bits; % 映射回原始比特(1 -> 0, -1 -> 1) case '16QAM' I = real(rx_symbols) * sqrt(10); % 恢复归一化前幅度 Q = imag(rx_symbols) * sqrt(10); % 按判决边界 -2, 0, 2 对实部和虚部分别量化 I_bits = quantize_4bit(I); Q_bits = quantize_4bit(Q); rx_bits = reshape([I_bits, Q_bits].', [], 1).'; end end

硬判决在 AWGN 信道下性能尚可,但要注意边界点的设置:16QAM 的判决边界在 -2、0、2,落在边界上的点会随机判到相邻星座,这在高 SNR 下不是问题,低 SNR 下就成了误码的主要来源。如果想做软判决 Viterbi 解码,需要把 DeConstellationMap 的输出从比特改成对数似然比(LLR),计算每个比特在给定接收符号条件下的概率比。这个工程里用的是硬判决,误码率会比理论曲线差 1~2dB,在做结果分析时要心里有数。

4. 6M 和 24M 的链路差异:编码率、交织深度和误码率表现的联动关系

4.1 两种速率的参数对照:不是换个调制方式那么简单

802.11a 的 6Mbps 和 24Mbps 不是只改星座映射就行的。6M 用的是 BPSK + 1/2 编码率,24M 用的是 16QAM + 3/4 编码率。这两组参数的联动关系体现在编码增益上:编码率越高,冗余越少,纠错能力越弱,所以 24M 需要更好的信道条件才能达到同样的误码率。下面这个表是分析两种速率差异时要盯住的对照。

参数6Mbps24Mbps
调制方式BPSK16QAM
每子载波比特数14
卷积编码率1/23/4
编码后比特数数据比特 ×2数据比特 ×4/3
交织器深度48 比特192 比特
数据子载波数4848
每 OFDM 符号数据比特2496
同 SNR 下误码率低高

看这个表能直观理解为什么 24M 比 6M 更容易出错:数据子载波数固定 48 不变,但每个子载波上承载的比特从 1 个变成 4 个,星座点之间的距离显著缩短。BPSK 两个点之间的距离是 2,16QAM 相邻点之间的距离只有 2/√10 ≈ 0.63,同样的噪声功率下,噪声把符号推过判决边界的概率大大增加。而 3/4 编码率比 1/2 编码率少了约 1/3 的冗余,纠错能力也打了折扣。

4.2 interweave.m 与 deinterweave.m:为什么交织深度跟着速率走

交织器的作用是把连续的错误分散开,让 Viterbi 解码器能利用纠错能力。802.11a 的交织器是两级结构:第一级做相邻比特的排列,第二级做跨子载波的排列。6M 模式的交织深度是 48 个编码比特(对应一个 OFDM 符号的编码比特数),24M 模式的交织深度是 192 个编码比特。interweave.m 里通过rate和mod_type计算交织矩阵的列数,列数越多,相邻比特在频率上隔得越远。

% interweave.m 的参数逻辑 function interleaved = interweave(encoded_bits, rate, mod_type) if strcmp(mod_type, 'BPSK') N_cbps = 48; % 每 OFDM 符号编码比特数 d = 16; % 第一级交织的位移量 else % 16QAM N_cbps = 192; % 16QAM: 48 子载波 × 4 比特 d = 64; % 位移量随 N_cbps 增大而增大 end % 第一级交织: index = i + d * floor(i / N_cbps) mod N_cbps % 第二级交织: index = (N_cbps / d) * j mod N_cbps + floor(j / (N_cbps / d)) ... end

交织的位移量d不是随便定的,它和卷积编码的约束长度有关。802.11a 的卷积编码器约束长度为 7,最大自由距离对应码字长度约为 18 比特,交织位移必须大于这个值才能把相关错误分散开。所以 6M 用 16、24M 用 64,保证相邻编码比特在交织后至少相隔d个位置,这样突发错误就不会一次性击穿 Viterbi 解码器的纠错能力。deinterweave.m 是它的逆过程,注意逆交织时索引计算要跟发射端完全对称,差一个索引位置,误码率就下不去。

4.3 conv_encode.m 与 conv_decode.m:删余(puncturing)是 24M 速率的隐藏逻辑

conv_encode.m 里藏着 6M 和 24M 的另一处关键差异:删余。6M 的 1/2 编码率直接把生成多项式输出的两路比特都发出去,而 24M 的 3/4 编码率需要从 1/2 编码器输出里删掉一部分比特,接收端译码时再把删掉的比特当作不确定信息处理。这个工程里应当实现了标准的 802.11a 删余表:每 6 个编码比特删掉 2 个,留下 4 个,形成 3/4 码率。

% conv_encode.m 的删余逻辑(24M 模式) function encoded = conv_encode(bits, rate) % 生成多项式 G0=133(octal), G1=171(octal) % 先做 1/2 卷积编码,再按删余表取舍 coded = poly2trellis(7, [133 171]); % 约束长度 7,码率 1/2 full_coded = convenc(bits, coded); if rate == 1/2 encoded = full_coded; % 6M 模式:全保留 else % rate == 3/4 % 删余表: 保留位置 [1 2 3 5](每 6 位删 2 位) punctured_idx = mod(1:length(full_coded), 6); encoded = full_coded(punctured_idx ~= 0 & punctured_idx ~= 4); end end

接收端 conv_decode.m 对应要做解删余(depuncturing),把删掉的比特位置填 0 或填中性值再送进vitdec。调用vitdec时注意输入格式:硬判决模式下删余位填 0 会把 LLR 拉偏,标准做法是填一个中间值(如硬判决时填 0.5),让译码器对该位置不偏不倚。这个细节是 24M 误码率能否贴近理论值的关键。

5. 误码率统计与仿真结果分析:count.m 在数什么、报告什么

5.1 误码率的定义:是比特错误率不是符号错误率

count.m 是整个工程的收尾模块,它比较发射端的原始比特和接收端解调后的比特,统计错误的比特数占总比特数的比例。要注意的是,这里的误码率(BER)是比特级别的,和符号错误率(SER)不同。BPSK 的 BER 和 SER 相等,因为一个符号就是一个比特;16QAM 的 SER 会明显高于 BER,因为一个 16QAM 符号携带 4 个比特,其中有些比特(比如实部或虚部的符号位)在判决时出错概率低,而幅度位的判决出错概率高,灰度编码保证相邻符号翻一个比特后 BER 大约是 SER 的四分之一。

% count.m 的统计逻辑 function ber = count(tx_bits, rx_bits) % 两个序列长度必须一致,否则先对齐或截断 if length(tx_bits) ~= length(rx_bits) min_len = min(length(tx_bits), length(rx_bits)); tx_bits = tx_bits(1:min_len); rx_bits = rx_bits(1:min_len); end errors = sum(tx_bits ~= rx_bits); total = length(tx_bits); ber = errors / total; fprintf('总比特数: %d, 错误比特: %d, 误码率: %e\n', total, errors, ber); end

5.2 误码率曲线怎么画:扫 SNR 还是定点测

跑单次仿真只能得到一个点的误码率,要画出误码率随信噪比变化的曲线,需要在外层循环里不断改变 SNR 并重复整个收发链路。常见做法是给发射信号叠加高斯白噪声,用awgn(tx_frame, snr_db, 'measured')控制信噪比。这时有个隐含前提:awgn的 SNR 是相对于信号总功率的,而 802.11a 的误码率理论曲线通常以 Eb/N0(每比特能量与噪声功率谱密度之比)为横坐标,两者之间差一个因子。

% 误码率扫描主循环 snr_dbs = 0:2:20; % 扫描 SNR 范围 ber_6m = zeros(size(snr_dbs)); ber_24m = zeros(size(snr_dbs)); for idx = 1:length(snr_dbs) snr_db = snr_dbs(idx); % 6M 链路 ber_6m(idx) = run_link('BPSK', 1/2, snr_db, num_symbols); % 24M 链路 ber_24m(idx) = run_link('16QAM', 3/4, snr_db, num_symbols); end % 绘制误码率曲线 semilogy(snr_dbs, ber_6m, 'o-', snr_dbs, ber_24m, 's-'); grid on; xlabel('SNR (dB)'); ylabel('BER'); legend('6Mbps BPSK 1/2', '24Mbps 16QAM 3/4');

把两条曲线放在同一张图里,最直观的现象是:6M 在 SNR 4~6dB 时误码率开始急剧下降,而 24M 要到 14~16dB 才出现同样的下降趋势。这个约 10dB 的差距来自调制增益和编码增益两部分的叠加:16QAM 比 BPSK 需要多大约 7~8dB 的 SNR 才能达到同样的符号错误率,3/4 编码率又比 1/2 编码率少了约 2~3dB 的编码增益。如果仿真结果里这个差距明显小于 10dB,那大概率是哪一环的参数没对齐。

5.3 帧数与置信度:二次计数时注意事项

误码率仿真的置信度取决于统计的错误比特数。在低误码率区间(比如 10⁻⁴ 以下),如果总比特数不够,几个随机错误就能让曲线剧烈抖动。一般经验是至少统计 100 个错误比特,也就是说如果预计误码率是 10⁻⁴,至少要跑 10⁶ 个比特。Main.m 里num_symbols参数决定跑多少个 OFDM 符号,按 6M 模式每个符号 24 个数据比特来算,跑 1000 个符号也只有 24000 个比特,统计到 10⁻³ 就到头了。要在高 SNR 区间画曲线,得把num_symbols提到 10⁴ 以上,或者直接在循环里重复调用链路函数累加错误比特数。

另一个容易忽略的点是误码率统计前需要确认收发比特对齐。如果 synchronization.m 给出的符号起点偏差了一个采样点,FFT 窗口截错位置,解出来的比特会整体错位,误码率直接接近 0.5——这不是信道差,是同步坏了。所以 count.m 里加一个简单的对齐检查,比如先统计前 100 比特的误码率,如果接近 0.5 就说明有同步偏移或解映射索引错乱,而不是真的在测信道性能。

6. 常见问题排查:误码率曲线不贴理论的五类翻车点

6.1 现象:6M 模式下 SNR 14dB 时误码率仍高达 10⁻²

原因排查:先确认是否做对了fftshift。如果ifft前少了fftshift,频域子载波位置整体偏移,接收端 FFT 后对应不上发射端的映射位置,星座图外圈的点会叠到内圈上,低 SNR 时误码率会异常偏高。另一个常见原因是 DeConstellationMap 里的判决边界算错,比如 16QAM 的判决边界误写成 -1、0、1,那高 SNR 下误码率也会出现平层。

解决:在发射端 IFFT64.m 里补上fftshift,同时在接收端 FFT64.m 里做对应的逆操作。检查 DeConstellationMap 的判决阈值是否和 ConstellationMap 的星座坐标匹配——这两处必须是一套对应关系,改一处就要同步改另一处。

6.2 现象:24M 模式高 SNR 下误码率下不去,出现平层效应

原因:硬判决 Viterbi 解码的固有缺陷。硬判决在 16QAM 解映射时丢掉了可靠性信息,判决错误的概率在高阶调制下不可忽略。另外如果删余表实现有误,接收端未做解删余直接送vitdec,译码器会把被删比特当作 0/1 硬信息处理,纠错性能严重下降。

解决:把解删余位补偿为 0.5(或对应 LLR 的 0 值),再送进vitdec。如果要彻底消除平层,需要把 DeConstellationMap 升级为软判决输出 LLR,并改用vitdec(..., 'soft', num_soft_bits)接口。软解调带来的性能增益在 16QAM 上肉眼可见,通常能找回 2dB 左右的差距。

6.3 现象:加循环前缀后误码率反而劣化

原因:Add_CP 和 Del_CP 的长度不一致。发射端加了 16 个采样点的循环前缀,接收端 Del_CP 却截掉了 17 个采样点,或者 CP 长度写进 Add_CP 的是变量但 Del_CP 里写成了硬编码 16。这种情况下每个 OFDM 符号都会带入前一个符号的尾巴,等效于引入了符号间干扰。

解决:统一用cp_len变量,在 main.m 开头定义一次,两个函数都读它。还要注意 Del_CP 截取的起点是同步完成后的sync_idx + 16还是sync_idx + 0,这取决于 preamble 结构和同步算法的定义,建议在 Del_CP 里加 fprintf 打印实际截取区间做对照。

6.4 现象:同步相关峰在低 SNR 下抖动剧烈,误码率不稳定

原因:synchronization.m 里自相关窗口取的 16 个采样点是短训练序列的周期,但如果信号在进入同步模块前没有做自动增益控制(AGC),信号幅度忽大忽小,自相关归一化后的峰值也会跟着抖动。另外threshold定得太低时,噪声段的随机相关也会超过阈值,同步点会跳到数据符号中间。

解决:给corr序列加一个滑动平均滤波,窗口取 32~64 采样点,平滑掉噪声毛刺。threshold不要取定值,改成先找最大值再取最大值的 50%~60% 作为判定门限,这样对幅度变化不敏感。同步点最终取平台区的中间位置而不是第一个峰值,避免前导尾部多径干扰对起点判定的影响。

6.5 现象:BER 统计结果忽高忽低,多次运行偏差大

原因:单次仿真的随机比特序列和噪声没有做多种子平均,特别是randi和awgn每次运行生成的随机数不同,低 SNR 时一次运行可能只产生几十个错误比特,统计波动大。

解决:在 main.m 开头加rng(0)固定随机种子,保证每次运行结果一致。如果要评估平均性能,用外层循环跑 10~20 次取平均 BER,每次换一个种子,最后把平均值画到曲线上。这样得到的曲线稳定、可复现,也方便和理论误码率公式做对照。

7. 进阶玩法:把 6M 与 24M 的仿真结果往理论曲线上靠

7.1 把横坐标从 SNR 换成 Eb/N0:曲线才能和教科书对比

6M 和 24M 的调制和编码都不同,直接用 SNR 对比两条误码率曲线时,横坐标的单位不对齐,看不出链路之间的真实差距。正确的做法是把横坐标换算成 Eb/N0:先算出每个 OFDM 符号的数据比特数(6M 是 24 比特,24M 是 96 比特),再按公式 Eb/N0 = SNR - 10log10(数据比特数 / 总采样点数) 换算。假设一个 OFDM 符号是 80 个采样点,6M 的 Eb/N0 比 SNR 低 10log10(24/80) ≈ 5.2dB,24M 低 10log10(96/80) ≈ 0.8dB。换算之后,两条曲线在低误码率区间的差距会收缩到纯粹调制和编码增益的差异,这个值更接近理论预期的 7dB 左右。

% Eb/N0 换算公式 data_bits_per_symbol_6m = 24; % BPSK, 1/2 编码 data_bits_per_symbol_24m = 96; % 16QAM, 3/4 编码 total_samples_per_symbol = 80; % 64 FFT + 16 CP ebn0_6m = snr_dbs - 10*log10(data_bits_per_symbol_6m / total_samples_per_symbol); ebn0_24m = snr_dbs - 10*log10(data_bits_per_symbol_24m / total_samples_per_symbol); semilogy(ebn0_6m, ber_6m, 'o-', ebn0_24m, ber_24m, 's-'); xlabel('Eb/N0 (dB)'); ylabel('BER');

7.2 用理论公式校验仿真结果:BPSK 与 16QAM 的 BER 边界

手边没有理论曲线时,可以用两个近似公式快速校验仿真数据是否合理。BPSK 在 AWGN 下的理论误码率是 Q(√(2Eb/N0)),16QAM 的误码率按灰度编码近似为 (3/4)Q(√(4Eb/(5N0)))。在 MATLAB 里用qfunc直接算这两个值画在同一张图上,跟仿真曲线对比,差距在 0.5dB 以内说明链路实现基本正确,差距超过 2dB 就回去查同步和解映射的边界条件。

% 理论误码率参考曲线 ebn0_dB = 0:0.5:20; ebn0_lin = 10.^(ebn0_dB/10); ber_bpsk_theory = qfunc(sqrt(2*ebn0_lin)); ber_16qam_theory = (3/4) * qfunc(sqrt(4*ebn0_lin/5));

7.3 从单帧仿真扩展到链路级评测:多帧循环和置信区间

做毕业设计或者工程验证时,只跑一帧数据说服力不够。把 main.m 的核心链路封装成run_link(mod_type, code_rate, snr_db, num_symbols)函数后,外层写一个多帧循环,每帧独立生成随机比特并计算该帧的误码率,把所有帧的错误比特累加后得到整体误码率。同时记录每帧的误码率做方差估计,当置信区间宽度大到遮盖相邻两个 SNR 点的曲线差异时,就说明帧数不够,需要增加num_symbols或帧数。

我最初拿到这套工程时,误码率曲线在高 SNR 端始终拖着一个 10⁻² 的尾巴,一度怀疑是信道建模的问题。后来逐行排查才发现 conv_decode.m 里把解删余的占位符填成了 0,导致 Viterbi 译码器在这些位置上的分支度量完全偏向 0 比特,24M 模式的纠错能力直接报废。从那以后我每次做带删余的链路仿真,都会在译码前打印一段解删余后的比特流和发射端的删余掩码对照,强制走一遍这个检查再跑误码率统计。希望这个排查习惯也能帮到你少走几步弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询