ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

16PSK为何拼不过16QAM:MATLAB信道容量曲线解析

16PSK为何拼不过16QAM:MATLAB信道容量曲线解析

简介:围绕QPSK、8PSK、16PSK与16QAM四种调制方式在高斯信道下的信道容量曲线,这份文档面向通信工程、电子信息类专业学生及数字通信课程学习者,帮助梳理二维M元信号的容量推导与仿真实现。正文从二维矢量表示、归一化正交函数组合和复高斯噪声模型出发,结合概率密度函数、信号功率与噪声功率关系,说明信道容量随信噪比及Eb/N0变化的计算思路,并给出星座图与MATLAB代码片段,便于对照理解QPSK、8PSK、16PSK和16QAM的差异。资源包共1个doc文件,约345KB,以推导说明和代码记录为主,适合课程作业、复习笔记或仿真前参考。已有1353人学习,可作为数字通信调制与信道容量知识点的补充材料。

1. 四种调制、一张图:16PSK 为什么拼不过 16QAM

把 QPSK、8PSK、16PSK、16QAM 四条信道容量曲线叠在同一张 Eb/N0 坐标系上看,最先跳出来的往往不是「谁容量高」,而是一个让人愣一下的现象:16PSK 和 16QAM 都搬 4 个比特、星座点都是 16 个,可 16QAM 那条线整整往左压了一大截,同样的每比特信噪比能多送将近半个比特。更别扭的是 16PSK 在高信噪比端几乎贴着 4 bit 的天花板往上爬,爬得极其吃力,而 16QAM 早早拐平。这份资料做的事,就是用 MATLAB 把这个「为什么」算出来:从二维复信号集合的互信息出发,推出 M 元调制在高斯信道下的容量表达式,再用蒙特卡洛采样把没有闭式解的积分项硬算出来,最后把四条曲线画进同一张图。

2. 容量公式的推导:为什么一半能算,一半只能采样

2.1 从互信息到微分熵之差:C = h(Y) − h(Y|X)

信道容量的定义是 C = max I(X;Y),对 M 元等概率输入的星座,输入分布已经被「等概率」这个假设钉死了,剩下的工作就是把 I(X;Y) 算出来。第一步是把互信息换成微分熵的差:

I(X;Y) = h(Y) − h(Y|X) = h(Y) − h(Z)

这个等号成立的原因很朴素:接收信号 Y = X + Z,一旦 X 已知,Y 身上剩下的不确定性就只剩下噪声 Z 了,与发的是哪个星座点无关。所以条件熵 h(Y|X) 直接退化成噪声熵 h(Z),是一个和星座几何形状完全无关的量。这一步是整个推导里最省力、也最关键的一刀——它把一个需要遍历所有 X、Y 联合分布的积分,砍成了「输出熵减去噪声熵」两项。

真正的麻烦全在 h(Y) 上。噪声是复高斯的,熵有闭式解;但输出 Y 是 M 个复高斯的混合,这个混合分布的熵没有初等表达式,只能数值求。这就是为什么一条看起来简单的容量曲线,最后非得写一段采样程序不可。很多教材在讲香农公式 C = log2(1 + SNR) 时让人产生一种错觉,仿佛容量随手一算就有,其实那个式子成立的前提是输入本身就是复高斯——输入连续、能量受限、没有任何星座约束。一旦输入被限制成「从 16 个固定点里等概率挑一个」,Y 就不再是高斯,混合分布的熵严格小于同方差高斯的熵,这个差值就是星座约束带来的容量损失。

换句话说,香农公式给的是天花板,M 元调制曲线是这块天花板下面的一条实线,两者之间的空隙全部由「星座长什么样」决定。这也是为什么后面必须老老实实算数值,而不能拿 0.5·log2(1+SNR) 去糊弄——那条式子画出来的曲线四条完全重合,一点信息量都没有。

2.2 复高斯的微分熵:log2(πeN0) 里的 π 和 e 是怎么来的

噪声熵 h(Z) 是唯一能写成闭式的一项,但它的系数极容易抄错。推导路径是:复高斯噪声 Z = Z_I + jZ_Q,实部虚部独立同分布,每一维方差是 N0/2,所以复噪声的总方差是 Var(Z) = N0。一个实高斯变量 X ~ N(0, σ²) 的微分熵是 0.5·log2(2πeσ²),两个独立的实高斯加起来(也就是复高斯的熵),熵等于各自熵之和:

