ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MATLAB实现QPSK过AWGN信道:误码率仿真与理论曲线对比

MATLAB实现QPSK过AWGN信道:误码率仿真与理论曲线对比 我们直接把话说在前头这期内容讲的是用MATLAB写一个QPSK调制信号过AWGN信道的完整仿真程序重点落在误符号率SER和误比特率BER怎么算、怎么画曲线、怎么跟理论值对上。不是只丢一句“理论SER是2Q(sqrt(2Eb/N0))”就完事的那种敷衍教程。我会从工程实现角度把整条链路拆开包括为什么要用蒙特卡洛仿真、星座点怎么映射、信噪比怎么换算、判决区域怎么写、统计错误数时为什么不能直接把对比符号相减这些坑我都会一个一个填平。适合刚学通信系统仿真、要做课程设计或者毕业论文里“仿真验证”部分的朋友直接抄作业也适合已经写过一点MATLAB但总感觉曲线对不上的同学用来排查问题。很多初学者一拿到这种题目就开始写代码写了半天发现误码率曲线在低信噪比区域跟理论值差了十万八千里回头查了一天也不知道哪错了。这种问题的根源通常不在代码本身而在你对“仿真的统计口径”和“信噪比定义”这两个概念的把握上。所以我先把整体设计思路讲透再放完整可运行的代码最后把你最可能踩的坑一次性列清楚。1. 整体设计思路为什么这么搭每个环节的取舍在哪1.1 仿真链路的核心构成一个完整的QPSK过AWGN信道仿真链路从信号角度看就五个环节随机比特生成、符号映射、加噪声、符号判决、错误计数。这五个环节看着简单但每一环都有“标准做法”和“工程做法”之分选错了直接影响结果和运行效率。比特生成环节要用randi([0,1], 1, N)生成均匀分布的0/1序列这没什么好说的。关键在于符号映射环节QPSK的星座点映射方式不是只有一种常见的是Gray映射和自然映射两种。这个选择直接决定理论误比特率公式用哪一个、判决区域怎么划分甚至影响你最后画出来的BER曲线和理论值是否一致。很多代码写成“随便把两个比特合一个符号、再随便映射到某个复数点上”结果就是仿真曲线乱成一团对不上理论公式因为理论上界公式通常默认Gray编码。AWGN信道建模这部分核心不是awgn这个函数本身而是信噪比的换算关系。你要在代码里清晰地知道Eb/N0、Es/N0、符号信噪比SNR和噪声功率sigma2这四者之间的转换公式。我看到太多人的代码把awgn函数的输入参数直接设成EsN0_dB还以为是EbN0最后理论BER曲线用的是EbN0坐标仿真曲线自然对不上。所以我会在这一节花力气把换算关系交代清楚然后再给代码。1.2 蒙特卡洛思想的核心逻辑与样本量本质上误符号率和误比特率的仿真属于蒙特卡洛方法。大数定律告诉我们只有在样本量足够大的时候错误事件的频率才会收敛到概率。低信噪比的时候错误多几千个符号就能统计得不错但高信噪比的时候错误非常稀疏需要几十万甚至上百万个符号才能得到一个稳定且准确的误码率值。这个逻辑非常关键。如果你在每个信噪比点都用固定数量的符号比如一万个符号那么到了高信噪比区域你可能一个错误都检测不到误码率画出来的就是0。在log坐标上0是没法表示的很多人的仿真曲线到高信噪比突然断掉就是这个问题。正确的做法是每个信噪比点统计到足够多的错误数比如100个或500个再停止或者设置一个错误数下限兜底。这直接关系到最终曲线质量后面代码部分我会给出具体实现。1.3 为什么用SER和BER两条指标同时统计误符号率度量的是“我发了一个符号接收端判决出错的概率”误比特率度量的是“我发了一个比特接收端判决出错的概率”。QPSK一个符号携带两个比特看起来BER SER / 2这里我直接说结论不完全对只有在Gray映射的前提下才近似成立而且即便Gray映射也只是近似准确说在QPSK的特定星座结构下是精确成立的。先说为什么。QPSK的四个星座点按Gray映射排列时相邻星座点之间只差一个比特。噪声导致符号判决错误时绝大多数错误都是错到相邻星座点。错误符号里只有一个比特错的概率接近1两个比特都错的概率极低。SER统计的是一次符号判决错误这个错误只代表一个比特错误时SER就等于BER两个比特都错时这个符号贡献的比特错误数是2。从数学期望角度在理想Gray映射下SER ≈ BER而不是SER/2。很多教材和论文直接把QPSK理论误比特率写成1/2 * erfc(sqrt(Eb/N0))这实际上是先给SER的公式再除以2它的前提其实是自然映射下的近似。所以不要笼统认为“BER就是SER除以2”仿真的时候直接用比特流对比来统计BER才是从定义出发的最稳方法。话说回来除了Gray映射还有人用自然映射自然映射下相邻星座点可能差两个比特所以BER会比Gray映射明显高。如果哪一天你看到别人的仿真曲线比自己低一半先别急着怀疑性能先看一眼他的星座映射方式。2. QPSK调制与AWGN信道模型先把基本概念全部掰开揉碎2.1 从比特到符号QPSK到底做了什么QPSK调制就是把两个二进制比特的组合映射成一个复数符号。最常见的映射是00 - 1 1j01 - -1 1j11 - -1 - 1j10 - 1 - 1j每个符号携带2比特符号速率是比特速率的一半。星座图上四个点构成一个正方形相邻两点距离是2平均功率是2。所以这里有一个很容易被忽略的细节如果你直接生成了复符号然后往里面加方差为N0的复高斯噪声你没有做功率归一化你的分母上可能多了一个2的因子。这个2的因子就足以让你的整条仿真曲线横移3dB。我遇到太多人问我这个问题我一问星座点平均值功率他们一算果然是2。正确的工程做法要么把符号功率归一化到1即符号 / sqrt(2)要么在计算噪声方差的时候把信号功率考虑进去。为了跟理论公式衔接最直接建议归一化到平均功率为1这样Es1Es/N0的换算关系最干净。理论公式中QPSK的符号错误概率写成2Q(sqrt(Es/(2N0)))时也是基于这个归一化。如果你懒得归一化就一直不归一化那么你能保证每一处推导都把信号功率写进去那也没问题但初学者恐怕做不到处处统一所以归一是最简单可靠的方案。2.2 AWGN信道中噪声的本质AWGN信道里接收信号等于发送信号加上一个白高斯噪声。在实际数字基带仿真中我们是把复基带信号建模为r s n其中n是复循环高斯随机变量实部和虚部是独立的零均值高斯随机变量方差都是N0/2。也就是说噪声的实部和虚部各携带一半噪声功率。很多人在写代码的时候只加了一维高斯噪声比如r s sqrt(N0)*randn(1, N)这样算出的噪声功率只有N0实际上是给复信号只加了实噪声最后误码率会比理论值差很多。正确的加噪写法有两种。一种是用复噪声r s sqrt(N0/2)(randn(1,N) 1jrandn(1,N))另一种是直接用awgn函数r awgn(s, EsN0_dB, measured)。我强烈建议初学者先把第一种手写方式理解透因为awgn函数内部做的事情你看不到出了问题很难排查。理解了第一种方式后再去用awgn函数做快速验证相当于双保险。另外要特别注意加噪是加在符号级不是加在比特级。比特是0/1的离散量噪声是连续量你没法直接对一个比特值加一个高斯噪声。正确的加法是“比特 - 符号 - 加噪声 - 判决 - 比特”。这个顺序绝对不能反。2.3 误符号率的判决区域边界在哪判错有多大的概率QPSK的判决规则实际上是一个二维平面上的最近邻判决。接收到的符号落在哪个象限就判成哪个星座点。在这个最简单的模型下判决区域就是四个象限实部0判右侧实部0判左侧虚部0判上方虚部0判下方。代码实现的时候先sign(real(r))和sign(imag(r))得到象限位置再反映射回比特是最快的做法。但这个判决方式有一个隐含假定四路信号等概率发送星座点对称。如果哪个星座点出现概率更高最优判决边界就变成非线性曲线那就超出了本题范围。对于标准QPSK等概率对称星座条件下象限判决就是最大后验概率判决也是最优的。理论上误符号率为什么写成2Q(sqrt(2Eb/N0))因为一个符号有两个独立的正交分量每个分量误判概率是Q(sqrt(2Eb/N0))两个分量合起来整个符号错判概率近似两倍单个分量出错的概率再减去两个分量同时出错的极小概率项。这个推导在教材里有的是但代码中出现的误码率统计值不会告诉你是理论曲线推导过来的所以自动比对才是关键。3. 完整MATLAB实现每一行代码都能讲清为什么3.1 主程序框架与参数定义下面这段代码是我在实测环境中反复调过的直接可运行。该程序结构清晰、注释完整可在此基础上扩展成16QAM、PSK或带编码的通信系统。先列个总框架细节在后面逐一展开。clear; clc; close all; % 基础参数设置 EbN0_dB 0:2:12; % Eb/N0 范围单位 dB numBits 2e6; % 总比特数建议至少 1e6 以上 numSymbols numBits / 2; % QPSK 每符号2比特 % 预分配存储 ber_sim zeros(size(EbN0_dB)); ser_sim zeros(size(EbN0_dB)); ber_theory zeros(size(EbN0_dB)); ser_theory zeros(size(EbN0_dB)); % 主循环 for idx 1:length(EbN0_dB) EbN0_lin 10^(EbN0_dB(idx)/10); EsN0_lin 2 * EbN0_lin; % QPSK 每符号2比特 noiseVar 1 / (2 * EsN0_lin); % 归一化星座点功率为1时的每维噪声方差 % 生成随机比特流 txBits randi([0 1], 1, numBits); % 比特 - 符号映射Gray编码 txSymbols bits2symbols(txBits); % 加 AWGN 噪声 noise sqrt(noiseVar) * (randn(1, numSymbols) 1j*randn(1, numSymbols)); rxSymbols txSymbols noise; % 符号判决 符号 - 比特 rxBits symbols2bits(rxSymbols); % 误码统计 bitErrors sum(txBits ~ rxBits); symErrors sum(txSymbols ~ rxSymbols); ber_sim(idx) bitErrors / numBits; ser_sim(idx) symErrors / numSymbols; % 理论值 ber_theory(idx) qfunc(sqrt(2 * EbN0_lin)); ser_theory(idx) 2*qfunc(sqrt(EsN0_lin/2)) * (1 - 0.5*qfunc(sqrt(EsN0_lin/2))); end注意这里第17行的sum(txSymbols ~ rxSymbols)这种写法直接对复数符号做不等比较在浮点判决后如果噪声没有让符号越过判决边界理论上恢复出来的复数符号和原始符号在数值上是严格相等的因为我们判决后得到的是标准星座点所以这个不等于在统计上是有效的。这个写法的前提是判决后一定要重新量化回标准星座点而不是拿加噪后的连续值去比。如果你的判决函数返回的是“最近星座点的复数坐标值”那自然没问题。但这里还有一个隐患判断条件里我没加“错误符号里错几个比特”SER和BER是两条独立的统计路径SER按符号比较数的是符号错误BER按比特流比较数的是比特错误两条路径完全是定义式的、不会混淆。3.2 比特到符号的Gray映射函数实现这里的核心是相邻星座点只差一个比特。QPSK有4个点两个比特的组合刚好4种Gray编码方式完全可以固定下来。function sym bits2symbols(bits) % 输入bits为 1xN 向量N为偶数 % 输出sym为 1x(N/2) 复数向量 bits reshape(bits, 2, []).; % 每行两个bit % 将两个bit映射到十进制0~3 idx bits(:,1)*2 bits(:,2); % 注意列位顺序高位在前 % Gray映射00-01j, 01-11j, 11-0-1j, 10-1-1j % 实际按十进制索引0,1,3,2对应的星座点来组织 map [11j, -11j, -1-1j, 1-1j]; sym map(idx 1) / sqrt(2); % 功率归一化 end这里的最优写法是先映射表再索引避免用if逐点判断尤其在百万比特级别时逐点if会明显拖慢速度。向量化是MATLAB仿真的灵魂任何能在矩阵层面完成的就不要用for循环。这里顺便解释下为什么要除以sqrt(2)map中四个点的模长都是sqrt(2)平均功率是2。除以sqrt(2)后平均功率变为1这样Es1后续噪声方差计算最直接。如果你偷懒不除以那要么噪声方差公式里多补一个2要么曲线就会偏移3dB。这是仿真曲线对不上的第一大隐藏原因。3.3 符号判决与比特恢复边界判断和代码实现判决的本质是看接收符号落在哪个象限但我们要在这个基础上多加一个映射逻辑和一个反归一化运算。function bits symbols2bits(sym) % 输入sym为 1xN 复数向量加噪后或未加噪 % 输出bits为 1x(2*N) 比特向量 sym sym * sqrt(2); % 反归一化回到原星座图规模 realPart real(sym); imagPart imag(sym); bit1 realPart 0; % 实部 0 第一位为1否则为0 bit2 imagPart 0; % 虚部 0 第二位为1否则为0 bits reshape([bit1; bit2], 1, []); end这个判决规则造出的映射是11j判为“1 1”-11j判为“0 1”-1-1j判为“0 0”1-1j判为“1 0”。跟前面bits2symbols的映射表对齐验证一下bits00时idx0symbol11j恢复时bit11, bit21跟原始00正好互补……诶这不是对不上了吗这里就是我特意要讲的坑映射的时候不能随意定义发送端和接收端的编码表必须是一个可逆映射最好直接在函数里保证同一张表。如果发送端定义00映射为11j接收端却把11j判成11你的BER就会直接接近50%或者完全错乱。正确做法要么发送端按“bit1对应实部、bit2对应虚部”来编码接收端也按同样规则解码要么干脆用一个查表函数统一发送和接收的映射逻辑。上面这段接收判决和前述发送映射确实不一致需要统一。我贴一个更鲁棒的做法解码也走星座点最近邻查表。function bits symbols2bits_v2(sym) % 最近邻判决法不依赖象限映射关系直接找最近星座点 const [11j, -11j, -1-1j, 1-1j] / sqrt(2); % 与发送端完全一致 bitTable [0 0; 0 1; 1 1; 1 0]; % 与发送端映射表一一对应 distMat abs(sym. - const); % Nx4 距离矩阵 [~, minIdx] min(distMat, [], 2); bits bitTable(minIdx, :).; bits bits(:).; end这个查表判决法虽然多算了4个距离但逻辑上绝对不会出现发送和接收映射不一致的问题而且扩展到16QAM时只需改星座点和比特表通用性很好。实测在2e6比特量级这段判决的耗时完全可以接受。初学者更推荐先把这个版本跑通再去优化成象限快速判决。3.4 主循环里为什么这样控制噪声方差在归一化星座点平均功率为1的前提下Es1。信噪比关系如下Es/N0 2 * Eb/N0因为一个符号2比特复噪声n nI j*nQnI和nQ各自方差为N0/2因此噪声方差 N0/2 Es / (2Es/N0) 1 / (2EsN0_lin)所以代码里noiseVar 1/(2*EsN0_lin)是这么来的。如果你用的是平均功率为2的星座点那么这个公式就会变成noiseVar 2/(2*EsN0_lin) 1/EsN0_lin。可见归一化改变了噪声方差的表达但不会改变最终BER。所以关键是整套代码前后统一最稳妥的方案就是归一化。awgn函数的参数里有个measured意思是先测一下信号功率再做加噪处理。这种便利背后隐藏了信号功率估计的环节如果你发送的符号功率本身有偏置awgn会帮你“纠正”掉但这反而掩盖了你星座映射里的功率问题。所以我建议你在完全理解了手动加噪以后再考虑用awgn函数来做交叉验证。实际项目里我两种方式都用手动加噪出主结果awgn做一次复核两边一致才认为代码没问题。4. 理论曲线计算与误码性能分析仿真和理论如何严丝合缝4.1 误符号率和误比特率的理论表达式推导与落地QPSK的理论误符号率公式推导分两步先看单个分量I路或Q路的错误概率再把两路合并。单路二进制判决的错误概率是Q(sqrt(2*Eb/N0))这一项其实就是BPSK的误比特率。QPSK两个正交分量各自独立承载一个比特所以完整符号的错误概率用概率论展开是P_symbol 1 - (1 - P_I) * (1 - P_Q)因为P_I P_Q Q(sqrt(2*Eb/N0))所以P_symbol 2Q(sqrt(2Eb/N0)) - Q^2(sqrt(2Eb/N0))。手册上经常把最后这个Q^2项省略写成P_symbol ≈ 2Q(sqrt(2Eb/N0))。为什么可以省略因为Q函数在高信噪比时衰减极快平方项就更是微小量工程估算时省略完全没问题。但做仿真对比时我强烈建议把Q^2项保留下来因为在Eb/N0 0dB左右的低信噪比区域Q值大约0.16平方项接近0.026相对于0.32的主项不算是可以忽略的量它会让你理论曲线在左端稍微往下压一点更贴近仿真点。理论误比特率的公式对应BER Q(sqrt(2*Eb/N0))。这个式子在Gray映射前提下是对的因为绝大多数符号错误是单比特错误。严格来说精确的BER表达式是(1/2)P_symbol前提是任意符号错误时比特错误数期望为1但直接用Q(sqrt(2Eb/N0))和仿真是否符合需要看具体条件。在很多教材中QPSK的理论BER就是BPSK的BER这是因为Gray映射下QPSK等效为两个独立的BPSK在I/Q两路并行传输每一路都是BPSK误比特率自然等于BPSK的误比特率。这个解释更直观QPSK就是两个BPSK叠加Gray映射本质上没用“相邻差1比特”来达到这一点不对这里我修正一下表述——即便不用Gray映射QPSK的I/Q两路本来就是两个独立的BPSK每路各自解调所以BER本来就等于BPSK的误比特率。Gray映射的意义在于让“符号错误”尽量等价于“单比特错误”但这与I/Q独立并不矛盾。准确说法是在逐比特硬判决的框架下I路和Q路各自是BPSK所以QPSK的BER天然就是Q(sqrt(2Eb/N0))跟映射方式无关。SER则跟映射方式无关只取决于星座点之间的距离和误判概率但“SER对BER的折算比例”跟映射方式直接相关。自然映射下相邻星座点差2比特符号错误导致比特错误数期望高SER可能还是2Q - Q^2但BER会接近SER而不是SER/2。所以你看到有的文章说“QPSK的BERSER/2”那是建立在Gray映射且近似忽略双比特错误的条件下说的严格意义上不能当定论用。4.2 仿真结果与理论值的验证方法画图的时候建议直接在同一张图上叠加仿真散点、理论曲线和参考标记线。出现偏差时不要只盯着曲线看而要看相对位置和偏移方向。比如仿真值整体比理论值高但形状一致那大概率是噪声方差算错了差了3dB左右就找找功率归一化如果只有高信噪比区域发散那大概率是统计样本量不够增加符号数即可如果低信噪比也有明显偏差考虑是不是映射表和解码表对不上。我自己调试时的顺序是这样的先在Eb/N0 3dB处跑一次2e6比特看BER大概在2e-2级别和理论值差多少再在Eb/N0 6dB处跑一次看是否到1e-3级别。这两个点的数量级如果正确后面的曲线基本不会跑偏。如果你一次跑全范围低信噪比和高信噪比各点的统计精度差异容易被曲线形状掩盖反而不容易定位问题。4.3 为什么高信噪比下仿真曲线会抖动或断掉高信噪比区域的BER很低比如在Eb/N012dB时理论BER约8e-6。这意味着平均每12.5万个比特才有1个错误。如果你总共只跑了2e6比特那么大约只能采到16个错误相对误差就会很大每次运行结果不一样的抖动即来源于此。更极端地在Eb/N014dB时理论BER低于1e-62e6比特几乎采不到错误仿真曲线就只能画到断点为止。解决方案有两种。第一种是固定错误数停止法设定一个目标错误计数比如至少100个达到以后平均并计算BER这个方法代码上稍复杂但效率和精度都好。第二种是分信噪比区间设定样本量低信噪比区用少量样本高信噪比区用大量样本比如把numBits从2e6提到2e7甚至2e8。你把握一个原则任何一个信噪比点上的错误计数低于100就不足以在log坐标上绘制一个可信的BER点。如果想验证14dB的理论值8e-8需要至少1e9比特仿真这就要考虑运行时间和内存优化——这也是为什么很多专业仿真会分段并行或者用C/MEX来提速。5. 仿真过程中的常见问题与避坑经验实录5.1 噪声维度缺失只加实数噪声现象仿真BER比理论值高很多而且你检查代码发现看起来一切“正常”。最常见的原因是噪声只加了实部% 错误写法 noise sqrt(N0) * randn(1, numSymbols); rxSymbols txSymbols noise; % 正确写法 noise sqrt(N0/2) * (randn(1, numSymbols) 1j*randn(1, numSymbols));错误写法的本质是你在复平面上只沿实轴方向加噪声虚轴方向完全没有扰动。QPSK信号同时用实部和虚部承载信息虚部不受噪声影响等效于只有一维受到干扰。结果就是仿真性能看起来比理论性能好而不是差等等我之前说“比理论值高”这里是矛盾的因为噪声维度缺失会让判决更容易正确BER应该更低才对。实际中确实如此只加实噪声时虚部的边界完全不会被穿越Q路判决永远对。整个符号的错误概率来自实部而虚部无误所以BER会显著低于理论值画在log图上是在理论线下方。这个方向性很重要不少人在排查问题时搞反了方向越改越错。记住噪声少加了一个维度仿真性能会“好得离谱”噪声方差多加了一倍性能会“差得离谱”。敏感方向一上来就错了后面越查越乱。5.2 信噪比公式里的3dB陷阱Eb/N0和Es/N0之间差一个因子2也就是3dB。QPSK里一个符号2比特所以Es/N0 Eb/N0 3dB。如果你在设置awgn参数时直接用了Eb/N0的数值而没有换算仿真性能会比理论值差3dB左右画出来的曲线像整体右移了。这个坑出现频率极高而且在低信噪比数值比较小的时候曲线形状看起来还很像容易让人误以为只是“仿真大差不差”。我习惯的做法是代码里明确写一行注释% EbN0_dB - EsN0_dB 转换QPSK一个符号2比特 EsN0_dB EbN0_dB 10*log10(2);然后后续所有用到信噪比的地方都用EsN0_dB这样在代码里可搜索、可审计、可复核。靠脑子记是最容易出错的。5.3 随机数种子与可复现性仿真的随机性来自randi和randn。如果你不做任何种子设置每次运行的结果之间会有随机抖动。做调试的时候这种抖动会让你很难判断改动代码前后到底是“真的变好了”还是“随机波动”。建议在主程序开头加rng(42); % 固定随机数种子便于结果复现用BTW(by the way)的经验项目发布或论文提交前一定要固定种子重新跑一遍全流程确保你贴在论文里的数据点是可以复现的否则审稿人或者导师让你重跑一次你可能会得到一组不太相同的数据那种感觉非常难受。我在实际项目里种子固定为一个特定数字后跑出的所有结果都存在独立目录里文件名带种子值和日期这样对比实验一目了然。5.4 大批量仿真时的内存与时间优化当numBits达到1e8量级时一个复数双精度数组占用的内存是16字节乘以8e7因为复数是两个double分别8字节即1.28GB这足以让很多电脑卡顿。如果整条链路上保留txBits、txSymbols、noise、rxSymbols、rxBits等多个同量级数组内存轻松上几个GB仿真会变得异常缓慢。优化方案有三种分块处理将数据切成多个块每块独立加噪和判決按块统计错误数最后汇总。统计错误本身满足可加性分块不影响结果还可以顺便在每块结束时检查当前信噪比点的错误数是否已足够不够则继续加块够则提前跳出。用单精度MATLAB里很多运算可以转成single类型内存减半代价是精度略微损失对BER统计而言完全够用。把最耗时的判决部分写成MEX函数或者用并行循环parfor替代for。如果只是课程设计分块优化就足够了MEX属于锦上添花。5.5 眼图、星座图和理论曲线的联合观察有些人拿到代码第一件事就是跑BER曲线但忽略了中间的观察环节。我建议你在加噪声后、判决前画出当前信噪比点下的星座图比如在Eb/N08dB下接收符号的散布情况。你会看到四个簇每簇围绕理想星座点呈圆形散布簇半径随噪声功率增大而增大。这张图能帮你一眼判断噪声方差是否符合预期如果簇半径明显大于理论值你的噪声方差大概率翻倍了如果簇中心偏移说明星座映射或归一化有错。如果你想看更细节的码间串扰和同步质量可以增加眼图仿真但QPSK基带仿真一般不直接画眼图眼图更多用于脉冲成型和信道均衡的环节。真有需求的话在用rcosdesign或升余弦滤波器做脉冲成型后将同相和正交分量分别plot出来观察“眼睛睁开度”这更接近实际系统的验收标准。但这些都是后续扩展方向本题设计中没有包含成形滤波器至少先在星座图上停留一下再画BER曲线你的调试成本会大幅下降。5.6 一个容易忽略的边界符号数必须是偶数QPSK每两个比特映射一个符号所以总比特数必须是偶数。如果你定义numBits 1000001reshape就会报错。更隐蔽的问题是你设置了numBits后没有检查奇偶性然后就得到“数组维度不匹配”的报错。处理办法很简单numBits numBits - mod(numBits, 2); % 确保偶数这个看起来是小问题但几乎所有第一次跑这个程序的人都会遇到早点写进代码里能少一个让调试心态爆炸的点。5.7 数据类型的隐性Bug复数和实数的加法MATLAB中复数和实数相加没问题但如果你不小心把星座点定义成整数形式的11j而噪声是double类型结果会变成complex double没问题。真正容易出问题的地方是逻辑比较txSymbols ~ rxSymbols在浮点数下是否稳定。由于判决后我直接让rxSymbols等于标准星座点查表得到所以比较时两者在数值上是完全一样的不会出现“明明应该是同一个点但因为浮点误差被判不等”的情况。如果你用连续值直接比较比如比较加噪后的rxSymbols和txSymbols是否相等那几乎每个符号都会因为噪声而不等统计出来SER接近1这完全是错误统计口径。判决后的量化这一步骤绝不能省。6. 后续可扩展的方向这套链路框架的延伸价值这个项目做完之后框架并不是一次性的。我建议你保留这个清晰的“比特-符号-加噪-判决-统计”模块化结构因为几乎所有数字通信仿真都能套这个壳子只是换内部实现。接下来你可以做这些扩展脉冲成型与匹配滤波在符号映射后插入上采样和升余弦滤波器在接收端加匹配滤波和下采样观察滚降因子对误码率的影响。你会发现当没有符号间干扰时BER曲线基本不变但加上带限信道后ISI会让地板效应出现。载波频偏和相偏在信道模型中引入固定相偏或者频率偏移观察星座图旋转和BER恶化程度这个扩展对理解实际系统同步的重要性很有价值。加信道编码把QPSK当作调制层外面套卷积码或LDPC码对比编码增益。此时统计BER的关注点要换成“译码后BER”同时要考虑编码速率对Eb/N0定义的影响这个扩展做完你的仿真能力基本覆盖了通信系统物理层的核心技术点。换成16QAM把星座映射表换成16个点比特表换成4比特组误码率理论公式换成16QAM的对应表达式其他部分保持不变这套代码可以无缝切换。我个人在实际编写和调试这类仿真程序时最大的体会是仿真程序本身的编写难度往往不到整体工作量的四成真正的难点在于建立起一套自洽的参数体系和一个能快速定位问题的调试路径。很多同学卡在“曲线对不上”的泥潭里并不是数学不行而是缺乏把Eb/N0换算、功率归一化、星座映射这些细节从头到尾理清楚的位置感。把这一张关系网打通之后你会发现QPSK这个例子所有的知识点都牢牢长在了一个可复现、可验证、可扩展的框架上以后再碰到更复杂的系统换的只是星座图、信道模型和判决方式这几个零件而已。
返回列表