首页 / 资讯中心 / 文章详情

MATLAB QPSK误码率仿真详解:从SNR换算到BER曲线实现

MATLAB QPSK误码率仿真详解:从SNR换算到BER曲线实现 ★ FEATURED ARTICLE
简介这是一份面向通信方向初学者与有一定开发经验的工程师的Matlab源码包用于计算QPSK调制下误码率随信噪比变化的仿真运行即可直接得到误码率-信噪比波形免去自行搭建仿真环境的麻烦。包内共2个文件一个.m脚本完整实现信号生成、加噪、解调与误码率统计代码注释详尽方便逐行阅读另一个docx文档给出了算法补充说明可作为扩展学习资料。压缩包整体仅13KB轻量精简尤其适合快速下载学习也可用于课程设计或毕业设计中的通信仿真部分。目前已有854人学习浏览经过达摩老生亲测校正质量可靠值得信任。通过这份资源读者既能获得可直接运行的QPSK误码率计算程序也能从注释与文档中理解仿真设计的完整思路掌握误码率性能分析方法减少自主摸索的时间成本。1. 拿到“matlab计算QPSK误码率随信噪比变化的程序源码”时先想清楚这三件事做QPSK误码率随信噪比变化的仿真是通信方向学生和工程师绕不开的一道基础题。你手里的这个matlab程序源码目标不外乎是画出一条误码率曲线让它从高误码区一路降到低误码区并且跟理论值贴在一起但它真正考你的是信噪比怎么在仿真里换算、噪声怎么加、统计多少次才算数。它适合刚开题需要跑通通信仿真流程的在校学生也适合要做链路预算或评估编码增益的射频工程师。这篇笔记不假装见过某个具体压缩包的内容而是把这一类程序背后最常见、最可靠的实现方案完整讲透从调制映射到噪声模型从主循环到避坑按你会实际动手的顺序来。2. QPSK仿真为什么绕不开信噪比换算Eb/N0与SNR的差异与理论基础2.1 QPSK调制与星座映射比特怎么变成I/Q两路符号QPSK的全称是Quadrature Phase Shift Keying一次传输两个比特对应四个相位状态。最常见的星座图是把四个点放在单位圆的四个象限上实部叫I路虚部叫Q路每一路的幅度都是±1/√2这样每个符号的平均功率刚好是1。做仿真时第一步永远是把一串随机的 0/1 比特流切成长度为 2 的块再按映射表把每一块变成一个复数符号。这个切分和映射过程看起来简单但新手最容易在“比特顺序”上翻车。matlab 里常见做法是先reshape成两列再用bi2de把每一行转成十进制索引这里要特别注意left-msb和right-msb的选择它决定 01 和 10 映射到哪个符号。我一般会用格雷映射也就是相邻相位只差一个比特的映射因为在高信噪比下最常见的错误是误判成相邻符号格雷映射能让这类错误尽量只引起一个比特错误实测误码率也更接近理论值。M 4; % QPSK 阶数 k log2(M); % 每符号比特数 2 nBits 100000; % 总仿真比特数 data randi([0 1], nBits, 1); % 随机二进制信源 bitMatrix reshape(data, k, []); % 每行是一个两比特组 symbolIdx bi2de(bitMatrix, left-msb); % 比特对 - 0~3 十进制索引这里reshape把长度为nBits的列向量变成nBits/2行 2 列的矩阵每一行对应一个 QPSK 符号。bi2de(..., left-msb)表示左边的比特是高位于是 00→0、01→1、10→2、11→3。选left-msb还是right-msb会影响星座图上四点的顺序但对后续误码率结果没有本质影响只要你发射和接收用同一套就行真正的坑在于后续de2bi转回比特时也必须用同一个方向否则解调出来的比特流会完全错乱曲线直接飘到天上。这一层还要注意不同的 matlab 版本对pskmod的默认映射行为不完全一样较新版本默认走格雷映射老版本则可能是自然映射。如果你后续要用手写判决而不是pskdemod就得保证自己的映射表和调制时用的完全一致否则会出现“星座点明明长得一模一样误码率却对不上理论曲线”的怪现象。这个细节在第四章我会专门展开。2.2 AWGN信道下的噪声模型Eb/N0与SNR的换算公式误码率曲线的横坐标到底用 SNR 还是 Eb/N0是这一类源码里分歧最大的地方。做通信的人习惯用 Eb/N0因为它把调制阶数的影响剥离开来对比不同调制方式时更公平但 matlab 的awgn函数只接受功率比 SNR所以你必须自己完成换算。QPSK 里每个符号携带 2 个比特符号功率归一化为 1 时每比特能量 Eb Es/k 1/2。于是SNR_linear EbN0_linear * k SNR_dB EbN0_dB 10*log10(k) EbN0_dB 3.0103这个 3dB 的偏移量是 QPSK 仿真的标志性数字。如果你把 EbN0_dB 直接当成awgn的 SNR 参数传进去实测误码率会比理论值偏大约 3dB——也就是曲线整体往左移看起来你“做出了更好的误码率曲线”实际上只是噪声加少了。反过来如果有人拿 BPSKk1没有这 3dB 偏移的代码直接改成 QPSK不改换算就会得到完全错误的绝对信噪比指标。判断一段代码是否靠谱第一眼就应该看它有没有做这个10*log10(k)的补偿。EbN0_dB 6; % 目标每比特信噪比 snr_dB EbN0_dB 10*log10(k); % 换算成符号信噪比 rxSig awgn(txSig, snr_dB, measured);awgn的第三个参数measured表示先实测信号功率再加噪声这能避免因滤波器或归一化残留导致的功率误差如果你的调制信号已经严格归一化也可以省略这个参数直接按默认的 0dB 信号功率加噪。但要注意awgn的 SNR 定义是信号功率与噪声功率之比不是信号幅度与噪声幅度之比所以当你手写噪声时每一维噪声功率应该是 N0/2而不是 N0。这个“2”是第四章第一个大坑的来源。2.3 理论误码率公式用erfc把Q函数落到matlab里有了星座映射和噪声模型接下来需要一条理论曲线作为基准。QPSK 在 AWGN 信道下、采用格雷映射时误码率理论值可以写成BER 0.5 * erfc(sqrt(EbN0_lin))这里 EbN0_lin 是线性值不是 dB 值。很多人会把 dB 值直接代进去算出来的理论曲线完全不对。注意这里用的是误码率 BER不是误符号率 SERQPSK 的误符号率理论值是erfc(sqrt(EbN0_lin))的高阶修正形式约等于 BER 的两倍。拿 SER 的理论值去对比仿真 BER曲线会始终高出一截怎么调噪声模型都救不回来。EbN0_dB 0:8; EbN0_lin 10.^(EbN0_dB/10); ber_theory 0.5 * erfc(sqrt(EbN0_lin)); semilogy(EbN0_dB, ber_theory, k-, LineWidth, 1.5); hold on;matlab 里erfc是内置函数不需要额外装工具箱qfunc也存在于通信工具箱里两者差一个系数qfunc(x) 0.5*erfc(x/sqrt(2))。理论上这两条路写出来的数值是一样的但qfunc在部分老版本里对输入参数的向量化处理有边界限制我建议直接用erfc少一层环境依赖。上面这段代码把 0 到 8dB 的理论曲线算出来了后面仿真的实测点如果要和它对比横坐标必须也是 EbN0而不是建模时的符号 SNR。这一节最后还要强调一个概念Eb/N0 和信噪比 SNR 不是一回事前者是每比特能量对噪声功率谱密度的比后者是信号功率对噪声功率的比。两者之间差了10*log10(k)还差了采样率相关的因子——如果你后面引入过采样、匹配滤波或者升余弦脉冲成型SNR 表达式里还会多一项-10*log10(nSamp)。做基带等效仿真的话我一般建议直接用 EbN0 作为主变量全程换算到符号 SNR 去加噪这样理论曲线和实测点天然对齐。3. 在matlab里搭起QPSK误码率仿真的主循环信号生成、加噪、判决、统计3.1 用通信工具箱快速跑通第一版pskmod awgn 组合如果你机器上装了 Communications Toolbox最快的一条路是用pskmod、awgn、pskdemod三个函数搭主循环。这个组合的好处是不用自己写星座映射和最近邻判决代码短、出错面窄特别适合先验证整体思路。第一版我一般写成下面这样clear; clc; M 4; k log2(M); nBits 5e5; % 不要一上来就跑到1e7先小规模验证 EbN0_dB 0:2:10; % 偶数格点先跑通再加密 ber_mc zeros(size(EbN0_dB)); for idx 1:length(EbN0_dB) data randi([0 1], nBits, 1); symIdx bi2de(reshape(data, k, []), left-msb); txSig pskmod(symIdx, M, pi/M, gray); % 格雷映射、相位偏移 pi/4 snr_dB EbN0_dB(idx) 10*log10(k); rxSig awgn(txSig, snr_dB, measured); rxSym pskdemod(rxSig, M, pi/M, gray); rxBits reshape(de2bi(rxSym(:), k, left-msb), [], 1); ber_mc(idx) mean(rxBits ~ data); end这一段里pskmod的第三个参数pi/M是相位偏移让四个星座点位于 45°、135°、225°、315°这是 QPSK 教科书里最常见的画法。第四个参数gray指定星座映射顺序为格雷映射。de2bi(rxSym(:), k, left-msb)返回nSymbols行 2 列的矩阵转置后按列reshape成 1D 比特流目的是把每个符号的两位比特按顺序排列回去。这行转置加reshape的顺序是所有脚本能否对齐的关键建议自己用很小的nBits比如 8单步调试确认映射无误。mean(rxBits ~ data)是误码率的朴素估计它默认你的比特流没有对齐延迟。如果实际系统中存在传播延迟或滤波器群延迟你需要先做相关对齐再比较否则误码率会假性偏高但在基带等效的单点仿真里发射和接收都是理想同步的所以这个简单写法没问题。3.2 不用工具箱的手写版映射、加噪、判决全流程很多工程机没有通信工具箱或者你想彻底搞清楚每一步在算什么那就手写整个链路。手写版更能暴露对噪声功率谱密度 N0 的理解是否到位我建议至少完整写一遍即使最后跑通版本用的是工具箱函数。下面的代码是完整的手写版 QPSK 仿真clear; clc; M 4; k 2; constellation [11i, -11i, -1-1i, 1-1i] / sqrt(2); grayMap [0 1 3 2]; % 格雷映射: 00-0, 01-1, 11-3, 10-2 nBits 1e6; EbN0_dB 0:2:10; ber zeros(size(EbN0_dB)); for idx 1:length(EbN0_dB) EbN0_lin 10^(EbN0_dB(idx)/10); N0 1 / (k * EbN0_lin); % 归一化条件下 Eb 1/k noiseVar N0 / 2; % 实部虚部各占一半噪声功率 data randi([0 1], nBits, 1); symIdx bi2de(reshape(data, k, []), left-msb); txSig constellation(grayMap(symIdx1)).; noise sqrt(noiseVar) * (randn(size(txSig)) 1i*randn(size(txSig))); rxSig txSig noise; rxSym zeros(size(txSig)); for n 1:length(txSig) [~, minIdx] min(abs(rxSig(n) - constellation)); rxSym(n) grayMap(minIdx); % 映射回十进制索引 end rxBits reshape(de2bi(rxSym(:), k, left-msb), [], 1); ber(idx) mean(rxBits ~ data); end这里的核心是噪声功率计算。因为星座点归一化到单位功率符号能量 Es1每比特能量 Eb Es/k 1/k于是 N0 Eb / EbN0_lin 1/(k·EbN0_lin)。高斯白噪声在 I、Q 两路各占一半功率所以每路噪声方差是 N0/2幅度标准差是sqrt(N0/2)。randn(...) 1i*randn(...)产生复高斯噪声实部虚部独立同分布这正是 AWGN 的复基带等价形式。很多手写代码忘记给实部虚部分别乘sqrt(0.5)结果总噪声功率变成 N0 而不是 N0/2实测曲线会整体右移 3dB。判决部分用暴力最近邻遍历。constellation只有 4 个点这个循环开销可以忽略但如果你后面扩展到 16QAM 或 64QAM建议用knnsearch或者把星座点拆成实部虚部单独判决否则循环会拖慢整个仿真。格雷映射在这里体现为grayMap(symIdx1)完成从索引到星座点的查表判决时又把最近的星座点通过grayMap(minIdx)映射回索引保证收发一致。grayMap的选择会影响理论 BER 公式——只有格雷映射下0.5*erfc(sqrt(EbN0))才严格成立。3.3 把主循环包装成脚本数据长度、信噪比范围、随机种子怎么定跑通单点仿真后要把它扩展成一条完整的曲线就需要考虑三个全局参数仿真比特数、信噪比范围、是否固定随机种子。固定随机种子用rng(0)放在脚本开头能让每次运行结果可复现——这个习惯在调参阶段非常重要否则你每次看到的曲线都是新一批随机数无法判断改动是否有效。rng(0); % 固定随机种子保证可复现 EbN0_dB 0:1:12; % 加密横坐标但高信噪比区要加仿真量 nBitsMin 1e5; % 低信噪比区的基础样本数 nBitsMax 2e7; % 高信噪比区的最大样本数 ber zeros(size(EbN0_dB)); for idx 1:length(EbN0_dB) if EbN0_dB(idx) 6 nBits nBitsMin; else nBits nBitsMax; end % ... 主仿真链路同 3.1 或 3.2复用同一份代码... ber(idx) sum(errBits) / nBits; end逐点分配nBits的动机是误码率的统计精度。误码率的相对方差大约正比于1/sqrt(N_errors)在高信噪比区比如 10dB 以上误码率可能只有 1e-6如果还用 1e5 个比特整个仿真过程一个误码都碰不到曲线的尾部就会变成 0在 log 坐标上直接掉出图外。常见做法是先跑一遍粗曲线观察误码率在哪个 EbN0 区间低于 1e-4然后只对尾段增加样本数。我自己的习惯是低信噪比区用 1e5每上升 2dB 提高一倍样本量最多跑到 2e7 就打住——再往上不是不能跑而是单次仿真耗时会从秒级变成分钟级且系统误差比如awgn的measured估计误差会开始主导加样本的钱包边际收益很低。主循环的写法上for循环逐 dB 扫描最直观也最容易加打印进度。也可以用arrayfun或parfor并行化但 QPSK 单次仿真本身很快并行化的收益要到大规模蒙特卡洛扫描时才体现。我更建议先把串行版跑通再用parfor替换最外层的for作为优化手段不要在调试阶段引入并行。4. 常见避坑与排查误码率曲线不平滑、信噪比偏3dB、判决方向不一致4.1 加噪功率算错实测BER与理论曲线差3dB怎么排查现象仿真出的误码率曲线形态和理论曲线完全一致斜率一样但整体向左或向右平移约 3dB高信噪比区尤其明显。原因几乎总是噪声功率多加了或少加了一半。用awgn时把 EbN0 直接传进去少了10*log10(k)的换算噪声偏小曲线左移手写噪声时把 N0 当成总方差而不是每维方差噪声偏大曲线右移。排查办法很简单在固定 EbN06dB 处打印当前理论的 BER 和实测 BER如果相差正好接近 3dB 对应的数量级就先检查换算式里的 k再检查噪声方差有没有多乘 2。EbN0_test 6; EbN0_lin 10^(EbN0_test/10); ber_theory_test 0.5 * erfc(sqrt(EbN0_lin)); % 理论值约为 0.0024实测若明显小于它大概率是噪声加少了这个 3dB 偏移的隐蔽之处在于它不改变曲线的形状只改变横坐标的偏移所以如果你只画 semilogy 图、不对照理论值肉眼很难发现。我见过不少人拿着一条“看起来很好”的曲线发到报告里结果评审老师一对照理论曲线就发现整体平移了。务必在图上同时画ber_theory和ber_mc并且两者用同一横坐标变量才能暴露这类问题。4.2 高信噪比下误码数为0统计窗口不够与曲线断裂现象仿真曲线在 EbN0 超过某个点之后误码率变成 0semilogy 图上曲线突然掉到图外。这不是系统做了什么神奇优化纯粹是统计样本不够。在误码率 1e-5 以下想要测到一个误码平均需要 1e5 个比特如果误码率为 1e-6你还用 1e6 个比特那么“刚好一个误码都没出现”的概率约等于 37%任何一次仿真都可能撞上。解决思路不是无脑加样本而是设一个最小误码数目标。targetErrors 100; % 至少统计到 100 个误码 while errTotal targetErrors % 继续生成新一批数据累加误码数与总比特数 % 每批之间共用同一 EbN0 end ber_est errTotal / nBitsTotal;“等够误码数再停”比“固定跑多少个比特”在统计上更稳因为它保证了相对估计误差大致在 1/sqrt(100)10% 以内。代价是仿真时间不可控所以在循环里要加最大迭代次数防止在误码率 1e-8 这种极端条件下跑到天荒地老。另一个折中方案是按误码率量级分级设nBits像我在 3.3 里写的那样大部分场景都够用。还有一个和零误码同源的现象低信噪比区 BER 曲线抖动大。原因是误码事件相关性较强尤其在比特级统计时误码往往成串出现。解决办法可以是增大随机种子重掷次数把多次独立仿真的误码数合并再算不要只跑一次就下结论。rng换不同种子跑 5 次取平均误码率曲线的光滑度会有明显改善。4.3 接收端映射方向不一致星座点对了但全错现象星座图看起来四相位置完全正确解调的星座点与发射点重叠但逐比特对比时误码率高达 50%。原因是grayMap的使用在发射和接收两端不对称。比如发射端用pskmod(symIdx, M, pi/M, gray)接收端却用pskdemod(rxSig, M, pi/M, gray)这两个版本对同样的复数信号解出的符号索引与你的de2bi解释方式是否一致取决于 matlab 内部映射表的方向。更常见的是手写映射表时grayMap [0 1 3 2]与pskmod内置的格雷顺序不同导致某一象限的判决结果被映射错。排查方法把发射的符号索引和接收判决的符号索引直接画出来对比不要只比较最终的比特流。在nBits8的极小规模下打印symIdx和rxSym逐项核对。如果只有部分符号错通常是某一列的比特位顺序反了如果全部错通常是映射表整体错位。这里最稳的写法是收发共用同一个映射表常量不要在两处分别定义。4.4 实部虚部交换一种只在复数信道上出现的隐蔽错误现象误码率曲线在高信噪比区域比理论高出大约一倍且不随 EbN0 增加而趋于和理论重合而是始终保留一个固定的差距。原因发射时用txSig constellation(idx).如果写成constellation(idx)而没有转置向量方向不对接收端判决时又恰好按行向量处理导致符号顺序错位。这种方向错误在低信噪比时被噪声掩盖误码率看起来还在理论附近一旦信噪比升高噪声不再是主导因素系统性的符号错乱就会显露出来。% 错误写法txSig 是行向量还是列向量会被后续加法广播规则悄悄改变 txSig constellation(symIdx); % 可能变成行向量 noise sqrt(noiseVar) * randn(1, length(symIdx)); % 行噪声 % 正确写法统一为列向量 txSig constellation(symIdx).; noise sqrt(noiseVar) * (randn(length(symIdx), 1) 1i*randn(length(symIdx), 1));这类问题用size()逐一检查就能发现但新手往往只盯着误码率数字忽略了中间变量的维度。我建议在每步操作后加一行assert(size(txSig,1) size(noise,1))让维度错误在第一时间爆出来而不是等你对着曲线猜半天。这一条算是血泪经验matlab 的隐式扩展让很多形状问题被掩盖等到最终结果异常时排查成本远高于一开始的防御性检查。5. 用三条验证线路确认仿真没白跑从单点数值到整条曲线拟合5.1 先验单点再验曲线最后验斜率很多同学把仿真代码一跑完看到一条下降的曲线就直接截图放到报告里。这个习惯隐患很大因为误码率曲线只要大方向对了细节错误很难看出来。我给自己的验证流程是三层第一层选一个中等信噪比点比如 EbN06dB把理论 BER 算出来实测值在它上下 20% 以内才算通过第二层把 0dB 到 4dB 区间内的实测曲线和理论曲线画在同一张图上允许的偏差是每点都在同一数量级第三层检查曲线斜率高信噪比区每增加 1dB误码率应显著下降如果某一段变成平缓甚至回升说明有系统错误或仿真点数不够。EbN0_check 6; ber_theory_check 0.5 * erfc(sqrt(10^(EbN0_check/10))); ber_mc_check ber(EbN0_dB EbN0_check); fprintf(theory %.4e, simulated %.4e\n, ber_theory_check, ber_mc_check);这层验证本质上是在检验“理论模型”与“仿真模型”的边界条件是否一致。理论公式假定理想同步、无定时误差、AWGN 信道如果你的仿真里加入成型滤波、载波同步或多径信道理论曲线就不再适用这时候对比对象要换成更接近实际系统的链路级模型。对于没有工具箱的纯手写仿真我建议至少保留理论曲线作为 sanity check不要直接拿别人的链路预算当基准。5.2 参数调优仿真点数、信噪比步长、重复次数怎么取舍做蒙特卡洛 BER 仿真本质是用计算时间换统计精度。下面这张参数表是我跑 QPSK 时常用的起点按信号处理链路复杂度和机器性能调整参数低配起点标准配置说明每点仿真比特数1e51e6高信噪比区至少 1e7EbN0 范围0:2:120:1:14步长 1dB 够画曲线0.5dB 用于精细对比随机种子rng(0)rng(0) 5 次重复重复次数多用于低信噪比区平滑目标最小误码数50100保证相对误差低于 10%信噪比步长从 2dB 细化到 1dB会直接影响高信噪比区的曲率表现但代价是总点数翻倍。如果不确定某个区间是否需要加密可以先跑 2dB 粗曲线找到曲线拐点通常是误码率从 1e-2 到 1e-5 那段再单独加密这个区间。样本数的分配上一个容易被忽略的点是EbN00dB 附近 BER 约 0.08用 1e5 个比特测试能得到约 8000 个误码统计已经很稳而 EbN010dB 附近 BER 约 4e-6需要 2.5e7 个比特才能得到 100 个误码。所以把大部分计算预算投给高信噪比区永远是合算的。5.3 图上验证semilogy坐标系下的视觉检查清单仿真结果的呈现方式也会影响排查效率。误码率曲线必须用semilogy或loglog在线性坐标下 1e-2 和 1e-5 看起来几乎都是零无法判断趋势。画图时把理论曲线画成实线黑线仿真点在图上用圆圈标出不要用点线连接起来——仿真点是样本估计不是连续函数用虚线连反而会误导观察。检查时重点看三处曲线末端是否出现平台期、曲线是否与理论曲线交叉、低信噪比区是否出现系统性偏移。出现平台期是零误码交叉意味着某个区段内模型偏差方向反转常见于映射错乱或残余符号间干扰系统性偏移则几乎总是功率换算问题。semilogy(EbN0_dB, ber_theory, k-, LineWidth, 1.5); hold on; semilogy(EbN0_dB, ber_mc, b-o, MarkerSize, 6); grid on; xlabel(Eb/N0 (dB)); ylabel(BER); legend(Theory, Simulation, Location, southwest);图本身也是最早能暴露问题的地方如果你的实测点在 0 到 4dB 区间比理论点低很多误码率更小第一反应不应该是高兴而是几乎可以断定噪声少加了——这是一个“看起来太好”恰是错误信号的高频场景。反过来实测明显差于理论则先检查接收判决方向、噪声方差、以及比特流对齐这三大元凶。6. 从“能跑”到“能复用”把QPSK脚本改造成参数化仿真函数验证通过之后下一步是把散乱的脚本改造成一个参数化函数让它的输入是调制阶数、EbN0范围、目标误码数输出是 BER 向量和耗时信息。这样同一套代码可以复用于 BPSK、16QAM甚至以后扩展 OFDM 子载波仿真。function [ber, EbN0_dB] simulateBER(M, EbN0_dB, doPlot) if nargin 3, doPlot false; end k log2(M); % 星座点和格雷映射表可按 M 生成这里只列 QPSK constellation [11i, -11i, -1-1i, 1-1i] / sqrt(2); grayMap [0 1 3 2]; ber zeros(size(EbN0_dB)); for idx 1:length(EbN0_dB) % 内部复用 3.2 的加噪、判决链路 end if doPlot semilogy(EbN0_dB, 0.5*erfc(sqrt(10.^(EbN0_dB/10))), k-); hold on; semilogy(EbN0_dB, ber, b-o); end end函数化的好处不只是代码整洁更重要的是把“调制方式选择”“噪声模型”“判决规则”这三个变点隔离出来以后换方案时只需要改星座生成和映射表。我自己最早的 QPSK 脚本就是满屏全局变量的面条代码后来在对比 16QAM 时吃了大亏——改动一个参数结果完全乱了。从那以后我养成的习惯是任何要跑超过三天的仿真脚本第一天就写成函数哪怕参数少也要让每个输入变量的含义明确。如果你以后要在这个基础上做信道编码对比、加卷积码或 LDPC还可以把simulateBER里的加噪和判决部分单独抽成channelModel和demodulator两个子函数这样误码率统计逻辑与具体调制方案解耦。回头看QPSK 误码率仿真永远是通信链路仿真里最透明、最不容易出错的模块但恰恰因为简单很多人忽略了系统化验证。从固定随机种子开始到逐点核对样本量再到最后曲线与理论的吻合每一步都能筛出几类不同的问题。希望这份操作路径能帮你少走几趟弯路也希望你那版源码跑出来的第一条曲线就是一条跟理论贴得漂亮、可信度经得起追问的曲线。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站