h(Z) = 0.5·log2(2πe·N0/2) × 2 = log2(πeN0)

这里的 2 倍关系是无数人翻车的地方。如果直接把 σ² = N0/2 代进 log2(2πeσ²),会得到 log2(πeN0),看起来对;但如果误以为复噪声方差是 N0/2 而不是 N0,就会算成 log2(πeN0/2),整条曲线整体右移 1 bit 左右,图上一眼看不出来但数值全错。代码里对应的写法是-log2(pi*exp(1)*No),其中No就是复噪声方差 N0;与之配套,噪声采样必须写成sqrt(No/2)*(randn + 1i*randn),每一维标准差是 sqrt(N0/2),两维合起来方差才是 N0。这两处一旦不匹配,曲线形状还是那个形状,位置却整体飘走。

顺带说一句,复高斯概率密度函数的归一化因子是 1/(πN0),而不是实高斯那样的 1/sqrt(2πσ²)。原因还是同一个:复密度是在实平面上对两个维度联合归一化,面积元是 dx·dy,所以分母里的 2πσ² 变成了 πN0。这个因子在式子里出现两次——一次在噪声熵,一次在 p(y) 的表达式里——两次都写对才能互相抵消出干净的结果。

2.3 p(y) 是 M 项高斯混合:式(2)(3) 的物理含义

输出端的概率密度 p(y) 是把所有可能的发送信号边缘化之后的结果。发 s_m 时,接收信号服从均值 s_m、方差 N0 的复高斯分布,写成

p(y|s_m) = (1/(πN0))·exp(−|y − s_m|²/N0)

输入等概率,所以 p(y) 就是这 M 个复高斯的算术平均,每项权重 1/M。这个式子的几何含义值得停下来看一眼:在复平面上,p(y) 是 M 个「钟形山包」叠起来的地形图,山包中心就在星座点上。信噪比高的时候山包又尖又窄、彼此分得很开,p(y) 在任何一个 y 处基本只由最近的那个星座点贡献;信噪比低的时候山包摊得很平、互相淹没,p(y) 近似变成一个大的高斯包——这也解释了为什么低信噪比下所有 M 元调制的容量曲线都收敛到同一个地方,星座形状被噪声抹平了。

把 h(Y) 展开成 −∫p(y)·log2 p(y) dy,再把 h(Z) 的闭式代进去,就得到资料里的式(4):

C = −E[log2 p(Y)] − log2(πeN0)

这个式子的结构非常干净:第一项是「输出有多随机」,第二项是「噪声本身有多随机」,两者相减剩下的才是「信号真正传过去的信息量」。第一项里那个期望没有闭式解,必须靠采样;第二项是常数,直接算。整份 MATLAB 程序,本质上就是把第一项用样本均值顶上去,剩下的全是工程细节。

3. 蒙特卡洛替代积分:代码里那几个 mean 和 log2 到底在算什么

3.1 式(4) 的离散化:样本均值代替数学期望

−E[log2 p(Y)] 是一个关于随机变量 Y 的期望,而 Y 的分布 p(y) 正是我们要算的东西,所以不存在「解析求期望」的可能。蒙特卡洛的思路是绕过解析:既然 Y 的生成过程是清楚的(先均匀抽一个星座点,再加复高斯噪声),那就直接按这个过程造样本,用样本均值逼近期望。大数定律保证,当样本数 L 足够大时

−(1/L)·Σ log2 p(y_l) → −E[log2 p(Y)]

收敛速度是 1/sqrt(L),也就是说样本数翻 4 倍,曲线抖动能减半。这一步替换是整个程序合法的根基:采样必须严格服从「等概率抽星座点 + 加对应方差复高斯噪声」这个真实生成过程,任何一处偏离(比如抽星座点时用 rand 忘了 floor、噪声方差写成 N0 而不是 N0/2)都会让样本均值收敛到一个错误的期望上去,而且这种错误不会因为加大样本数而消失,只会更稳定地错。

