尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

QPSK调制解调实战:星座图旋转、载波同步与定时同步

QPSK调制解调实战:星座图旋转、载波同步与定时同步 简介四相相移键控QPSK调制解调完整仿真代码适合通信工程专业学生、数字通信课程学习者以及无线通信算法工程师参考学习。内容围绕QPSK信号生成、频谱分析与同步解调等核心环节通过改变载波相位来映射两位比特具有较高频谱效率是理解现代数字调制的重要案例。rar压缩包内共18个m文件体积仅8KB均为MATLAB脚本覆盖序列生成、串并变换、载波调制、扩频解扩、低通滤波和误码测试等功能模块还包含用于傅立叶变换与波形绘制的辅助函数。代码结构清晰、注释简洁便于按步骤复现QPSK信号从生成到解调的完整链路直观观察星座图、时域波形与频谱特性深入理解I/Q调制、载波恢复以及判决门限设置等关键细节。资源已有152人学习下载既可作为课程设计、毕业设计仿真底稿也可作为自学入门样例帮助初学者快速掌握四相相移键控技术的原理与实现方法。1. QPSK信号调制为什么你解出来的星座图总在转做无线通信仿真或调试SDR链路时QPSKQuadrature Phase Shift Keying正交相移键控是最常见的起点。它把两个比特映射成四个相位之一频谱效率高、实现简单但真正动手做一次信号调制而不只是调用modulate函数时很多人会发现星座图在转、误码率下不去、频谱包络对不上理论值。这些问题大多不是算法没学懂而是调制实现里的采样率、符号映射、载波频率和滤波器参数没有对齐。这篇内容围绕QPSK信号调制的完整链路展开从比特到星座点的数学映射讲起给出可直接运行的Python正交调制实现再落到解调端的载波同步和定时同步。适合正在做通信仿真、准备用SDR发射QPSK信号或者做FPGA调制器开发的人。你会看到怎么设采样率、怎么选载波、怎么判断星座图旋转的根源而不是只得到一个能跑的脚本。2. QPSK调制原理与映射关系从比特到星座点的数学2.1 QPSK的I/Q双通道调制结构QPSK信号可以看作两个正交BPSK信号的叠加。一个通道叫I路In-phase一个叫Q路Quadrature两路载波相差90度。把二进制的比特流每两个比特分成一组其中一个比特控制I路另一个控制Q路两路分别做BPSK调制后相加就得到了QPSK信号。数学上一个QPSK符号可以写成[ s(t) I(t) \cos(2\pi f_c t) - Q(t) \sin(2\pi f_c t) ]其中 ( I(t) ) 和 ( Q(t) ) 是基带脉冲成型后的幅度取值通常是 (A) 或 (-A)。四个相位落在 ( \pi/4, 3\pi/4, 5\pi/4, 7\pi/4 )即45°、135°、225°、315°正好对应两路BPSK的四种组合。你也可以旋转45度映射到0°/90°/180°/270°但大多数标准都采用第一种因为这样I/Q两路的符号判断正好对应格雷码的相邻位。2.2 格雷映射与星座图判定的边界格雷映射的核心思想是相邻相位只差一个比特这样在噪声影响下误判成相邻星座点时只错一个比特而不是两个。QPSK最常用的映射关系如下输入比特 (b0 b1)I 路幅度Q 路幅度相位001145°011-1315°11-1-1225°10-11135°注意这里映射顺序不是简单的二进制递增而是按格雷码排列00、01、11、10保证相邻相位之间只有一位不同。实际工程里如果直接用00、01、10、11的顺序映射误码率在高信噪比下会差大约0.5dB因为误判到对角星座点时会错两个比特。判定相位时接收端只需要比较I路和Q路的符号。大于0判为1小于0判为-1。这是硬判决。但如果I/Q通道之间有幅度不平衡或者正交偏差判决边界就会倾斜误码就多了。这也是为什么后面要讲I/Q不平衡补偿。2.3 载波信号频率一般设为多少“载波信号一般为多少”这个问题没有固定答案取决于系统设计。做仿真时载波频率必须满足一个条件远大于符号速率同时不能超过采样率的一半。常见做法是取采样率的1/4到1/6比如采样率4MHz载波设1MHz左右。这样既保证信号不靠近直流避免直流偏置和低频噪声干扰又让每个载波周期能采到足够的点便于后面的I/Q解调。如果载波频率太接近符号速率基带信号的频谱和载波的边带会混叠如果载波频率太高逼近奈奎斯特频率采样点太少解调时定时同步会很困难。我一般会先把符号速率定下来再决定载波频率而不是反过来。3. 用Python从零实现QPSK调制器波形生成与参数设置3.1 最小可运行代码生成QPSK基带信号先不看滤波器也不乘载波只生成基带的I/Q符号序列。这段代码是最小可运行的QPSK调制核心import numpy as np # 参数设置 sps 8 # 每个符号的采样点数与后续成型滤波有关 symbols 1000 # 发送符号数 bits_per_symbol 2 # 生成随机比特流 bits np.random.randint(0, 2, symbols * bits_per_symbol) # 按格雷码映射成I/Q幅度 mapping { (0, 0): (1, 1), (0, 1): (1, -1), (1, 0): (-1, 1), (1, 1): (-1, -1), } i_symbols np.zeros(symbols) q_symbols np.zeros(symbols) for n in range(symbols): b0 bits[2*n] b1 bits[2*n 1] i_val, q_val mapping[(b0, b1)] i_symbols[n] i_val q_symbols[n] q_val这段代码先把随机比特按每组两位拆开然后通过字典映射得到I路和Q路的幅度。注意这里的mapping字典用的键是(b0, b1)值是对应的I、Q幅度。用字典的好处是映射关系一目了然后续想换成非格雷码只改这里就行。i_symbols和q_symbols还是符号级的序列每个符号只有一个值还不是真正的波形。要让它们变成带限信号还需要过采样和成型滤波这就是下一步。3.2 关键参数符号速率、采样率、滚降系数怎么定符号速率baud rate决定了信号的传输速率。比如符号率是1Msps每个符号带2比特那么比特率就是2Mbps。采样率必须大于符号速率的2倍工程上通常取4到16倍。如果每个符号采样点数太少后面的匹配滤波和定时同步会很难做太多则运算量大滤波器阶数也高。成型滤波用升余弦或根升余弦滤波器滚降系数roll-off factor是一个核心参数。它决定信号的频谱占用和过冲特性滚降系数 α占用带宽符号率R过冲典型场景0.11.1R很大卫星通信带宽受限0.251.25R较大常规数字微波0.51.5R中等一般测试验证1.02R很小教学演示OQPSK实际调试中α越小频谱越紧凑但滤波器的冲击响应衰减慢定时抖动敏感度更高匹配滤波后的眼图开口更小。第一次做QPSK仿真时建议用0.35到0.5先让链路跑通再往紧凑方向压。用根升余弦滤波器做脉冲成型时滤波器系数可以用commpy或者scipy.signal手动构造。下面给出一个不用专用库的实现from scipy import signal def rrc_filter(alpha, span, sps): 生成根升余弦滤波器系数 N span * sps 1 t np.arange(N) - span * sps / 2 t t / sps # 归一化到符号周期 h np.zeros_like(t) for i, ti in enumerate(t): if ti 0.0: h[i] 1.0 - alpha 4 * alpha / np.pi elif np.abs(np.abs(ti) - 1/(4*alpha)) 1e-8: h[i] alpha/np.sqrt(2) * ((12/np.pi)*np.sin(np.pi/(4*alpha)) (1-2/np.pi)*np.cos(np.pi/(4*alpha))) else: num np.sin(np.pi*ti*(1-alpha)) 4*alpha*ti*np.cos(np.pi*ti*(1alpha)) den np.pi*ti*(1-(4*alpha*ti)**2) h[i] num / den # 归一化 return h / np.sqrt(np.sum(h**2)) alpha 0.35 span 6 filter_coeffs rrc_filter(alpha, span, sps)这个滤波器的关键是让发射端和接收端各用一个根升余弦滤波器两者级联后等效于一个完整的升余弦滤波器能在采样点处消除符号间干扰。注意span是滤波器覆盖的符号个数取6到10比较合适。太小则截断误差大太大则信号延迟长。3.3 让信号上载波混频与正交调制实现基带符号经过成型滤波后需要上变频到载波频率。正交调制器用两路正交载波分别乘上I/Q信号再相加。做这个步骤的代码很简单但坑很多def qpsk_modulate(i_wave, q_wave, fc, fs): 正交调制I路乘cosQ路乘sin相加 n len(i_wave) t np.arange(n) / fs carrier_i np.cos(2 * np.pi * fc * t) carrier_q -np.sin(2 * np.pi * fc * t) qpsk_signal i_wave * carrier_i q_wave * carrier_q return qpsk_signal fs sps * 1000 # 假设符号率1000采样率8000 fc 2000 # 载波频率取采样率的1/4 i_up signal.upfirdn(filter_coeffs, i_symbols, upsps) q_up signal.upfirdn(filter_coeffs, q_symbols, upsps) # 补偿滤波器延迟 delay int(span * sps / 2) i_up i_up[delay:] q_up q_up[delay:] tx_signal qpsk_modulate(i_up, q_up, fc, fs)这里Q路载波用负正弦是因为前面公式里是减去Q路径的正弦分量。如果你写成加号星座图会镜像解调时I/Q翻转问题很隐蔽。另外upfirdn会引入滤波器延迟必须截掉前面span*sps/2个点否则发射信号的起始部分是滤波器填充的零频谱会不干净。还有一个容易出错的点载波频率必须能被采样率的整数分之一描述但不能是奈奎斯特频率的一半。比如fs8000时fc2000刚好是fs/4每个载波周期采4个点I/Q混频系数正好是1,0,-1,0的序列方便后续调试。实际系统里载波频率由硬件PLL决定仿真里这样选只是为了直观。4. 解调端同步问题为什么你的QPSK误码率高4.1 载波同步Costas环与频偏影响调制容易解调难。QPSK解调的第一步是载波同步。如果接收端的本地振荡器和发射端有频率偏差解调出来的星座图会持续旋转。频偏是几十Hz时星座图慢慢转频偏是几百Hz时星座图糊成圆圈完全没法判决。常见的载波同步方法分两类数据辅助和非数据辅助。非数据辅助里Costas环是QPSK的标准方案。它的误差信号用I/Q两路的硬判决结果来估计相位偏差# 简化版Costas环迭代逐符号 phase 0.0 for n in range(len(rx_symbols)): # 去旋转 I rx_symbols[n].real * np.cos(phase) rx_symbols[n].imag * np.sin(phase) Q -rx_symbols[n].real * np.sin(phase) rx_symbols[n].imag * np.cos(phase) # 硬判决 dI 1.0 if I 0 else -1.0 dQ 1.0 if Q 0 else -1.0 # 误差信号 error dI * Q - dQ * I phase 0.1 * error # 环路增益需按符号率调整这段代码里相位更新量等于环路增益乘以误差。环路增益太小则锁定慢太大则会抖。实际工程中需要按信噪比和符号率调可能在0.01到0.2之间。而且这个算法在QPSK里有四相位模糊即使锁定也可能旋转到任意一个90度倍数上需要差分编码或者在帧头里做相位解模糊。4.2 定时同步与匹配滤波除了载波相位接收机还必须知道每个符号的最佳采样时刻。这是定时同步要解决的问题。如果定时偏差是采样周期的一半眼图完全闭合误码率接近50%。最常见的定时同步算法是Gardner算法它不用数据辅助独立于载波相位适合QPSK。Gardner的误差公式是[ e (y_N - y_{N-1}) \times y_{N-1/2} ]其中 ( y_N ) 是当前符号采样点( y_{N-1} ) 是上一个符号采样点( y_{N-1/2} ) 是两者中间时刻的采样。当采样点正好落在符号峰值时中间点的期望值为零误差为零偏离时产生正负误差信号去调整插值时刻。实际实现中接收端先对I/Q信号做根升余弦匹配滤波再用Gardner环和插值器完成定时恢复。匹配滤波器和发射端的成型滤波器系数相同这也是为什么前面说根升余弦要分在两端用。如果发射端用了升余弦而接收端也用升余弦整体就不是奈奎斯特响应符号间干扰会显现出来。4.3 星座图旋转诊断从现象反推故障看到星座图异常时先不要调环路参数先观察现象。第一种星座图逆时针匀速旋转。这是载波频偏的典型表现。频偏方向决定旋转方向频偏大小决定旋转速度。解决方法是先做粗频偏估计比如用帧前导的已知符号做相关估计把残余频偏压到环路可以接受的范围。第二种星座图不转但四个点分别拉成四团椭圆斑。这是定时同步或滤波器失配的问题。检查接收端滤波器的采样相位或者看眼图有没有闭合。也可能是发射端加了滚降系数很大的滤波器接收端却用了匹配系数。第三种星座图只出现两个点另外两个点重叠。这说明I/Q两路中有一路幅度为零通常是发射端I路或Q路的信号延迟没对齐或者混频时少了一路载波。检查I/Q波形的时域图确认两路波形长度一致、延迟一致。第四种星座图整体旋转45度也就是说点出现在轴正负方向上。这是本地载波相位偏了45度且环路还没开始收敛。等锁稳定后应该会转回来。如果始终锁在45度说明Costas环没有正常工作看误差信号是否周期性过零。5. 实战技巧用SDR或仿真验证QPSK调制链路5.1 用GNURadio搭一个QPSK收发流图手写代码验证了算法再用SDR平台做一次无线路测是完整流程。GNURadio里做QPSK收发的流图结构很固定我已经搭过多次关键节点如下发射端Random Source → Chuck按2比特分块 → Constellation Encoder选QPSK格雷映射 → Root Raised Cosine Filter滚降系数0.35每符号采样数8 → Throttle → UHD USRP Sink接收端UHD USRP Source → Root Raised Cosine Filter和发射端同样系数 → Symbol Sync算法选Gardner检测器选Mueller Muller环路带宽0.05 → Costas Loop环路带宽0.02顺序2阶QPSK → Constellation SinkSymbol Sync模块的参数中Samples per Symbol必须和发射端的Samples per Symbol一致。如果这里配错定时同步完全无法收敛。Costas Loop的Order选项里QPSK要用2或4不是1和3——1和3是BPSK/OOK用的。5.2 眼图和EVM验证调制质量最后一步验证不是看误码率而是看眼图和EVM。EVM误差矢量幅度是衡量调制质量的核心指标它计算理想星座点和实际接收符号之间的误差。5G NR标准里256QAM要求EVM低于3.5%QPSK通常要求低于17.5%但自己验证时EVM低于10%就算调制链路正常。在GNURadio里加一个Constellation Sink勾选EVM选项就能直接看到数值。如果是自己写PythonEVM的计算方式如下# 假设rx_syms是归一化后的接收符号ref_syms是对齐后的理想符号 error rx_syms - ref_syms evm np.sqrt(np.mean(np.abs(error)**2) / np.mean(np.abs(ref_syms)**2)) * 100 print(fEVM: {evm:.2f}%)如果EVM高于20%优先检查接收信号功率是否在SDR ADC的最佳范围内。功率太低量化噪声大EVM恶化功率太高产生非线性失真星座图边缘的点会向内收缩。对于常见的SDR前端把接收增益调到信号幅度约为满量程的60%到70%时EVM通常最低。除了EVM眼图也是快速定位问题的好工具。QPSK的I路和Q路分别画眼图正常情况下在最佳采样时刻有四个清晰的开口。如果开口处线条很粗定时抖动大如果眼图中线交叉位置不是中心点说明滤波器系数有偏置。还有一个验证技巧把滚降系数从0.35改成0.15重复跑同一条链路观察EVM和频谱占用变化。如果EVM急剧恶化说明你的定时同步环路带宽与滤波器的群延迟不匹配需要把环路带宽调小同时增加滤波器的span参数。这组对比能帮你快速理解滚降系数和同步性能之间的权衡。本文还有配套的精品资源点击获取
返回列表