索引OFDM仿真:Python实现与AWGN/瑞利信道误码率分析

发布时间:2026/10/7 17:05:50
索引OFDM仿真:Python实现与AWGN/瑞利信道误码率分析 索引OFDM系统像是个自带导航的快递员每个子载波都能携带数据包和地址标签。我第一次听说索引OFDM时这句话直接让我记住了它。后来做物理层仿真从零写代码验证这个系统才发现这句话不只是个比喻——它把索引调制最核心的思想说透了传信息的不只是子载波上的星座符号数据包还有哪些子载波被激活这个排列组合本身地址标签。这篇文章记录我自己撸的一套最小二乘仿真链路用Python实现索引OFDMIndex Modulated OFDM常缩写为OFDM-IM分别在AWGN信道和瑞利信道下测误码率看看这个系统到底抗不抗揍顺便把仿真里几个容易踩的坑全交代出来。如果你正在学OFDM进阶、或者想搞清楚索引调制相比传统OFDM到底赢在哪里、输在哪里这份笔记应该能帮你省下不少弯路。1. 从快递员带导航说起索引OFDM到底在传什么1.1 传统OFDM和索引OFDM差在子载波的使用方式传统OFDM大家都很熟一帧数据拆成N个子载波每个子载波独立调制一个星座符号BPSK就每个子载波扛1 bitQPSK就扛2 bit。子载波是货车有多少车拉多少货所有车都上路。索引OFDM不一样。它先把N个子载波分成若干个小组每个小组n个子载波里只激活k个k n。被激活的子载波正常传星座符号没被激活的子载波直接零功率发射。那k个位置从n个里面挑出来一共有C(n,k)种组合这个组合本身就能编码 log2(C(n,k)) 比特的信息——这就是索引比特。在接收端检测器不仅要解出激活子载波上的符号还要猜中这一块到底哪几个子载波被激活了。这就是OFDM-IM的基本框架每一块子载波组信息分两路走。一路是明面上的星座符号另一路藏在子载波激活模式里。后者就是标题说的地址标签。1.2 数据包和地址标签分别是谁把整个系统比作快递分拨中心子载波 格口。一组里有n个格口但今天只有k个格口真的被使用。数据包 激活格口上放的星座符号比如BPSK的±1。地址标签 激活格口的编号组合。接收端先看哪个格口亮了根据亮灯的组合推出一部分比特再读这个格口里的符号推出另一部分比特。这个比喻好在哪里它点出了一个关键物理事实没被激活的子载波是不耗发射功率的。同样总比特数、同样子载波数的情况下索引OFDM把能量集中在k个激活子载波上每个激活符号的实际发射能量比传统OFDM每个符号的平均能量更高。这在高信噪比下会带来一种自然编码增益也是索引调制最有吸引力的地方。1.3 一个看得见摸得着的例子n4、k2我自己做仿真时选了最经典的参数每组4个子载波激活其中2个BPSK调制。这个配置小到可以手算验证大到能看出性能规律。激活组合总数是C(4,2)6但2的幂只有4个所以只能取4种组合来编码2 bit索引比特。每个激活子载波用BPSK再带1 bit两个激活子载波就是2 bit数据比特。加一起每组子载波每时隙传4 bit和传统4子载波BPSK OFDM的比特数完全一样对比起来公平。索引比特激活子载波位置数据比特发送向量示意00子载波0、1b0 b1[±1, ±1, 0, 0]01子载波0、2b0 b1[±1, 0, ±1, 0]10子载波1、2b0 b1[0, ±1, ±1, 0]11子载波2、3b0 b1[0, 0, ±1, ±1]注意每块4 bit里前2 bit是导航信息地址标签后2 bit是包裹内容数据包。接收端的工作就是把这4 bit恢复出来第一步先把激活位置猜对第二步再按符号硬判。2. 动手仿真前先定三件事参数、组合表与检测方案2.1 参数为什么这么选很多人一上来就想跑大的n8、k4、16QAM一起上结果折腾一晚上连发端映射都没跑对。我给的建议是第一版务必小。n2、k1确实太小C(2,1)2索引只有1 bit体现不出组合带信息的味道n8、k4的C(8,4)70虽然能取64种映射传6 bit但ML检测要对64种组合×星座符号做穷举初学阶段复杂度徒增。n4、k2是教科书里常用的最小演示配置索引比特2 bit不大不小ML检测候选数只有4种组合×2种符号×2种符号16个单块计算量完全可以忽略跑几千个块也就眨眼的功夫。决定好参数后剩下的三件事其实是互相锁死的组合表要能覆盖所有索引比特状态索引比特位数和子载波组的比特分配要一致检测器要用同一张组合表来反查。任何一个地方前后对不上BER直接崩到接近0.5而且很难排查。2.2 组合表怎么建itertools一行搞定建表在Python里异常简单from itertools import combinations N 4 K 2 # 所有6种组合按itertools字典序排列 all_combos list(combinations(range(N), K)) print(all_combos) # [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)] # 取前4种作为有效组合2^p必须不超过C(N,K) p int(2**0) # 占位实际应该是 int(floor(log2(len(all_combos)))) p len(all_combos).bit_length() - 1 # 即 floor(log2(6)) 2 use_combos all_combos[:2**p] index_bit_maps [f{i:0{p}b} for i in range(2**p)] # [00, 01, 10, 11]三个容易忽略的点combinations返回的元组是字典序(0,1)在最前这个顺序是稳定的但你心里必须清楚00对应(0,1)而不是00对应(1,0)。6种里砍掉两个不用取的是前4种。这是合法性优先不是性能最优。实际做出来我会发现取哪4个会影响一点性能但对入门演示来说先保证收发一致。建表逻辑必须写成确定性算法不能每次运行重新生成。后面如果要把表持久化到文件或者发给接收端直接重建时顺序变了整个链路就废了。2.3 ML检测为什么小块全搜索就是最优策略接收端的检测目标是从接收向量y里同时猜出激活组合和符号。最优的就是最大似然ML检测它对所有可能的发送向量x_cand都算一遍欧氏距离距离 || y - h * x_cand ||²其中h是信道增益向量。这个距离最小的候选就作为判决结果。因为候选发送向量的结构非常稀疏——每块只有k个位置有能量、其余全是0——所以即使做全搜索候选数量也只有组合数 × 星座点数^k以n4、k2、BPSK为例就是4×2×216个。这点数量CPU处理起来毫无压力。真正的大系统里ML复杂度指数增长一般会用log-MAP或者基于消息传递的近似检测但那是后话。入门阶段我强烈建议先用ML把链路跑通因为ML的性能是天花板你后面写任何低复杂度算法都是跟它在比。3. AWGN信道下的第一轮测试代码实现与基准BER3.1 发送端从比特到频域向量发送端要做的事情很简单把随机比特流按块切分每块4 bit前2 bit选组合后2 bit映射到BPSK符号。我写的modulator如下import numpy as np CONST np.array([-1, 1]) # BPSK: bit 0 - -1bit 1 - 1 def block_to_tx(bit_block, use_combosuse_combos, p2): 输入: bit_block 长度为 p K例如4位 输出: 长度为N的频域发送向量 x未激活子载波置0 idx int(.join(map(str, bit_block[:p])), 2) # 前2位 - 组合下标 data_bits bit_block[p:] # 后2位 - 符号bit x np.zeros(N, dtypecomplex) pos use_combos[idx] for j in range(K): x[pos[j]] CONST[data_bits[j]] return x, idx这个函数返回值里的idx是真实索引比特对应的十进制组合号接收端要用它反查组合。发送端本身不需要它但调试时拿它对照接收端判出来的是不是同一个组合非常方便。3.2 加噪声与ML接收AWGN里没有信道需要均衡AWGN信道是最理想的情形h1接收向量就是发送向量加复高斯噪声。仿真里我按每个BPSK符号能量Es1来设置噪声功率信噪比用Es/N0表示。设线性信噪比为snr_lin 10^(snr_db/10)则噪声方差N0 1/snr_lin噪声向量每个分量的实部和虚部分别是方差N0/2的独立高斯。def ml_detect(y, h, use_combosuse_combos, CONSTCONST): best_dist np.inf best_bits None for idx, pos in enumerate(use_combos): for s0 in CONST: for s1 in CONST: x_cand np.zeros(N, dtypecomplex) x_cand[pos[0]] s0 x_cand[pos[1]] s1 dist np.sum(np.abs(y - h * x_cand) ** 2) if dist best_dist: best_dist dist best_bits np.array([1 if s0 1 else 0, 1 if s1 1 else 0]) # 把索引bit和符号bit拼回去 idx_bits np.array([int(b) for b in f{best_idx:0{p}b}]) if best_bits is not None else None return np.concatenate([idx_bits, best_bits])等一下上面这个写法的best_idx没定义我实际跑通的是下面这个版本把索引也记下来def ml_detect(y, h, use_combosuse_combos, CONSTCONST, p2): best_dist np.inf best_idx 0 best_data np.zeros(K, dtypeint) for idx, pos in enumerate(use_combos): for s0 in CONST: for s1 in CONST: x_cand np.zeros(N, dtypecomplex) x_cand[pos[0]] s0 x_cand[pos[1]] s1 dist np.sum(np.abs(y - h * x_cand) ** 2) if dist best_dist: best_dist dist best_idx idx best_data np.array([1 if s0 1 else 0, 1 if s1 1 else 0]) idx_bits np.array([int(b) for b in f{best_idx:0{p}b}]) return np.concatenate([idx_bits, best_data])有了这两个函数AWGN下的主仿真循环就是最朴素的那个样子了def run_awgn_ber(snr_db, nblocks3000): snr_lin 10 ** (snr_db / 10) noise_var 1.0 / snr_lin total_err 0 total_bits 0 for _ in range(nblocks): bit_block np.random.randint(0, 2, p K) x, _ block_to_tx(bit_block) noise (np.random.randn(N) 1j * np.random.randn(N)) / np.sqrt(2) * np.sqrt(noise_var) y x noise # AWGN: h 1 bits_est ml_detect(y, np.ones(N)) total_err np.sum(bits_est ! bit_block) total_bits len(bit_block) return total_err / total_bits这里有个小细节(randn 1j*randn)/sqrt(2)生成的是标准复高斯实部和虚部方差各为0.5总方差1。再乘sqrt(noise_var)后总方差就是noise_var正好等于N0。很多人第一次写复噪声会在sqrt(2)上栽跟头导致实测BER比理论差3 dB原因就在这里。3.3 跑出来的曲线应该长什么样我把SNR从0 dB扫到12 dB每个点跑3000个块。先看传统BPSK OFDM参考线的理论公式BER 0.5 * erfc(sqrt(Eb/N0))这个公式在AWGN下是精确的。索引OFDM的BER不会比这个简单公式完全重合因为它的总错误有两部分索引组合判错、符号判错。低SNR阶段组合判错的概率很高所以BER曲线会比纯BPSK略高一点但到了中高SNR因为同样的4 bit信息只用2个激活子载波发射能量集中带来的增益逐渐体现曲线表现反而优于传统全激活子载波方案。具体仿真图形状我看到的典型结果是在BER1e-3附近n4、k2的索引OFDM会比传统4子载波BPSK OFDM好约1~2 dB。这不算夸张但要注意这是在相同频谱效率、相同比特数的前提下拿到的属于白赚的增益。4. 换到瑞利信道衰落建模与接收端如何应对4.1 瑞利信道建模每个子载波的复增益从哪来无线信号经过多径传播到达接收端的每个频点上的增益不再是1而是一个随机复数。OFDM把宽带信道切成窄带子载波后每个子载波上的等效信道可以看成平坦衰落。最简单的瑞利信道模型就是每个子载波上的h服从标准复高斯分布h ~ CN(0, 1)生成方式h (randn 1j*randn)/sqrt(2)。这样E[|h|^2]1平均接收功率和AWGN情形对齐方便对比。严格说真实多径信道相邻子载波上的衰落是相关的相关性由信道时延扩展决定。我这次仿真先假设子载波间独立这在加了足够深的交织器之后是个合理近似也是论文里最常见的benchmark设置。想看更贴近实际的信道可以把独立抽样换成带相关性的频域信道生成那部分我在第五节扩展里再提。每个块重新生成一次h相当于块与块之间的信道也独立——这就是典型的快衰落蒙特卡洛仿真。REPL每个块都用新信道跑出来的BE R已经包含了所有衰落状态的平均不需要额外再做信道平均。4.2 接收端怎么应对理想CSI下的ML检测瑞利信道下接收向量为y h * x n注意这里的乘法是逐子载波相乘。检测器在计算候选距离时要把信道乘回去也就是用y - h * x_cand来算距离。之前AWGN里h1所以看起来没区别到了瑞利下这个h立刻变得关键。我在仿真中假设接收端已知h也就是理想信道状态信息CSI。实际系统里h要靠导频估计出来但做算法验证时先用理想CSI能把检测算法本身的性能和信道估计误差的影响分开。等确认检测没写错再去叠加LS/MMSE信道估计定位问题会清楚很多。所以瑞利下的主循环其实只比AWGN多了两行h (np.random.randn(N) 1j * np.random.randn(N)) / np.sqrt(2) y h * x noise bits_est ml_detect(y, h)ml_detect内部不变它已经把h当参数传进去了。这也是我把检测器设计成接收h参数的原因——同一个函数能同时跑两种信道少写一遍检测逻辑就少一份出bug的风险。4.3 性能对比AWGN和瑞利差多远传统BPSK在瑞利信道下的平均误比特率有闭式解P_b 0.5 * (1 - sqrt(g / (1 g))) 其中g是平均每比特信噪比Eb/N0跟我开头在AWGN下看到的理论曲线一比差别是很直观的同样10 dB平均信噪比AWGN下BPSK的BER已经到1e-5量级瑞利下却还停在2e-2量级。也就是说瑞利让曲线整体右移了大概10 dB。这个现象在索引OFDM上也一样会看到只是具体数字因为有索引错误的存在会有些偏移。索引OFDM在瑞利下的一个额外卖点是它天然拥有一种激活子载波分集。因为不同激活模式的组合在接收端的等效距离被衰落后的信道矩阵重新拉大ML检测能从多个衰落状态里选最好的那部分能量整体BER曲线的斜率会比某些固定星座方案略好。这也是学术文章里常说索引调制在衰落信道下能提供额外分集增益的来源。实测下来n4、k2的配置在瑞利下相比全激活BPSK OFDM的优势大概在2~3 dB比AWGN下更明显。5. 这次仿真里踩过的坑和下一步可以玩的方向5.1 最大坑组合表顺序与映射关系不一致我第一版代码里犯的错特别典型发送端用itertools.combinations生成组合接收端为了省事手动写了一个列了4个元组的表。结果发送端的(0,1)映射到索引00接收端的表里(0,1)却在索引01位置。跑出来BER稳定在0.5一开始还以为是噪声协调性写错了排查了半天才发现是映射错位。这类问题调试周期长因为完全不报错只有BER异常。我的建议是发送端和接收端共用同一个建表函数不要各写一遍。建表时把组合表存成文件比如CSV、JSON跨文件使用时直接读不重新生成。调试时打印几个块的真实索引和检测索引肉眼比对前几个块就能发现映射错位。5.2 能量归一化和Eb/N0换算的细节做OFDM仿真能量归一化是永远的痛。这次索引OFDM比普通OFDM更容易踩因为块内只有k个子载波有能量其余是0。如果你按每个子载波平均能量去设置噪声功率BER曲线会整体偏移。正确做法是明确锚定激活符号能量。我让每个BPSK符号能量Es1每块总能量就是K2。噪声方差直接按N01/snr_lin设置。此时横轴是Es/N0。如果想画Eb/N0曲线换算关系是每块总能量 / 每块总比特数 K / (p K)以n4、k2为例就是2/40.5所以Eb/N0 Es/N0 - 3 dB。也就是说同一帧数据实际每比特能量比每符号能量低3 dB因为每个激活符号只附带了平均0.5比特的索引信息。画图时务必把横轴标清楚我见过不少同学拿Es/N0的图直接跟论文里Eb/N0的图比数字以为差3 dB是算法问题其实只是坐标定义不同。5.3 下一步可以玩的方向这套链路跑通之后后面能扩展的东西非常多把BPSK换成QPSK或16QAM看看数据比特变大后索引部分和数据部分的错误占比怎么变化。把分组改成n8、k4索引比特变成6 bit但ML检测候选数会暴涨到64×4^4这时必须换低复杂度检测比如按子载波独立计算对数似然比再组合判决。在信道模型里加入子载波相关性用多径时延来生成频域信道看相关衰落对索引检测的影响。叠加上行链路常见的发射端单射频映射方案利用激活子载波的位置信息换取更低的射频开关复杂度。我个人跑完这组仿真最大的体会不是那十几个dB的增益而是索引调制提供了一种全新的自由度把信息藏在系统资源配置方式里而不是只藏在波形上。这种思路在接下来的通感一体化、超大规模MIMO里都被反复翻出来用。所以这套小代码虽然参数朴素但它背后的思维方式值得好好揣摩。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询