这里还有个容易忽略的细节:p(y_l) 的计算本身要对 M 个星座点求和,复杂度是 O(L·M)。所以总开销是样本数乘以星座规模,16QAM 的每一项计算量是 QPSK 的 4 倍。当年用双重循环硬算,66666 个样本跑四组调制,在普通机器上要等一会儿;放到今天可以直接向量化成矩阵运算,L×M 的距离矩阵一次算完。

3.2 核心循环拆解:从星座点生成到 Capacity 数组

把原脚本的核心逻辑整理成等价写法,去掉重复分支,结构会更清楚:

snr = -10:1:35; % 扫描的 Es/N0,单位 dB PsPn = 10.^(0.1*snr); % dB 转线性,得到 Es/N0 的比值 sample = 66666; % 蒙特卡洛样本数 L for k = 2:4 M = 2.^k; switch k % 三种 PSK 星座,全部落在单位圆上 case 2, signal_space = [1+1i, -1+1i, -1-1i, 1-1i]; % QPSK,Es = 2 case 3, signal_space = exp(1i*(0:7)*pi/4); % 8PSK,Es = 1 case 4, signal_space = exp(1i*(0:15)*pi/8); % 16PSK,Es = 1 end Es = mean(abs(signal_space).^2); % 星座平均能量 for i = 1:length(snr) No = Es / PsPn(i); % 复噪声方差 N0 = Es / (Es/N0) idx = floor(rand(1,sample)*M) + 1; % 等概率抽星座点下标 1..M x = signal_space(idx); % 发送符号 y = x + sqrt(No/2)*(randn(1,sample) + 1i*randn(1,sample)); % 过信道 log2py = zeros(1,sample); % 每次循环必须重置,否则残留旧值 for j = 1:sample acc = 0; for b = 1:M acc = acc + exp(-abs(y(j)-signal_space(b)).^2 / No); % 混合分布分子 end log2py(j) = log2(acc / (M*pi*No)); % 除以 M 和 pi*N0,得到 log2 p(y) end Capacity(i) = -mean(log2py) - log2(pi*exp(1)*No); end EbNo = 10*log10(PsPn ./ Capacity); % 横轴换算到每比特信噪比 plot(EbNo, Capacity); hold on; end

逐行对一遍参数含义,每一处都对应式子里的一项。PsPn是线性域的 Es/N0,由 dB 值 10.^(0.1snr) 得到;No是复噪声方差,由「平均符号能量 ÷ 每符号信噪比」算出,这一步隐含了「功率比等于能量比」的换算,后面会专门讲。idx用 floor(randM)+1 得到 1 到 M 的均匀整数,保证星座点等概率。噪声那一行拆成两维各自乘 sqrt(No/2),合起来复方差才是 No。

内层求和acc对应 p(y) 表达式里 M 个复高斯的累加,注意它算的是未归一化的分子;log2(acc/(M*pi*No))才把 1/M 的等概率权重和 1/(πN0) 的归一化因子一起除进去,得到真正的 log2 p(y)。最后一行把两项拼起来:-mean(log2py)是第一项的样本均值,-log2(pi*exp(1)*No)是噪声熵那一项。

值得单独指出的是EbNo = 10*log10(PsPn ./ Capacity)。这里的除法是逐元素的,也就是每个信噪比点用自己的容量值去除,把「每符号能量」摊到「每比特」上。如果写成 PsPn/Capacity 的平均值或者别的形式,曲线形状就完全不对了。

3.3 采样点数与随机性:66666 个样本够不够

脚本里 sample 取 66666,这个数字没有任何数学含义,就是一个「看着够大」的经验值。真正决定曲线平滑程度的是估计量的方差:样本均值的标准差约为 std(log2 p(Y))/sqrt(L)。log2 p(Y) 的取值在几个 bit 到十几个 bit 之间浮动,按标准差 20 bit 粗估,L = 66666 时单点的标准误约 0.08 bit,画在图上是肉眼可见的轻微毛刺。

更麻烦的是,这个方差不均匀。低信噪比区域 p(y) 接近单峰高斯,log2 p(Y) 波动小,曲线很稳;高信噪比区域 p(y) 变成 M 个尖锐峰,错误的样本会落进「离所有星座点都远」的深谷,log2 p(Y) 出现大幅负值,方差被这几个离群样本拉得很高。所以你会发现曲线右上角(高 Eb/N0 端)抖得比左下角厉害,加大样本数时也总是右上角先变平滑。

