从正交基展开到星座图:通信信号空间几何化实战 做通信、信号处理这行的早年间大概率都被“信号空间”这套东西卡过一次。我第一次啃正交基展开时公式会推、习题会做但脑子里没有画面感——把一段波形写成几根坐标轴上的分量这个动作到底意味着什么直到自己动手做一段短距链路的仿真才真正尝到这套几何化的甜头当我把十几种调制方式全部画成平面上的点阵之后误码率的差异、星座设计的取舍、为什么 BPSK 和 QPSK 的抗噪表现是同一档全都变成了一眼能看明白的事。这次我把整条链路重新走了一遍从正交基函数的选取、信号展开系数的计算到能量和距离的几何含义最后落到调制信号在几何空间中的表示也就是星座图。公式我按手算能复现的标准推了一遍也配了能直接跑的代码从理论到手算再到仿真验算一层层往下压。不管你是正在学通信原理、信号与系统的同学还是工作几年想把底层直觉补回来的工程师这套流程都能照着走一遍走完你对“波形”和“向量”的理解会换一副眼镜。1. 为什么要把波形塞进几何空间1.1 从一段波形到一组坐标的那次思维跳跃现实里的信号是时间的函数连续、无限、不可数。你没法用笔把一整段 s(t) 画成表格存下来更没法直接拿两个函数做“减法比大小”。但工程师总得比较两个波形像不像、差多远尤其是接收端拿到一个被噪声污染过的波形时它要判断这到底是发送端发的哪一个。如果还停留在时域函数的层面这个问题基本无解——两个函数之间的“距离”不是个自然存在的量。几何化的思路就是在这里切进去的既然函数定义在某个区间上而且它们的平方可积能量有限那它其实就活在一个内积空间里。这个空间和我们熟悉的三维欧氏空间共享几乎所有的代数结构只是维数可能是无穷的。一旦承认这一点两个波形之间的“接近程度”就有了严格定义用的是内积诱导出的距离。整套方法最漂亮的地方在于它把一个关于“连续函数”的判断题转换成了一个关于“有限维向量”的几何题。我自己总结这个转换的价值就三点第一能量有限的信号一定可以被有限个基函数精确或者近似表示维数往往远小于直觉第二一旦落到有限维所有关于欧氏几何的直觉夹角、投影、最短距离全部可以用第三接收机的最优判决规则本质上就是“找最近的那个点”这是几何语言里最自然的一句话。1.2 内积是这套体系的度量尺内积在这套体系里扮演的角色相当于现实世界中测量工具的组合它同时提供长度和角度两类信息。对实信号内积定义为两个函数在公共区间上的乘积积分对复信号则要把第二个函数取共轭。能量就是信号和它自己的内积距离是两个信号差的能量开根号。这里有个实操中最容易踩的小坑不同的教材对积分区间和系数的取法略有差异有的把归一化因子塞进基里有的放到系数里结果看起来差一个常数倍。物理量本身没变但你在对答案的时候会被坑很久。我自己的习惯是永远先把基函数归一化到单位能量再算系数这样系数的平方和直接等于信号能量能量守恒的校验一眼就能看出来。注意实信号和复信号的内积定义不同复信号必须在第二个函数上取共轭。如果做代码实现时漏掉共轭正交性检验会莫名其妙地失败而且失败得很有迷惑性——因为对实信号你永远碰不到这个坑。1.3 选一套什么样的“坐标轴”基函数的选择基本决定了后面所有计算的复杂度。理想情况下我们想要三样东西同时成立两两正交、单位能量、数量尽可能少。数学上一定有满足前两条的基Gram-Schmidt 保证存在但第三条取决于信号集合本身的维度这个维度是信号集的固有属性不是基选得好就能压下去的。工程上的取舍通常发生在“基函数是否容易在硬件里生成”这一层。正弦余弦基在频域分析里天经地义但生成它需要振荡器和精确的相位矩形脉冲组成的基实现起来只要开关和积分器代价是频谱泄漏严重Walsh 函数靠加减法就能生成特别适合数字实现但它对时偏极其敏感。这些取舍不是数学问题是工程问题也是很多教材一笔带过、实际做事时最耗时间的地方。2. 正交基函数谁来当那几根坐标轴2.1 正交与归一化的严格定义一套基函数要能用得先满足正交条件任意两个不同基函数的内积为零。归一化条件则是每个基函数与自己的内积等于一。两条合起来就是所谓的标准正交集。注意这里的“正交”是带积分权重的两个函数看上去八竿子打不着但如果积分出来的结果不为零那它们就不正交不能作为同一套基的两根轴。完备性这一条经常被忽略但没有它整套展开就是残废的。完备的意思是这套基张成的空间已经足够大信号集合里的任意一个信号都能被它表示残差能量趋于零。检验完备性最直接的办法就是算残差——把信号投影到基上加回去看剩下的那部分能量是不是零。如果残差随着维数增加不收敛到零说明基不够用。实际操作中还有一种更实用的判据用帕塞瓦尔关系。如果信号的时域能量恰好等于展开系数的平方和那展开就是无损的基在这组信号上完备。这个恒等式在我写代码时几乎每次都会拿来当断言用因为它能立刻暴露归一化因子写错的问题。2.2 常见正交基族与它们各自的适用场景教材里出现的基大多集中在几个家族每个家族背后都对应一类实际需求。正弦余弦族是最常见的任意整数倍频率的正弦和余弦在整数个周期上积分都是零它们适合分析平稳的窄带信号sinc 函数族在带限信号里是天生的一对采样定理本质上就是 sinc 基展开矩形脉冲分段常值基适合分析数字脉冲串实现简单但频谱拖尾严重Walsh 函数是取值只有正负一的方波族在正交码分多址里是主力因为它让乘法退化成取反硬件成本极低Legendre 多项式族常用于连续逼近问题因为它在最小二乘意义下的截断误差表现好。选择哪一套最核心的判断依据其实是你想从信号里提取什么。想做频谱分析就选正弦基因为它直接对应频率成分想做脉冲形状匹配就选矩形基因为它直接对应时隙想做低复杂度相关运算就选 Walsh。基的选择本身就是一种特征提取这个视角比“凑公式”有用得多。2.3 维数到底由什么决定一个信号集合的维数理论上等于它的线性无关元素的最大个数。实践中这个数字往往比想象的小得多。举个极端的例子一个只在有限个时隙上有值的脉冲串信号集合无论时隙划分多细、采样率多高它的维度最多就是时隙数。再比如所有能量有限的带限信号维度是可数的但无穷多截断到有效带宽之后又会退化到有限维。这个维数在通信里有个专门的名字叫信号空间的自由度。自由度决定了你这套系统最多能同时传送多少个独立的符号也决定了星座图最长能画成什么样。理解这一点很重要你以为增加调制阶数是在“提高信息速率”实际上是在同一个能量预算下把点挤得更密代价一定是抗噪性能下降。这个矛盾后面在星座设计里会反复出现。实操心得动手之前先估一下维数。维数估计错了后面的基函数构造和系数计算全部白做而且错误不会立刻显现通常要等到残差校验才暴露排查成本很高。3. 展开系数怎么算投影这件事3.1 一个公式背后的几何动作展开系数的计算公式看着平平无奇第 k 个系数等于信号与第 k 个基函数的内积再除以该基函数的能量如果基已经归一化除数就是 1。这个式子本质上是投影。你可以想象信号是一根斜放在空间里的棍子基函数是坐标轴系数就是这根棍子在各轴上的影子长度。投影的这个解释在三维空间里靠直觉就能接受搬到函数空间里只是把“点积”换成了“积分”。为什么必须是投影而不是别的什么线性组合因为投影是唯一让残差与已选基全部正交的分解方式。正交意味着残差里不含有任何已经提取过的成分信息不重复。如果系数取别的值残差里就会混入已经用基解释过的部分等于同一份能量算了两次能量守恒立刻破掉。写代码时我会顺手把残差也存下来看一眼它和每个基的内积是不是都接近机器精度。这个检查比事后调试能量不平要省事一百倍。3.2 手算一遍两个正交脉冲的展开拿最简单的一组信号练手。考虑两个等幅矩形脉冲第一个占据前半段第二个占据后半段幅度都是 A每段时长都是半个周期。这两个脉冲天然正交因为它们不重叠。先算能量每个脉冲的能量是 A² 乘以半周期长度记为 E。归一化基就是原脉冲除以根号 E落到每段上基函数的取值是根号下 2 除以周期长度两段各一份。再算系数。信号和第一个基的内积只在重叠的那段上有贡献结果是根号 E。第二个系数同理也是根号 E。系数平方和正好等于 E 加 E也就是总能量帕塞瓦尔成立。这组信号的几何图像特别干净两个点分别落在两根轴上坐标都是根号 E它们之间的距离是根号下 2E。这个结果就是正交信号集的标志性结论——最小距离平方等于两倍符号能量。所有正交调制比如正交频移键控、脉冲位置调制的抗噪性能都由这个数字决定记住它能省下大量推导。3.3 Gram-Schmidt没有现成基的时候自己造现实中的信号集合很少正好正交这时候就得用 Gram-Schmidt 一步步造基。流程是机械的取第一个信号归一化成第一根轴第二个信号减去它在第一根轴上的投影剩下的部分归一化成第二根轴第三个信号减去它在前面所有轴上的投影剩下的归一化依此类推。中途如果某一步减完剩下的部分能量接近零说明这个信号已经被前面的基完全表示了不贡献新维度跳过就行。举个三信号的手算例子把时间轴分成三个等长时隙三个分段常值信号分别是1,1,0、1,0,1、0,1,1。第一个能量是 2第一根轴是它除以根号 2得到0.7071, 0.7071, 0。第二个信号在第一根轴上的投影是 1 乘 0.7071 加 0 加 0等于 0.7071。减掉之后剩下的向量是0.5, −0.5, 1能量 1.5归一化得到第二根轴0.4082, −0.4082, 0.8165。第三个信号在第一根轴上的投影同样 0.7071在第二根轴上的投影是 0 乘 0.4082 加 1 乘 −0.4082 加 1 乘 0.8165等于 0.4082。减掉这两份之后剩下−0.6667, 0.6667, 0.6667能量 1.3333归一化得到−0.5774, 0.5774, 0.5774。三个信号张成三维正好满维。校验一下第三个信号的系数平方和0.5 加 0.1667 加 1.3333等于 2和它的能量一致。整套流程手算下来不超过五分钟但每一步都要小心符号尤其是负投影那一步符号写错会得到一个看起来很合理但完全错误的基。注意Gram-Schmidt 的数值稳定性一般。手工算一两次没问题程序里如果信号接近线性相关浮点误差会放大得很厉害。遇到相关性强的信号集合改用修正的 Gram-Schmidt每步重新正交化一次或者直接上奇异值分解稳得多。3.4 截断误差与帕塞瓦尔校验实际做的时候基的数量往往是被预算卡住的不可能无限多。这时候就出现截断误差的问题用前 N 根轴表示信号残差能量占总能量的比例是多少这个比例可以直接算——总能量减去前 N 个系数的平方和再除以总能量。帕塞瓦尔关系在这里是最强的自检工具。如果算出来的系数平方和大于信号总能量一定是归一化写错了基函数没有归一化到单位能量如果明显小于总能量且随着 N 增大不收敛要么是基不完备要么是某个正交化步骤出错了。我在代码里习惯把这条写成断言只要跑通就说明前面的数学推导没写错后面画星座图才有意义。4. 能量、距离、相关性三个量的几何含义4.1 能量就是向量的长度平方能量在几何里对应长度的平方这个对应关系是整个体系的锚点。信号的能量是它和自己的内积也就是各个展开系数的平方和。这个等式让“能量”从抽象概念变成了可以直接用坐标算出来的数。为什么这么在意能量因为它是通信系统里最贵的资源。发射功率、电池寿命、热噪声全都和能量挂钩。把信号归一化到单位能量本质上是在做一个公平的对比基准在同样的能量预算下哪种方案能让接收端把点分得更开。离开能量谈距离没有意义因为你可以把任何星座的间距放大代价是功耗翻倍。平均符号能量和峰值能量是两个不同的指标在实际系统里要分开看。平均能量决定链路预算峰值能量决定功放是否饱和。正交频分复用之所以对峰均比敏感就是因为在几何上它的点分布在多维空间里某些组合会让瞬时值叠加得很高。4.2 距离与判决接收机到底在干什么两个信号之间的距离定义成它们差的能量开根号。在几何图像里就是两个点之间的直线距离在所有坐标轴上的分量差平方求和再开根号。这个量的物理意义非常明确接收端要区分这两个信号噪声向量至少得有这么长并且方向得正好把点推过判决边界。接收机的最优判决规则在这套语言下就一句话找欧氏距离最近的那个星座点。这就是最大似然判决在加性高斯白噪声下的等价形式因为高斯噪声的似然函数只依赖距离。所有那些看起来复杂的匹配滤波器结构本质上都在算同一个投影然后用投影出来的坐标去比距离。既然判决靠距离最小距离就成了衡量一种调制方案抗噪能力的核心指标。最小距离越小噪声把点推过边界的概率越大。后面在星座图那一节会看到几乎所有调制方案的设计取舍都可以归结为“在给定的平均能量下怎么把最小距离做大”。4.3 相关系数与夹角余弦两个信号的内积除以各自能量的平方根就是相关系数几何上等于夹角的余弦。这个量在工程里有两个极端好用的场合。一个是判断正交性。相关系数为零即正交意味着两个信号在接收端可以完全分离干扰为零。正交频移键控、脉冲位置调制这类方案就是靠这个吃饭的。另一个是判断相似性。相关系数接近正负一时两个信号几乎分不开接收机在噪声下必然混淆。这也是为什么扩频码要求互相关尽可能低——码之间的夹角越大多用户之间的干扰越小。实操心得正负号很重要。相关系数为负的两个信号虽然夹角是钝角但可区分程度和正相关一样差。做码字设计时一定要看绝对值只看正值会漏掉一半的问题。4.4 常用信号集的几何参数速查把常见信号集的距离和能量关系整理成表格做快速估算时非常好用。约定每个信号的符号能量是 E。信号集类型维数最小距离平方相关系数典型应用二进制反极性14E−1BPSK二进制正交22E0正交 FSK四元正交42E0四维正交码单极性开关键控1E0OOK双正交4 信号22E0 或 −1双正交编码这张表里最值得记住的是第一行和第二行的对比。同样是二进制反极性的距离平方是 4E正交只有 2E换算成信噪比是 3 dB 的差距。这 3 dB 就是“为什么 BPSK 比正交 FSK 省功率”的全部答案也是相干解调比非相干解调占优的根源。5. 调制信号在几何空间里长什么样5.1 星座图不是画出来的是基选出来的很多人以为星座图是人为定义的一张图实际上它完全由基函数的选取决定。以四相相移键控为例选定两根正交的单位能量基——通常是半个周期内的余弦和正弦——四个信号就变成平面上的四个点坐标都是正负根号下 E 除以 2 的组合。换一套基坐标会变但点之间的相对距离和整体形状不变。这个不变性非常重要。它意味着几何结构是信号集的客观属性而不是某套基的产物。做仿真时我经常换基底来验证这一点用标准正弦基画出来是一个旋转过的方阵用 Gram-Schmidt 从信号本身生成的基画出来是一个正向对齐的方阵两边的距离矩阵完全一致。如果距离矩阵对不上那一定是基构造或者系数计算哪一步错了。理解这一点还有个额外好处你可以自由选择让画图最方便的那套基。做理论分析时长用正弦基因为坐标好写做硬件实现时用正交化后的基因为可以用开关和积分器实现。5.2 二进制与四相的几何对比BPSK 用一根轴就够了两个点落在实数轴的对称位置上坐标是正负根号 E。最小距离就是 2 根号 E平方后是 4E。QPSK 用两根轴四个点落在正方形的四个角上坐标是正负根号下 E 除以 2 的组合。相邻点之间的最小距离是根号下 2E平方后是 2E。看起来比 BPSK 小了但别急着下结论——QPSK 的能量是每符号 E而它一个符号送两个比特折算成每比特能量 Eb 就是 E 的一半。把最小距离换算成每比特的尺度QPSK 和 BPSK 的归一化最小距离完全相同误码率曲线也完全重合。这个结论反直觉但极其重要在同样的每比特能量预算下用正交的两维送两个比特和用一维送一个比特但传两倍时间抗噪性能是一样的。QPSK 省的是带宽不是功率。我在给新人讲这一段的时候一定会让他们自己把这个换算走一遍比背公式有用得多。5.3 高阶调制的几何代价把点继续往同一个平面里塞代价就开始显现。八相相移键控把八个点摊在圆上最小距离变成 2 根号 E 乘以八分之π的正弦约为 0.765 根号 E平方后只剩 0.586E。对比 QPSK 的 2E差距已经有 5 个 dB 出头。十六正交幅度调制走的是另一条路不是把点排在圆上而是排在网格上。四个幅度电平在每根轴上取正负一的对称值最小距离平方是 0.4 乘以每符号能量。这个数字比十六相相移键控的 0.152E 大了一倍半还多换算成信噪比大约是 4.2 dB 的优势。这就是为什么高阶系统里几乎看不到超过八相的相移键控全都转去用正交幅度调制——圆形排布在点数变多时浪费了太多角落空间。再往上走网格排布的收益也在衰减。每个维度的电平数增加最小距离的相对衰减大约是电平数减一的倒数关系所以每一阶的提升都要付出功率代价。工程上选择阶数本质是在带宽效率和功率效率之间找一个和链路预算匹配的平衡点不是越高越好。调制方式每符号能量 Es最小距离平方折合每比特相对 BPSK 损失BPSKEb4Eb4Eb0 dBQPSK2Eb4Eb4Eb0 dB8-PSK3Eb1.76Eb1.76Eb约 3.6 dB16-QAM4Eb1.6Eb1.6Eb约 4.0 dB16-PSK4Eb0.61Eb0.61Eb约 8.2 dB这张表最直观的用法是链路预算紧张的时候先看损失那一列再看带宽需求最后决定阶数。16-QAM 和 16-PSK 那两行的对比是经典案例同样的频谱效率一个四点几 dB一个八点几 dB选错方案等于白白浪费一半发射功率。5.4 从最小距离估误符号率有了最小距离误符号率可以估得很准。在高信噪比下错误几乎只发生在最近邻之间所以误符号率主要由最小距离和噪声功率的比值决定近似公式是一个 Q 函数乘以最近邻的个数。具体地噪声在每个维度上的方差是 N0/2判决边界在相邻点连线的中点上所以错误概率约等于 Q 乘上最小距离除以两倍标准差。对方形星座最近邻个数在两个方向上各算一次系数是四乘以一减去根号 M 分之一的量。这套估算在很多场景下和精确仿真差不到零点几个 dB用来做链路预算绰绰有余。注意这套近似在高信噪比下才准。信噪比低于某个阈值之后非最近邻的错误开始贡献实际误符号率会比估算值高出现所谓的错误平底。做灵敏度测试的时候别只信公式一定要跑蒙特卡洛。6. 把整套流程用代码跑一遍6.1 环境与数据准备环境很轻装好数值计算和绘图两个库就够。数据我用分段常值的信号向量来构造因为它在离散域里和连续域的积分一一对应求和就是积分能量就是平方和所有公式都能原样落地不用担心采样率和积分区间的换算问题——这一层换算出错是初学者最常见的翻车点。构造三个信号每个信号三个时隙取值用前面手算的例子。信号矩阵按行存放每行一个信号列数是时隙数。这个布局在后面的正交化和投影里比较顺手。import numpy as np np.set_printoptions(precision4, suppressTrue) # 三行分别是三个信号列是三个等长时隙 S np.array([ [1.0, 1.0, 0.0], [1.0, 0.0, 1.0], [0.0, 1.0, 1.0], ]) # 离散域里能量就是平方和 energies np.sum(S**2, axis1) print(各信号能量:, energies)跑出来的能量是 2、2、2三个信号能量相同这意味着后面算出来的距离是对称的适合做教学案例。实际工程里能量往往不等归一化那一步就必须严格按每个信号自己的能量来除。6.2 正交化与展开系数正交化按 Gram-Schmidt 走每一步减去前面所有基上的投影然后归一化。用矩阵的形式写更清楚维护一个已生成的基矩阵每一步用矩阵乘法一次性算完所有投影。def gram_schmidt(signals, tol1e-10): basis [] for s in signals: v s.astype(float).copy() for b in basis: v v - np.dot(v, b) * b norm np.linalg.norm(v) if norm tol: continue # 已被前面的基表示跳过 basis.append(v / norm) return np.array(basis) B gram_schmidt(S) print(基向量:\n, B) print(基两两内积矩阵:\n, B B.T)内积矩阵应该是单位矩阵。这个断言一旦不成立后面所有的系数都不必看了先回头查归一化。我一般会把这个检查写成显式的 assert因为一旦让它静默通过调试方向会偏得很远。有了基系数就是信号在基上的投影用一个矩阵乘法就能批量算完所有信号。C S B.T # 每行是一个信号在各基上的展开系数 S_rec C B # 反投影重建 print(展开系数:\n, C) print(重建误差:, np.max(np.abs(S_rec - S))) print(系数平方和:, np.sum(C**2, axis1)) print(原始能量:, np.sum(S**2, axis1))重建误差应该在机器精度量级系数平方和必须和原始能量逐项相等。这两条同时成立说明基完备、归一化正确、投影公式没写错。三个检查一次过才敢往下一步走。6.3 能量、距离与相关系数的批量计算有了系数矩阵所有几何量都能用矩阵运算批量算。距离矩阵用广播做差再求范数相关系数矩阵用内积除以能量的外积开根号都是几行的事。这种批量写法在做星座对比时特别省时间一堆调制方案可以一次算完。def pairwise_distance(C): diff C[:, None, :] - C[None, :, :] return np.sqrt(np.sum(diff**2, axis-1)) def corr_matrix(C): G C C.T n np.sqrt(np.diag(G)) return G / np.outer(n, n) D pairwise_distance(C) R corr_matrix(C) print(距离矩阵:\n, D) print(相关系数矩阵:\n, R)距离矩阵的对角线是零非对角线元素应该有两两相等的结构因为三个信号的能量和互相关结构是对称的。相关系数矩阵里会出现负值这是正常的说明某些信号对的夹角是钝角。这里验证一个关键点非对角元素绝对值最大不超过一超过说明内积算错了。6.4 星座图绘制与最小距离扫描画星座图我用的是每符号能量归一化的方式这样不同阶数的调制可以放在同一个坐标系里比较。相移键控按圆排布正交幅度调制按网格排布两者的点集都能用几行代码生成。def psk(M, Es1.0): k np.arange(M) r np.sqrt(Es) return np.stack([r * np.cos(2*np.pi*k/M), r * np.sin(2*np.pi*k/M)], axis1) def qam(M, Es1.0): m int(np.sqrt(M)) levels np.arange(-(m-1), m, 2) pts np.array([[a, b] for a in levels for b in levels], dtypefloat) # 归一化到目标符号能量 return pts * np.sqrt(Es / np.mean(np.sum(pts**2, axis1))) def dmin(pts): diff pts[:, None, :] - pts[None, :, :] d np.sqrt(np.sum(diff**2, axis-1)) np.fill_diagonal(d, np.inf) return d.min() for name, pts in [(BPSK, psk(2)), (QPSK, psk(4)), (8PSK, psk(8)), (16PSK, psk(16)), (16QAM, qam(16))]: d dmin(pts) print(f{name:6s} dmin{d:.4f} dmin^2/Es{d**2:.4f} f相对BPSK{10*np.log10((d**2)/2):.2f} dB)最后那一行的换算值得解释一下。BPSK 在单位符号能量下的最小距离平方是 4但每符号只送一个比特为了和每比特能量对齐我用 dmin 平方除以 2 作为基准相当于把 QPSK 的每符号能量折算成两个比特。跑出来的数字和前面表格里的理论值应该完全对上8PSK 约 3.6 dB 损失16PSK 约 8.2 dB16QAM 约 4.0 dB。这一行输出是我认为整个项目里最有价值的一步——它把抽象的最小距离直接翻译成了链路预算上能用的分贝数。7. 常见问题与排查技巧实录7.1 问题速查表这套东西我前后重做了不止一次踩过的坑高度集中在几个位置。整理成表遇到问题按症状查比从头推一遍快得多。症状最可能的原因排查动作基两两内积不为零归一化或投影减错打印内积矩阵定位是哪一对系数平方和小于原能量基不完备或维数不够增大基数量看残差是否收敛系数平方和大于原能量基未归一化检查每个基的能量是否为 1重建误差不收敛正交化过程有累积误差改用修正 Gram-Schmidt距离矩阵出现负数对角线数值误差对角线强制置零再比较星座图整体旋转基选择不同属正常现象检查距离矩阵是否一致误符号率仿真偏离理论噪声方差定义不一致确认每维方差是 N0/27.2 几个真正花时间的地方第一个是复信号的内积共轭。这条前面提过但值得再强调一次因为它是那种“看起来完全正常、结果全错”的问题。实信号仿真跑通了不代表复信号也对两套代码共用一个内积函数是很危险的做法我现在的习惯是显式写两个函数参数类型一目了然。第二个是归一化的对象搞混。有人把信号能量归一化有人把基函数能量归一化两种做法都能得到一套自洽的公式但混着用就全乱了。我的做法是固定一条约定基永远单位能量信号能量单独算这样任何时刻看到能量不平都能立刻判断是系数算错了还是基没归一化。第三个是高阶星座的归一化基准。十六正交幅度调制的原始网格点平均能量不是一必须先除再乘目标能量。忘记这一步画出来的点和理论值差一个常数因子所有距离都在同一个比例上偏图看起来完全正常数字全错。这种错误最难发现因为视觉上毫无违和感。第四个是噪声方差和每维能量的对应。在平面星座上两个维度的噪声方差各是 N0/2不是 N0。仿真里如果按 N0 加噪误符号率曲线会整体平移几个 dB而且形状是对的特别容易误判成“理论公式有误”。提示每次改完基或者星座定义先把内积矩阵、重建误差、能量守恒三条断言跑一遍。这三条全绿再去看误符号率曲线能省掉大量返工。7.3 一点延伸思路这套几何化的框架能撑起的东西比想象中多。同样的方法可以搬到多天线系统里把空间维度当作额外的坐标轴信道矩阵直接变成一组非正交的基接收端的迫零和最小均方误差检测本质上是对这组基做正交化。也可以搬到多用户检测把每个用户的码字看成向量用户之间的干扰就是夹角不为九十度的后果。我后面打算把这套代码往上加一层噪声和蒙特卡洛直接跑出误符号率曲线和理论值叠在一起看那样对整个链条的理解会更扎实。如果你也在补这块基础我的建议是先别碰代码拿纸笔把三信号那个 Gram-Schmidt 手推一遍推到每个数字都能对上。手上有了这个感觉再回头看公式那种“为什么是投影”的疑问会自己消失。代码只是把已经想清楚的事重复一遍想不清楚的部分写再多代码也补不上。