ARTICLE DETAIL

资讯详情

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

16QAM调制从原理到MATLAB仿真:完整程序与避坑指南

16QAM调制从原理到MATLAB仿真:完整程序与避坑指南 简介这是一份基于Verilog的16QAM调制器FPGA工程源码面向无线通信、数字信号处理方向的电子工程师与FPGA学习者适合在Quartus环境中完成从RTL设计到板级验证的完整流程。资源包共126个文件约640KB以cdb/hdb等工程数据库、v源码、rpt报告、qmsg日志及sof/pof配置文件为主还包含bmp仿真波形图、sdc时序约束和readme说明便于对照检查综合、布局布线及下载配置等各环节。项目覆盖数据预处理、星座映射、I/Q调制、PLL时钟同步与FEC编码等关键模块并附有add、dds、top等核心源码可直观理解16QAM调制器在FPGA上的实现思路。目前已有757人学习下载适合需要快速上手16QAM调制硬件设计、参考工程结构或进行二次开发的读者。 前段时间整理代码库时翻出一个大学时代写的16QAM调制程序回家跑了一遍居然还能出正确的星座图。回想当初为了搞懂这个调制方式前后折腾了好几个晚上踩了一堆现在已经觉得理所当然、但当时完全想不通的坑。正好身边有几个朋友在学通信原理一上来就被16QAM的各种公式和概念绕晕问我说到底该怎么入门、程序怎么写才算真正理解。我想了想与其一句句讲理论不如把我写过的、验证过的完整程序流程梳理出来讲清楚每一步在干什么、为什么要这样干顺便把那些容易踩的坑也一并交代了。这篇东西没有高深数学尽量说人话适合刚接触数字调制、想动手跑通仿真或者正在做课程设计的朋友参考。1. 为什么一上来就要写16QAM调制程序而不是先学理论很多教材讲数字调制是从2ASK、2FSK、BPSK、QPSK一路讲上来的等讲到MQAM的时候公式已经堆了一黑板符号星座图密密麻麻初学者很容易被劝退。但我个人的体会是调制这个东西光看公式没什么感觉真正让你“懂了”的时刻是你亲手把一个比特序列变成IQ两路信号再画到复平面上看见那些点规规矩矩地落在16个格子里。16QAM特别适合作为第一个“真正意义上的”数字调制程序来练手原因有三。第一它是二维调制IQ两路同时承载信息比单纯的幅度调制、相位调制都更接近现代通信系统的真实工作方式。第二它的映射规则足够简单16个点用手都能画出来不需要借助复杂的编码理论就能理解“比特到符号”的映射关系。第三它的性能分析、误码率计算都有非常成熟的结论你的仿真结果可以跟理论值对照程序写对了没有一目了然。而且从实用的角度看16QAM是Wi-Fi 4802.11n、LTE、数字电视广播等系统的实际选择之一不是教科书里那种“仅仅为了教学而存在”的调制方式。你写一个16QAM调制程序等于亲手复现了真实通信链路中的发射端核心模块。这件事的价值比单纯刷十道课后习题要高得多。2. 16QAM调制核心原理与程序设计的三个关键点2.1 从比特到符号映射表才是整个程序的灵魂16QAM的名字已经说明了它的本质16个符号每个符号携带4个比特因为2^416。所谓调制就是把这4个比特映射到复平面上的某个点。映射的方式有很多种最常见的是格雷码映射——相邻星座点之间只差1个比特。这样做的好处是接收端一旦发生判决错误大概率只会错1个比特而不是错2个甚至更多误比特率BER会更优。写程序的时候映射表建议直接用数组或者字典结构写死而不是现场计算。我第一次写的时候想着“用公式现场算多优雅”结果程序跑起来倒是没错但调试的时候根本无法直观地看出哪个输入对应哪个输出一个不起眼的象限判断错误查了整整一下午。这里给出一个我实际使用过的映射值星座点坐标为复数实部为I路虚部为Q路import numpy as np # 16QAM 格雷码映射表索引为二进制比特值 qam16_mapping { 0b0000: -3 - 3j, 0b0001: -3 - 1j, 0b0011: -3 1j, 0b0010: -3 3j, 0b0100: -1 - 3j, 0b0101: -1 - 1j, 0b0111: -1 1j, 0b0110: -1 3j, 0b1100: 1 - 3j, 0b1101: 1 - 1j, 0b1111: 1 1j, 0b1110: 1 3j, 0b1000: 3 - 3j, 0b1001: 3 - 1j, 0b1011: 3 1j, 0b1010: 3 3j, }有的同学可能注意到了我这里是按二进制值直接索引的上下左右相邻点恰好只差1比特这就是格雷码的好处。实际项目中映射表往往是由上层协议决定的程序里只需要保证映射关系与协议一致即可。2.2 为什么要对星座点做归一化归一化因子怎么算很多入门教程会直接给出上面那16个坐标点然后就不管了。但如果你真的把这16个点直接送进调制器后面做功率计算、信噪比SNR设置、误码率对比的时候就会发现结果总是差那么一点。原因很简单这16个点的平均功率不是1。16QAM的16个星座点坐标是{±1, ±3}在I路和Q路上的两两组合。平均功率平均能量怎么算取所有点的模平方再求平均有4个点模平方为 1^2 1^2 2四个角的“内角”点有8个点模平方为 1^2 3^2 10边上的点有4个点模平方为 3^2 3^2 18四个角的“外角”点平均能量 E_avg (4×2 8×10 4×18) / 16 (8 80 72) / 16 160 / 16 10。也就是说这16个点的平均功率是10。如果你不做归一化后面设定“信噪比为10dB”的时候实际的信噪比和你的设定就对不上因为信号的功率默认成了10而不是1。解决的办法很简单把每个星座点都除以 √10让平均功率变成1。归一化后的星座点就是 {-3/√10, -1/√10, 1/√10, 3/√10} 在I、Q两路上的组合。这步运算我建议在程序初始化的时候一次性完成而不是写进调制函数里每次计算。一个是效率考虑另外一个原因是便于后续调试——你随时可以关掉归一化观察星座图的绝对坐标。2.3 I/Q两路信号生成串并转换和基带脉冲成型16QAM符号是复数实部对应I路同相分量虚部对应Q路正交分量。调制前的比特流是串行的每4个比特为一组前2个比特决定I路幅度后2个比特决定Q路幅度具体是前2还是后2取决于你的映射表实现。这个过程在数字通信里叫串并转换程序实现起来其实很简单就是把一维的比特数组 reshape 成二维矩阵。真正容易忽略的是基带脉冲成型。很多仿真程序为了让星座图画出来好看直接发射“冲击串”——也就是每个符号是一个点中间没有过渡波形。这样做在纯仿真的误码率计算里问题不大因为接收端是理想采样但只要你仿真里加了带宽限制、过采样或者信道效应立刻就会出问题信号频谱超出带宽邻道干扰接收端采样时刻稍有偏差误码率就急剧恶化。实际发射机中会用一个成型滤波器通常是根升余弦滤波器RRC对基带信号进行脉冲成型。虽然这一节的重点是调制但你在写调制程序的时候就应该把成型滤波的位置预留出来至少留一个接口。否则后面想加滤波器的时候整个程序结构都要推翻重来。我最初偷懒没用成型滤波后来做带限信道仿真的时候不得不在原有代码外面硬套了一层很丑陋的滤波现在想起来都想吐槽当时的自己。3. 完整的16QAM调制MATLAB程序与逐段解读3.1 主程序框架参数设置与调用流程下面给出一段完整的MATLAB程序基本上是我当年课程设计版本的简化重写结构比较清晰适合从头读。核心流程是生成随机比特 → 串并转换 → 查映射表得到符号序列 → 归一化 → 过采样与成型滤波 → 绘制星座图和波形图。%% 16QAM 基带调制仿真主程序 clear; clc; close all; % 参数设置 M 16; % 调制阶数 k log2(M); % 每个符号的比特数k4 numSymbols 2000; % 仿真符号数 numBits numSymbols * k; % 总比特数 sps 8; % 每个符号的采样点数过采样率 rolloff 0.35; % 根升余弦滤波器滚降系数 % 生成随机比特流 bits randi([0 1], numBits, 1); % 16QAM 映射表未归一化 mapTable [-3-3j; -3-1j; -31j; -33j; ... -1-3j; -1-1j; -11j; -13j; ... 1-3j; 1-1j; 11j; 13j; ... 3-3j; 3-1j; 31j; 33j]; mapTable mapTable / sqrt(10); % 能量归一化 % 串并转换比特流按每4比特一组转换为十进制索引 bitMatrix reshape(bits, k, numSymbols); symbolIdx bi2de(bitMatrix, left-msb); % 每一行是一个符号的十进制值 symbolIdx symbolIdx 1; % MATLAB 索引从1开始 % 调制映射查表得到复数符号序列 symbols mapTable(symbolIdx); % 过采样插零 symbolsUp upsample(symbols, sps); % 根升余弦脉冲成型滤波 rrcFilter rcosdesign(rolloff, 6, sps, sqrt); txSignal filter(rrcFilter, 1, symbolsUp); % 补偿滤波器延迟 delay (length(rrcFilter) - 1) / 2; txSignal txSignal(delay1:end); % 绘图 figure; plot(real(symbols), imag(symbols), o); grid on; axis equal; title(16QAM 星座图发送端); xlabel(I); ylabel(Q); figure; plot(real(txSignal(1:sps*20)), b); hold on; plot(imag(txSignal(1:sps*20)), r); legend(I 路波形, Q 路波形); title(16QAM 基带信号波形前20个符号);3.2 逐段说明这几行代码为什么不能省先看比特流生成和串并转换。randi([0 1], numBits, 1)生成一个列向量这就是我们要发送的原始信息。reshape(bits, k, numSymbols)的分组方式值得留意MATLAB 是按列填充的所以reshape之后每列恰好是一个符号的4个比特。然后转置成numSymbols × 4的矩阵每一行对应一个符号。再用bi2de(..., left-msb)把这4个比特转成十进制。这就是“把比特流切成4比特一组”的标准写法。接下来查表映射。很多教材会把映射写成一个巨大的 switch-case但用查表法更接近真实硬件的实现思路——在硬件里映射就是一个只读存储器输入索引输出IQ值。程序里直接索引mapTable(symbolIdx)干的就是这个事。然后看过采样和成型滤波。upsample(symbols, sps)在每个符号之间插入sps-1个0把符号率提高sps倍。插0之后再通过rcosdesign生成的根升余弦滤波器就完成了脉冲成型。rcosdesign(rolloff, 6, sps, sqrt)这个函数我多解释一句6是滤波器的截断符号长度意思是滤波器持续6个符号周期sps是每个符号的采样点数sqrt表示生成根升余弦滤波器而不是普通升余弦。滚降系数rolloff越大信号带宽越宽但时域波形衰减越快对定时同步的要求越低。0.35 是个折中值工程上很常用。最后为什么要补偿滤波器延迟。任何因果滤波器都有延迟根升余弦滤波器的群延迟是(length(rrcFilter)-1)/2个采样点。不把这部分延迟补偿掉星座图看起来就会是“乱”的因为采样时刻不对信号还在过渡过程中就被采下来了。这个问题我在初学的时候经常遇到一度以为是调制算错了其实是滤波器延迟没补。3.3 一个完整的接收端示例加噪声、判决、解映射调制程序只有发射端是不够的至少得能自己验证。最简单的验证方式加白噪声 → 匹配滤波还是那个根升余弦→ 按符号周期采样 → 最小欧氏距离判决 → 解映射回比特 → 对比误码率。我把接收端的基础代码也贴出来这样你就有了一个完整的16QAM链路。%% 接收端加噪、匹配滤波、采样、判决、解映射 EbN0dB 15; % 每比特信噪比dB EbN0 10^(EbN0dB/10); % 注归一化后每个符号能量为1每个符号4比特所以 Es/N0 4 * Eb/N0 EsN0 4 * EbN0; noiseVar 1 / EsN0; % 复噪声方差每维是 noiseVar/2 noise sqrt(noiseVar/2) * (randn(size(txSignal)) 1j*randn(size(txSignal))); rxSignal txSignal noise; % 匹配滤波与发送端相同 rxFiltered filter(rrcFilter, 1, rxSignal); rxFiltered rxFiltered(delay1:end); % 按符号周期采样提取每个符号点 rxSymbols rxFiltered(1:sps:end); rxSymbols rxSymbols(1:numSymbols); % 去掉尾部多余数据 % 最小欧氏距离判决 distances abs(rxSymbols - mapTable); % 每个接收符号到16个星座点的距离 [~, detectedIdx] min(distances, [], 2); detectedIdx detectedIdx - 1; % 转为0-15 % 解映射十进制索引转回比特序列 detectedBits de2bi(detectedIdx, k, left-msb); detectedBits reshape(detectedBits., [], 1); % 误码率统计 ber sum(bits ~ detectedBits) / numBits; fprintf(Eb/N0 %.2f dB, 误比特率 %.6f\n, EbN0dB, ber); % 绘制接收星座图 figure; plot(real(rxSymbols), imag(rxSymbols), .); axis equal; grid on; title(16QAM 接收星座图加噪后);这段代码里有一个关键换算我单独拎出来强调程序里设定的噪声方差是1/(4*EbN0)这是因为我们对星座点做了能量归一化每个符号的平均能量等于1。一个符号有4个比特所以Es/N0 4 * Eb/N0。而复噪声的方差等于N0所以每个维度的实部/虚部噪声方差就是N0/2 1/(2*EsN0) 1/(8*EbN0)。如果你噪声功率设错了仿真出来的误码率会比理论值差好几个数量级但星座图看起来却“好像还行”这种隐蔽错误最坑人。还有一点接收端的滤波器必须和发送端一致这就是“匹配滤波器”的含义它能让采样时刻的信噪比最大化。如果你用了普通升余弦而不是根升余弦这里就得改成普通升余弦。发送根升余弦、接收根升余弦、收发级联等效为升余弦这是一个经典结论仿真程序里一定要保持这种匹配关系。4. 从仿真误码率验证程序正确性理论对比与常见偏差排查4.1 16QAM的理论误码率公式与仿真曲线绘制程序能跑是一回事跑得对不对是另一回事。最可靠的验证方式就是画误码率曲线和理论公式对比。16QAM在AWGN信道下误符号率SER的近似公式是P_symbol ≈ 3 × Q(√(4×EbN0/5))其中 Q(x) 是高斯Q函数EbN0是每比特信噪比。误比特率BER在格雷码映射下近似为 BER ≈ SER / 4因为一个符号错误大概率只导致1个比特错误。计算理论曲线的MATLAB代码很简单EbN0dB 0:2:20; EbN0 10.^(EbN0dB/10); theoreticalSER 3 * qfunc(sqrt(4 * EbN0 / 5)); theoreticalBER theoreticalSER / 4; figure; semilogy(EbN0dB, theoreticalBER, k-, LineWidth, 1.5); hold on;但仿真曲线怎么画才是重点。要得到一条完整曲线你需要对每个信噪比点执行“生成比特 → 调制 → 加噪 → 判决 → 解调 → 统计错误”并且每个点至少跑几十万个比特以上否则低误码率区域全是抖动没法跟理论值拟合。我自己的经验是在误码率低于 1e-4 的区域至少需要10/bitErrorRate个比特的仿真量否则误差会大到让你怀疑程序写错了。这个经验值可以记住做仿真时能省很多时间。4.2 仿真结果和理论值对不上时先查这三件事我在给朋友调试程序时发现误码率对不上理论值的原因90%以上出在三个地方按出现频率排序第一噪声功率算错了。这是最高频的错误。很多人直接设置noiseVar 1/SNR但这里的SNR到底是 Es/N0 还是 Eb/N0如果不统一曲线整体偏移而且偏移量恰好是 10×log10(4) ≈ 6dB。当你发现仿真曲线比理论曲线差了大约6dB的时候先检查单位换算大概率是 Es/N0 和 Eb/N0 搞混了。第二归一化没做或者做错了。星座点不归一化信号功率不是1同样会导致信噪比和设定值不一致。特征同样是曲线偏移但偏移量不一定正好是6dB。验证方法很简单在加噪声之前先打印mean(abs(symbols).^2)归一化良好的系统这个值应该是1。第三滤波器延迟补偿不对。这个错误的表现比较有意思——单独看某个信噪比点似乎“差不多”但整体曲线的斜率不对尤其在高信噪比区域差得特别多。因为采样时刻不对等效于引入了码间干扰噪声变大的时候这种干扰被噪声淹没看起来反而没那么明显。查这个问题的方法在无噪声条件下跑一次接收端看采样后的星座图是否完美叠到理想星座点上。如果叠加后还有明显散落那就是延迟补偿或者采样时刻有误。5. 程序里的工程化设计从“能跑”到“能用的仿真平台”5.1 模块拆分与参数集中管理初学者写程序容易把所有代码堆在一个脚本里好处是直观坏处是一旦开始做参数扫描、性能对比你会发现改一个参数要满世界找好几处而且每找一个地方就可能引入一个新的不一致错误。我的建议是哪怕只是课程设计级别也把程序拆成几个函数——比特生成、调制映射、脉冲成型、加噪声、匹配滤波、判决解映射、误码率统计。每个函数只干一件事参数统一从主脚本传入。这样拆的好处是你可以单独测试每一个模块。比如先验证映射表输入固定的比特组合观察输出符号坐标是否正确再验证成型滤波器输入一个单符号观察输出波形是否在符号时刻精确等于符号值。分模块测试能大大降低联调阶段的调试难度。参数集中管理的意思是在主脚本最前面用一个结构体或者配置函数把所有可调参数列出来至少包括调制阶数M、符号数、过采样率、滚降系数、Eb/N0范围、每信噪比点的最小比特数。这样你要做“滚降系数对比实验”的时候只要改一处就可以不用满篇找。5.2 性能统计模块不只是算一个误码率数字完整的性能统计应该包含三样东西误码率BER、误符号率SER和星座图误差向量幅度EVM。前两个是最终性能指标第三个是中间的信号质量指标特别是当你加入信道均衡、相位噪声等模块时EVM 能直观地告诉你信号质量在哪个环节恶化了。EVM的计算方式判决后的符号与理想星座点之间的误差向量除以理想星座点的平均幅度。MATLAB里可以写成% 假设 rxSymbols 是采样判决前的符号序列mapTable是归一化理想星座点 % 先做最小距离判决找到理想点 [~, idealIdx] min(abs(rxSymbols - mapTable), [], 2); idealSymbols mapTable(idealIdx); errorVector rxSymbols - idealSymbols; EVM_rms sqrt(mean(abs(errorVector).^2) / mean(abs(idealSymbols).^2)) * 100; fprintf(RMS EVM %.2f%%\n, EVM_rms);EVM 和 BER 是互相关联的指标但视角不同。EVM 更偏硬件实现层面的度量——功放的非线性失真、本振的相位噪声、IQ不平衡这些非理想因素都会反映在 EVM 上而且可以定位到是幅度误差还是相位误差。你在仿真程序里养成统计 EVM 的习惯之后以后转到实机测试平台上思路是通用的。5.3 从仿真平台到硬件实现的过渡思路写完了MATLAB仿真很多做课程设计的同学会问接下来呢这一步该怎么往实际系统走我给一个比较实际的路线参考。第一步把基带调制和解调的浮点程序用定点化改造。MATLAB的fi对象可以帮你做定点仿真核心决策是给I路、Q路幅度各分配多少bit滤波器系数分配多少bit。16QAM的星座点幅度只有{1, 3}两种其实对定点化非常友好一般I/Q各用6位定点就足够了。第二步用C语言重写一遍核心调制解调模块数据用整型这个过程能帮你搞清楚哪些运算在硬件上是“昂贵”的。比如除法尽量换成移位平方根尽量用查表滤波器系数尽量用2的幂次近似。这些优化在MATLAB里完全不需要关心但正是实现在DSP或FPGA上的关键。第三步如果感兴趣可以用FPGA实现一个实际的16QAM发射器。XiLinx的System Generator或者AMD官方的Vivado HLS都能把MATLAB算法快速转成硬件逻辑。到了这一步你才会真正理解为什么映射表要写成LUT、为什么滤波器要选系数对称的结构——这些在仿真平台里都体现不出来。6. 进阶玩法从固定映射到自适应调制与定时同步6.1 扩展方向1加入Gardner定时同步算法之前热搜词里有“16qam gardner”我猜是有朋友在做接收端的符号定时同步。这个方向确实有意义因为真实系统中接收端并不知道每个符号的精确起止时刻尤其是信道存在延时、频偏和多径时采样时刻会漂移。Gardner算法是一种经典的、不需要知道载波相位的定时误差检测算法特别适合QAM这类信号。Gardner算法的核心思想是每个符号周期内额外采样两个点一个在符号中间时刻一个在符号边界左右。利用相邻符号的中间采样点和边界采样点之间的关系构造出定时误差信号然后驱动一个插值滤波器去调整采样时刻直到误差信号趋近于零。16QAM和BPSK/QPSK不同它的幅度有多个电平所以Gardner的误差检测公式里要把符号中间点与左右符号平均值的差做共轭相乘形式稍微复杂一些但核心思想不变。建议你先搭好基带仿真链路再叠加Gardner环每一步都单独验证。6.2 扩展方向2与LDPC/BCH信道编码结合观察编码增益16QAM调制本身不解决误码问题它只负责高效地传比特。但实际系统里信道噪声和衰落会让部分比特出错所以前面通常要接一个信道编码器LDPC、BCH、卷积码等。热搜词里提到的“bcm编码调制”其实就是BICM比特交织编码调制把编码、交织、调制三级级联起来这是现代无线通信系统Wi-Fi、LTE、DVB的标准发射端结构。在你已有的16QAM程序上扩展BICM链路其实不难在比特生成之后加一个编码器MATLAB通信工具箱有现成的LDPC和卷积码函数在解映射之前加一个软解调器把每个比特的对数似然比LLR计算出来喂给译码器。当你对比“未编码16QAM”和“编码16QAM”在相同信噪比下的BER时能看到信道编码带来的编码增益——通常几个dB的改善。这个实验做完你对“调制、编码是不可分割的整体”这句话的理解会深入很多。6.3 扩展方向3单相逆变器SPWM与16QAM有什么异同热搜词里出现了“spwm调制”和“单相逆变器调制方式”那顺便提一嘴因为这两个“调制”字面上一样本质完全不同。SPWM正弦脉宽调制是功率电子领域的技术用三角载波和正弦参考波比较产生一系列脉宽可变的开关信号目的是让逆变器输出电压的基波接近正弦。它调制的是“开关器件的导通时间”跟数字通信里的16QAM调制——把比特映射为星座点——完全是两个维度的东西。如果你带着通信的背景去查SPWM很容易被名字带偏心里要有个数。7. 写16QAM程序时的几个实用建议与常见问题速查7.1 调试顺序建议按照如下顺序能省一半时间我的调试顺序是固定的先看映射表输出再看无噪声接收再看加噪接收最后才跑误码率曲线。每一步都验证无误了再进下一步。无噪声接收这一步特别容易被跳过但恰恰是这一步能暴露最多问题。在无噪声条件下接收端采样到的点必须和理想星座点完全重合仿真环境是理想信道不存在其他失真。只要有一点偏移就说明滤波器延迟、采样时刻或者上下变频的相位处理有bug这种问题在加噪声的时候完全看不出来因为误码率已经被噪声主导了。7.2 关于MATLAB版本和工具箱依赖上面代码里用到的rcosdesign和bi2de、de2bi需要通信工具箱Communications Toolbox不是MATLAB基础包自带的。如果你的环境没有这个工具箱可以自己写一个根升余弦滤波器的系数函数公式不复杂网上也容易找到实现用普通的fir1也能逼近。bi2de和de2bi也可以用矩阵乘法自己替代或者用sum(bits .* [8 4 2 1])手动转。如果是学生党学校正版授权一般包含了通信工具箱直接拿来用最省事如果是自己在家装的家庭版大概率没有通信工具箱需要注意这一点。7.3 常见问题速查表现象可能原因排查方法星座图明显偏移不是标准格状映射表坐标写错或象限搞反打印映射表逐点核对星座图格状正确但整体偏大/偏小归一化因子错误检查mean(abs(symbols).^2)是否为1星座图点周围有散落且无噪声滤波器延迟补偿错误无噪声条件下查看接收星座图误码率曲线整体偏差约6dBEs/N0和Eb/N0混淆检查噪声方差公式高信噪比区域误码率下不去码间干扰成型滤波或采样时刻问题增大过采样率、检查匹配滤波器7.4 最后的个人经验给刚接触数字通信仿真的朋友一个忠告不要把仿真程序当成“算个结果交差”的工具它其实是你理解通信系统的第一实践平台。每当你困惑某个概念——什么叫码间干扰、什么叫匹配滤波、为什么格雷码能降低误码率——最有效的方式是改几行代码亲手把这个问题“造”出来再“消”掉它。这个过程比啃十遍教材都管用。我写这篇整理的时候重新跑了一遍当初的程序还是会被那几个关键细节打动归一化因子错了性能就崩匹配滤波忘加误码率高到离谱延迟补偿漏掉星座图就看着像“雾化”了。这些细节单独拎出来都简单连在一起就是一个完整的、能用的16QAM通信系统。希望这篇东西能帮你少走一些我当时走过的弯路把更多时间花在真正有意思的进阶玩法上。本文还有配套的精品资源点击获取
返回列表