工程上的处理办法有三个:一是直接把 L 提到 1e6 量级,代价是运行时间线性增长;二是固定随机种子(rng(0) 之类),至少保证每次跑出来的图一样,避免「改了个无关参数图就变了」的玄学;三是对同一信噪比点重复跑若干次取平均,等效于把 L 乘上重复次数,还能顺便估计出误差棒。血泪经验是:任何一次「曲线突然跳了一下」的排查,第一步都应该先固定种子重跑一遍,确认不是随机性在作祟。

4. Es、N0、Eb/N0:三个能量量纲怎么串成一条横轴

4.1 星座点写成复数之后,平均符号能量 Es 怎么算

星座点用复数表示后,每个符号的能量就是它到原点的距离平方 |s_m|²,平均符号能量 Es 是这 M 个值再取平均。四种调制的数值差别很大:

调制方式星座点坐标单点能量Es
QPSK(±1, ±1)22
8PSK单位圆上 8 等分11
16PSK单位圆上 16 等分11
16QAM(±1, ±3) 全组合2、10、1810

这张表解释了两个常见困惑。第一,为什么 QPSK 的 Es 是 2 而不是 1——因为它的坐标取的是 (±1,±1),每维幅度 1,合起来模方就是 2;如果把 QPSK 改成 (±1/√2, ±1/√2),Es 就变成 1,但整条曲线不会变。第二,16QAM 的 Es = 10,比 PSK 大一个数量级,很多人第一次看到会以为能量没归一化、结果不可比。其实恰恰相反,Es 的绝对大小对最终的容量曲线毫无影响。

这一点值得展开说。假设把整个星座按比例放大 a 倍(s_m → a·s_m),同时把噪声方差放大 a² 倍(N0 → a²N0),那么接收信号 y 也同比放大 a 倍,p(y) 的密度值缩小 a² 倍,于是 log2 p(y) 减少 2·log2 a;第一项 −E[log2 p(Y)] 相应增加 2·log2 a,而第二项 −log2(πeN0) 减少 2·log2 a,两项恰好互相抵消,容量 C 不变。而横轴的 Es/N0 = a²Es/(a²N0) 也不变。结论是:整条曲线在 (Eb/N0, C) 平面上对星座的整体缩放完全不敏感,敏感的只有星座的几何形状——点与点之间的相对距离分布。16QAM 曲线比 16PSK 靠左,靠的不是能量大小,而是点的摆法。

4.2 N0 = Es/(Ps/Pn):功率比凭什么等于能量比

脚本里No = Es/PsPn(i)这一行,注释写的是「能量比等于功率比」。这句话成立的前提是符号周期相同。功率是能量除以时间,P = E/T,当所有调制方式用同一个符号周期 T 时,功率之比就等于能量之比,T 被约掉了。所以在离散基带模型里(等价于 T = 1),Ps/Pn 和 Es/N0 是同一个数,代码可以直接把 10.^(0.1*snr) 当成能量比来用。

这个前提在实际系统里未必成立——如果不同调制方式的符号率不同,就必须显式地把 T 带进去,否则横轴的物理意义会错。但在这种「同一符号周期下比较不同星座」的分析里,这个简化是标准做法。

还要再强调一次 N0 的身份:它是复噪声的方差,不是单维方差。信噪比 SNR 通常定义成信号功率与总噪声功率之比,而总噪声功率对应的就是复方差 N0。如果有人在采样噪声时写成 sqrt(No)(randn + 1irandn),实际噪声功率变成了 2N0,曲线会整体左移 3 dB,形状还很像对的,这是最难查的一类错误。

4.3 从 Es/N0 到 Eb/N0:为什么横轴一定要除以 C

扫描变量本来是 Es/N0,但画图时横轴换成了 Eb/N0,代码是EbNo = 10*log10(PsPn ./ Capacity)。这个换算的动机是公平性:QPSK 一个符号搬 2 比特,16QAM 一个符号搬 4 比特,同样一份符号能量,摊到每个比特上的能量不一样。用 Es/N0 当横轴,高阶调制天然吃亏(因为它的能量要分给更多比特),比较就失去了意义。

每符号搬 C 个比特(这里的 C 就是容量,在理想编码下每符号可靠传输的比特数),所以每比特能量 Eb = Es/C,于是 Eb/N0 = (Es/N0)/C。注意分母是 C 而不是 log2 M——用容量而不是星座规模,是因为这条曲线描述的是「可靠传输」的极限,某些信噪比下 16QAM 根本达不到 4 bit,用 log2 M 去摊会低估每比特的真实能量代价。

这个换算带来一个漂亮的副产品:当 C → 0 时,Eb/N0 → ln2 ≈ −1.59 dB,而且与 M 无关。也就是说,无论用哪种调制,只要肯把频谱效率压到接近零,总能逼近同一个极限。这条 −1.59 dB 的水平线是所有容量图上的隐形基准线,也是验证程序算得对不对的第一道关卡。反过来,高信噪比端 C 饱和在 log2 M,曲线变成水平,这时候 Eb/N0 继续增大也没用——这是星座规模给出的硬上限。

5. 常见问题排查:这类仿真最容易翻车的几个地方

5.1 星座坐标手写角度:一行写错,整条曲线悄悄变形

现象:16PSK 的曲线在低信噪比段比预期低,但高信噪比段又正常,查公式查不出来。原因:16PSK 的 16 个点如果一个个手写 complex(cos θ, sin θ),角度序列极容易写错。原始脚本里有一个点写的是 cos(7π/8) 配 sin(−7π/8),数值上它等价于 9π/8(因为 cos(9π/8) = cos(7π/8)、sin(9π/8) = −sin(7π/8)),但混用负角度会让后续维护的人反复怀疑。更危险的是另一种错法:某个点角度差了一个 π/8,星座变成非均匀分布,等概率假设就不成立了,容量会偏小但不会报错。

解决:不要手写角度,用生成式signal_space = exp(1i*(0:M-1)*2*pi/M)一次搞定,然后加两条断言自检——所有点的模应该等于 1(all(abs(abs(signal_space)-1) < 1e-12)),相邻点的相位差应该处处相等。

5.2 工作区变量残留:第二次运行时曲线变了

现象:脚本连续跑第二次,容量曲线和第一次不一样;或者把 16QAM 那段挪到前面之后,PSK 的结果全乱了。原因:MATLAB 的循环不会自动清空数组。signal_space在 k = 2 时只有 4 个元素,k = 3 时被扩展到 8 个,扩出来的位置是零;如果某次执行顺序变化,数组里会残留上一轮的旧星座点,Es = sum(abs(signal_space).^2)/M里的 M 却是新的,能量就算错了。同理,log2py如果在循环外没有重置,某些下标不会被覆盖,mean会把上一轮的值混进来。

解决:每个循环体内第一件事就是显式初始化signal_space = zeros(1,M)和log2py = zeros(1,sample),不要依赖「反正每次都会赋值」。这种坑的隐蔽之处在于它不影响首次运行,只在反复调试时咬人。

5.3 高信噪比下 exp 下溢:log2(0) 变成 −Inf

现象:曲线右端突然掉下去,或者 Capacity 出现 −Inf / NaN。原因:内层求和里 exp(−|y − s_b|²/N0),当 N0 很小(高信噪比)且样本恰好离所有星座点都远时,指数项全部下溢成 0,acc = 0,log2(0) 直接炸掉。这个现象恰恰集中在曲线最右端,也就是最需要看清饱和行为的地方。

解决:用 log-sum-exp 技巧,先减去最大值再取指数:

d2 = abs(y(j) - signal_space).^2 / No; % 1×M 的距离平方 m = min(d2); % 数值稳定的平移量 log2py(j) = log2(sum(exp(-(d2 - m)))) - m/log(2) - log2(M*pi*No);

平移量 m 只影响中间计算的尺度,最后再补回来,数学上完全等价,但把指数项的动态范围压进了安全区。这种改写不改变结果,只改变数值稳定性,属于典型的「先能跑,再跑得稳」。

5.4 横轴不换算 Eb/N0:四条曲线挤成一团

现象:图画出来四条线几乎重合,看不出 16QAM 的优势,怀疑程序写错了。原因:直接拿 Es/N0 或 SNR 当横轴。不同调制的 Es 不同(QPSK 是 2、PSK 是 1、16QAM 是 10),每符号比特数也不同,用符号能量比较等于让高阶调制背着一个常数劣势。

解决:老老实实做EbNo = 10*log10(PsPn./Capacity),逐点除以容量。这一步做完,四条曲线会立刻在图上拉开,16QAM 明显位于最左上,16PSK 拖在右下,QPSK 在低容量段反而占优——这个排序才是物理上正确的。

5.5 样本数不足:曲线抖动被误当成物理现象

现象:曲线有锯齿,加大信噪比扫描步长后锯齿位置变了,看起来像是某种「容量振荡」。原因:蒙特卡洛方差,而且如前面所说,高信噪比端方差更大。解决:先固定随机种子确认抖动是否可复现,再把样本数提高一个数量级观察曲线是否收敛;如果只是想让图好看,也可以对相邻信噪比点做轻微平滑,但要在图注里说清楚这是后处理,不是物理效应。

6. 设计取舍:容量曲线背后的那 4 dB 是怎么来的

6.1 最小距离视角:恒包络的代价

16PSK 和 16QAM 的差距,可以用一个非常朴素的角度解释:点与点之间的最小距离。16PSK 所有的点都挤在半径 1 的圆上,相邻点夹角 22.5°,最小欧氏距离是 2·sin(π/16) ≈ 0.391,而 Es = 1,所以归一化最小距离 d/√Es ≈ 0.391。16QAM 的点摆在方格上,最小距离是 2,Es = 10,归一化距离 2/√10 ≈ 0.632。两者相差 20·log10(0.632/0.391) ≈ 4.2 dB——这几乎就是图上 16QAM 曲线相对 16PSK 左移的距离。

这就是「恒包络」的代价。PSK 把点锁在圆上换来的是功率放大器可以工作在饱和区、不需要昂贵的线性化,代价是高阶时点挤在一起,距离急剧缩小。M 越大这个惩罚越重:8PSK 归一化距离 2·sin(π/8) ≈ 0.765,16PSK 掉到 0.391,几乎腰斩。所以实际系统里 16PSK 基本没人用,16QAM 却遍地都是,中间的取舍从来不是「哪个容量高」,而是「省下来的那 4 dB 值不值得换一个线性功放」。

6.2 三个可以自查的锚点

拿到任何一条 M 元调制容量曲线,有三个地方可以立刻验证对错。第一,C → 0 时所有曲线必须收敛到 Eb/N0 = ln2 ≈ −1.59 dB,如果某条线在低端跑到了 −3 dB 或者 0 dB,说明 N0 的定义或者 Es 的归一化错了。第二,高信噪比端每条曲线必须水平饱和在 log2 M,QPSK 卡在 2、8PSK 卡在 3、16PSK 和 16QAM 卡在 4,如果 16QAM 跑到 4.5 去了,一定是容量公式里的常数项丢了。第三,同一族调制内部,曲线间距应该随 M 增大而拉大——8PSK 相对 QPSK 的损失约 1.6 dB,16PSK 相对 8PSK 的损失接近 4 dB,如果间距反而变小,多半是星座没归一化到同一个 Es。

6.3 再往前一步:等概率输入并不是最优

这套推导从头到尾假设了「M 个星座点等概率」,这个假设让 p(y) 变成简单的算术平均,也让采样程序写起来很直接。但它并不是最优的:M 元输入下的最优分布是 Maxwell-Boltzmann 分布,外圈的点出现概率低一些、内圈的点高一些,能再挤出 0.5 到 1 dB 的成形增益(shaping gain)。这套资料算的是「等概率星座」的容量,也就是实际系统不开成形时能拿到的水平,作为工程基线恰到好处——想再加码就得动分布了。

我自己的习惯是,每次看到一条漂亮的容量曲线,先不急着看图,而是回到代码里把那三个锚点挨个跑一遍,尤其是 −1.59 dB 那条线。早年做课程设计的时候,因为噪声方差少乘了一个 2,整张图平移了 3 dB,我还对着「16QAM 居然比理论值好」的结果高兴了半天,直到导师随口问了句低端截距是多少才崩掉。从那以后我每次写这类仿真都强制走一遍锚点校验,图可以难看,数不能飘。希望帮到你。

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

返回列表