在 GNSS 定位项目中,GPS 信号异常是排查成本最高的一类问题。接收机可能上一秒还正常输出经纬度,下一秒就出现位置跳变、卫星数骤降、时间同步失败。很多项目开始时把这类现象笼统归为“信号不好”,但实际原因可能包括射频干扰、多径反射、天线故障、遮挡、电源纹波,甚至是附近设备产生的带外辐射。干扰在 GNSS 工程里是真实存在的风险:GPS 民用信号到达接收机时功率极低,只有 -130 dBm 左右,一个不合规设备发出的杂散信号就能让整颗接收通道失锁。这篇文章从工程实践角度,讲清楚如何用 SNR、NMEA 数据、chrony 时间同步和多传感器交叉验证,一步步定位 GPS 被干扰时的异常链路。
适合阅读这篇文章的读者包括:做嵌入式定位设备的开发者、给车辆或机器人配 GPS 的工程师、负责 NTP 时间同步的运维人员,以及刚接触 GNSS 数据质量分析的学生。文章不依赖特定硬件,但示例以 u-blox 接收机和 Linux 上位机为主,其他品牌接收机可以按同样的思路替换。
1. 先理解 GPS 为什么怕干扰,再排查异常才有方向
1.1 GPS 信号的功率太低,抗干扰余量有限
GPS 卫星距离地面约 20200 公里,民用 L1 C/A 码信号到达地面的功率密度通常只有 -130 dBm 左右,比 Wi-Fi 信号弱十几个数量级。接收机之所以能解调,依靠的是扩频码的增益:通过 1.023 MHz 的 C/A 码相关解扩,接收机能从噪声中把信号提出来。
扩频增益带来的好处是抗窄带干扰能力强,但这种能力有上限。当干扰信号功率超过接收机自动增益控制的线性范围,或者干扰频率落在 L1 频带内时,相关器输出会迅速恶化。工程上常见的现象是:接收机显示卫星数从 10 颗掉到 3 颗,定位精度从 2 米跳到 50 米,最后直接进入未定位状态。
理解这一点,排查时就不会只盯着定位算法,而是先检查接收信号的最前端:天线、馈线、供电、干扰源。
1.2 干扰不全是“有意干扰”,更多是工程环境中的无意干扰
GNSS 领域把干扰分为两类:
- 有意干扰:使用专门设备在 GNSS 频段发射信号,目的是让接收机失效,主要用于军事演练、管制区域测试等场景。这类干扰不是普通项目能控制的,只能通过抗干扰天线、滤波器和冗余定位来降低影响。
- 无意干扰:工程环境中更常见。例如车载电子设备产生的谐波、劣质 USB 摄像头辐射、变频器开关噪声、手机充电器杂散、同一设备上的 4G/LTE 模块天线距离 GPS 天线太近,都会让 GPS 接收机灵敏度下降。
排查时不要一开始就认定存在“外部恶意干扰”。实际项目中,多数信号异常来自天线摆放、线缆屏蔽和板级电磁兼容设计。
1.3 干扰导致的现象会层层传导
GPS 干扰不会只影响定位。GNSS 接收机在很多系统里同时承担定位和时间基准两个角色。
| 影响层 | 典型现象 | 后续后果 |
|---|---|---|
| 射频层 | C/N0 骤降,所有卫星 SNR 同时低于 20 dBHz | 接收机无法跟踪卫星 |
| 定位层 | 可见卫星数下降,HDOP 升高,位置跳变 | 导航轨迹明显漂移 |
| 时间层 | GPS 时间源失锁,PPS 丢脉冲 | NTP 同步失败,设备时间漂移 |
| 业务层 | 车辆无法定位、机器人停止导航、日志时间戳错乱 | 上层系统需要额外兜底 |
如果只修业务层,不断重启接收机,问题会反复出现。正确的排查顺序是从底层射频信号质量开始,逐层向上验证。
2. 建立一份 GPS 数据质量基线,先知道“正常”长什么样
2.1 NMEA 输出里真正需要关注的字段
GPS 接收机默认输出 NMEA 0183 语句。常见语句包括$GPGGA、$GPRMC、$GPGSA、$GPGSV。排查时不需要全部解析,重点看三条:
$GPGGA:定位状态、卫星数、HDOP、海拔。$GPGSA:定位模式、参与定位的卫星 PRN、PDOP、HDOP、VDOP。$GPGSV:每颗可见卫星的仰角、方位角、信噪比 SNR。
下面是一条典型的$GPGGA:
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47字段含义按顺序是:UTC 时间 12:35:19,纬度 4807.038 分,北纬,经度 01131.000 分,东经,定位质量 1(单点定位),使用 8 颗卫星,HDOP 0.9,海拔 545.4 米。
如果 fix quality 是 0,说明没有定位;如果是 2,说明使用差分定位;如果是 5,说明使用 RTK 固定解。排查干扰问题时,尤其要注意卫星数和 HDOP 同时变化的时刻。
2.2 用 SNR/C/N0 判断信号健康度
SNR 和 C/N0 在接收机数据中经常混用。严格说,C/N0 是载波噪声密度比,单位是 dBHz,反映信号强度与噪声密度之间的关系;u-blox 的 NMEA 输出中,$GPGSV最后一个字段通常就是每个通道的 SNR(dBHz)。
工程参考阈值:
| SNR(dBHz) | 信号状态 | 建议处理 |
|---|---|---|
| 大于 40 | 良好 | 正常跟踪 |
| 30 到 40 | 一般 | 可定位,但精度下降 |
| 20 到 30 | 较差 | 容易失锁,需要关注 |
| 小于 20 | 很差 | 基本无法正常定位 |
需要注意,单颗卫星 SNR 低可能是卫星仰角低,但所有卫星 SNR 同时下降,基本可以判定是天线、射频链路或干扰问题。
2.3 记录一周的正常数据作为对照基线
没有基线,就无法判断“当前数据是否异常”。在项目现场负责的设备上,建议连续采集 24 小时到一周的数据,记录:
- 可见卫星数随时间变化曲线。
- 每颗卫星的 SNR 分布。
- HDOP、PDOP、VDOP 的统计值。
- 定位状态切换次数。
- 天线位置周围的环境变化。
记录基线时,要选择天气稳定、周围没有施工和新增电子设备的时间段。基线的目的是建立正常范围,后续一旦出现异常,即可对比同一天同一时段的卫星几何分布,判断问题属于环境变化还是设备故障。
3. 从设备侧抓取信号异常数据,把现象变成可量化指标
3.1 硬件连接和串口参数
本文示例使用一块 u-blox NEO-M8N 模块,通过 UART 连接 STM32 或 USB 转串口模块到 Linux 上位机。接收机默认输出波特率常见为 9600 或 115200,使用前先确认模块配置:
- 串口波特率与上位机一致。
- 输出语句开启 GGA、GSA、GSV。
- 如果使用二进制 UBX 协议,需要关闭 NMEA 或同时输出。
接线要点:
- GPS 天线放置位置要远离大电流线、电机、无线模块天线。
- 有源天线需要 3.3V 或 5V 供电,检查模块是否已开启天线电源。
- 馈线不要与电源线捆在一起。
3.2 用 Python 解析 NMEA,实时记录信号指标
下面代码读取串口中的$GPGGA和$GPGSV,实时输出定位状态和卫星信噪比统计。
import serial import re def parse_gpgga(line): parts = line.split(',') if len(parts) < 15: return None try: return { 'time': parts[1], 'fix_quality': int(parts[6]), 'satellites': int(parts[7]), 'hdop': float(parts[8]), 'altitude': float(parts[9]), } except ValueError: return None def parse_gpgsv(line): parts = line.split(',') if len(parts) < 7: return [] sats = [] idx = 4 while idx < len(parts) - 1: try: prn = int(parts[idx]) elevation = int(parts[idx + 1]) azimuth = int(parts[idx + 2]) snr = int(parts[idx + 3]) if parts[idx + 3] else 0 sats.append({'prn': prn, 'elevation': elevation, 'azimuth': azimuth, 'snr': snr}) except (ValueError, IndexError): pass idx += 4 return sats ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1) all_snr = [] while True: line = ser.readline().decode('ascii', errors='ignore').strip() if line.startswith('$GPGGA'): gga = parse_gpgga(line) if gga: print('time=%s fix=%d sats=%d hdop=%.1f' % ( gga['time'], gga['fix_quality'], gga['satellites'], gga['hdop'])) elif line.startswith('$GPGSV'): sats = parse_gpgsv(line) snr_list = [s['snr'] for s in sats if s['snr'] > 0] if snr_list: avg_snr = sum(snr_list) / len(snr_list) all_snr.extend(snr_list) print(' gsv_sats=%d avg_snr=%.1f max_snr=%d min_snr=%d' % ( len(snr_list), avg_snr, max(snr_list), min(snr_list)))正常环境下输出大致如下:
time=123519 fix=1 sats=9 hdop=0.9 gsv_sats=11 avg_snr=38.2 max_snr=46 min_snr=22 time=123520 fix=1 sats=10 hdop=0.8 gsv_sats=12 avg_snr=38.5 max_snr=47 min_snr=21如果出现下面这种输出,基本可以判定信号链路存在问题:
time=123521 fix=0 sats=0 hdop=99.0 gsv_sats=1 avg_snr=18.0 max_snr=18 min_snr=183.3 异常数据的典型特征
干扰导致的数据异常有几个明显特征,抓取数据后可以按特征分类:
- 所有卫星 SNR 同时下降:指向射频链路或外部干扰,不是单颗卫星的问题。
- 卫星数正常但定位质量差:可能是 HDOP 偏高,说明卫星几何分布差,常见于城市峡谷或靠近遮挡物。
- SNR 呈现周期性波动:怀疑附近有周期性工作的设备,比如电机、变频器、雷达。
- 上电前几分钟正常,随后恶化:可能是接收机或天线自身发热导致,也可能与周边设备间歇工作有关。
抓取数据时,建议同时用手机录下现场环境变化,标注时间点,方便之后与日志对应。
4. 判断干扰来源:从现象倒推到根因
4.1 先排除环境遮挡和多径,再考虑射频干扰
很多时候信号变差并不是干扰,而是天线被遮挡或者多径反射。可以用下面的表格快速区分:
| 现象 | 遮挡 | 多径 | 射频干扰 |
|---|---|---|---|
| 卫星数 | 减少,但剩余卫星 SNR 正常 | 正常或略少 | 所有卫星 SNR 同时大幅下降 |
| 低仰角卫星 | 先丢失低仰角卫星 | 低仰角卫星 SNR 异常偏高后跳变 | 与仰角无关,所有通道一起变差 |
| 定位结果 | 精度下降 | 位置频繁跳向反射面方向 | 直接失锁或无法定位 |
| 时间规律 | 与位置移动相关 | 与周围建筑相关 | 与干扰设备工作时间相关 |
| 换天线后 | 无改善 | 可能改善 | 如果干扰来自天线附近则改善,来自外部则无改善 |
其中多径的特征最难判断:反射信号叠加在直达信号上,会让 SNR 看起来偏高,但载波相位发生畸变,导致定位结果来回漂移。此时可以用 C/N0 和伪距残差联合判断,普通项目里最简单的方法是改变天线位置,观察定位是否稳定。
4.2 检查天线、馈线和供电
天线问题比外部干扰更容易排查,也更容易被忽略。按下面顺序检查:
- GPS 天线是否放在室外或窗边,周围是否有金属遮挡。
- 馈线是否有破损,SMA 接头是否拧紧。
- 有源天线供电是否正常,测量天线电源电压。
- 天线是否与 Wi-Fi、4G、对讲机天线距离过近,建议至少保持 30 厘米以上间距。
- 接收机供电是否稳定,劣质 USB 电源的纹波会影响接收机内部 LNA 工作点。
如果以上都正常,再考虑外部干扰源。
4.3 用频谱分析和 u-center 定位干扰源
有条件的话,使用频谱仪观察 L1 频段(1575.42 MHz 附近)是否存在异常宽带信号。没有频谱仪时,使用 u-blox 的 u-center 软件即可完成初步分析:
- 连接接收机,打开 GSV 视图,观察所有卫星 SNR。
- 打开 UBX-MON-SNR 或 UBX-RXM-RAW,查看原始通道电平。
- 把天线朝向不同方向,观察 SNR 是否随方向变化。
- 关闭附近可疑设备,逐个排查。
需要说明的是,普通接收机无法直接测出干扰信号强度,以下现象只能作为备查依据:
| 观测结果 | 可能原因 |
|---|---|
| 所有通道 SNR 同时下降,且在天线附近找到可疑发射设备 | 外部干扰 |
| 只有某个频率范围部分通道受影响 | 接收机带宽内窄带干扰 |
| 换天线后 SNR 恢复正常 | 天线或馈线问题 |
| 断开电源后 SNR 恢复 | 接收机自身 EMC 问题 |
排查外部干扰时,尽量记录时间、地点、设备工作状态。现场数据越完整,定位根因就越快。
5. GPS 时间同步异常时,用 chrony 从时间链路排查
5.1 GPS 同时是定位源和时间源
很多系统里,GPS 接收机通过 PPS 脉冲和串口时间信息给 NTP 服务提供时间参考。干扰发生时,PPS 脉冲中断或时间信息不可用,NTP 系统就会失去参考源。
在 Linux 系统中,GPS 通常通过gpsd或 PPS 工具接入 chrony。常见配置如下:
refclock PPS /dev/pps0 refid PPS prefer refclock SHM 0 offset 0.5 refid GPS第一条使用 PPS 设备作为首选参考源,第二条使用共享内存读取 GPS 时间。
5.2 chronyc 命令如何看 GPS 状态
排查时间同步问题,主要看三个命令。
查看同步源状态:
chronyc sources -v正常输出示例:
210 Number of sources = 2 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== #* PPS 0 4 377 18 -127ns[ -232ns] +/- 237ns #? GPS 0 4 377 18 +12us[ +12us] +/- 1ms#*表示当前使用的参考源,#?表示不可达,Reach值接近 377 表示最近 8 次轮询全部成功。如果Reach为 0,说明参考源已经丢失。
查看时间校正状态:
chronyc tracking重点看Last offset和RMS offset。正常情况下 PPS 参考的 offset 应该在微秒或亚微秒级。如果 offset 突然增大到毫秒级,可能是 GPS 失锁期间时钟自由运行,或者 PPS 信号本身抖动。
查看每个参考源的统计数据:
chronyc sourcestats当 GPS 被干扰时,chronyc sources中 GPS 源会从#*变成#?,PPS 源如果也依赖 GPS 则同样失锁,整个系统会退化为本地时钟。
5.3 干扰期间的保持策略
GPS 时间源失锁后,系统不会立刻停止工作,而是依赖本地振荡器保持。保持能力取决于硬件:
- 普通晶振:每天漂移可能达到几十毫秒。
- OCXO(恒温晶振):每天漂移可能在微秒到毫秒级别。
- 铷钟:保持能力更强,但成本高。
因此,工程策略不是让系统在 GPS 干扰下继续保证纳秒级精度,而是:
- 检测 GPS 失锁,立刻发出告警。
- 切换到备份时间源,例如另一台 NTP 服务器或本地高稳晶振。
- 记录失锁时刻和持续时间,事后分析干扰窗口。
- GPS 恢复后,观察
Reach恢复为 377 且 offset 稳定后再恢复正常同步。
在 purely 学习环境中,可以用一个开关模拟 GPS 断线,验证 chrony 的 failover 逻辑是否正常。
6. 用多传感器交叉验证,避免被 GPS 单点误导
6.1 只靠 GPS 判断干扰,容易误报
GPS 信号异常并不一定都是干扰。车辆进入隧道、高楼密集区、树木遮挡,都会出现卫星数下降。如果系统只有 GPS 一个传感器,很难区分“外部环境造成”还是“接收机故障”。
此时需要引入 IMU、摄像头、激光雷达等其他传感器做交叉验证。常见思路是:
- 对比 GPS 位置变化与 IMU 积分位移,判断位置跳变是否真实。
- 对比 GPS 速度与轮速传感器,确认速度跳变是否可信。
- 对比 GPS 时间与系统 RTC,判断时间源是否异常。
6.2 四类传感器的质量评估指标
针对 camera、lidar、imu、gps 四类传感器,可以分别建立质量指标:
| 传感器 | 常用质量指标 | 异常表现 |
|---|---|---|
| GPS | 卫星数、HDOP、SNR、fix quality | 卫星数骤降,HDOP 突变 |
| IMU | 零偏稳定性、Allan 方差、加速度/角速度范围 | 数据跳变、饱和、温度漂移 |
| Camera | 图像模糊度、曝光时间、特征点数量 | 过曝、欠曝、运动模糊 |
| Lidar | 点云数量、距离分布、反射强度 | 点云稀疏、距离异常、强度偏低 |
交叉验证的典型逻辑是:GPS 显示位置跳变 50 米,但 IMU 积分位移表明车辆实际只移动了 2 米,则 GPS 跳变大概率来自信号异常,而不是真实运动。
6.3 建立异常窗口记录,为事后分析留证据
在自动驾驶、机器人和测量设备中,建议记录一个统一的时间对齐数据包:
- 时间戳采用同一时钟源。
- 每条 GPS 数据附带 fix quality、卫星数、HDOP、平均 SNR。
- 同时记录 IMU 和轮速数据。
- 异常触发时,自动保存异常窗口前后各 30 秒的原始数据。
这样,即使干扰是间歇性的,也能在事后回放中还原现场。
7. 工程落地:学习环境快速验证,生产环境再加防护
7.1 学习环境怎么快速跑通
学习阶段不需要复杂设备,推荐最小组合:
- 一块支持 NMEA 输出的 GPS 模块,价格不高。
- 一个 USB 转串口模块。
- 一台安装 Python 和 pyserial 的电脑。
- 有条件的话,加一个 u-blox 接收机配合 u-center 软件。
操作顺序:
- 先把模块放到窗边,确认能定位。
- 运行上面的 Python 脚本,采集 30 分钟正常数据。
- 把模块移入室内或放在金属盒附近,观察 SNR 变化。
- 打开一个调频对讲机靠近天线,观察 SNR 是否会下降。
- 把观察结果和采集到的数据做对比。
这个过程可以让新手直观理解 SNR、HDOP、卫星数和干扰之间的关系。
7.2 生产环境必须补充的防护措施
生产环境不能只依赖一套裸 GPS 方案。建议至少增加以下保障:
- 天线选型:选择带滤波、带抗干扰能力的有源天线,安装位置远离射频模块。
- 板级隔离:GPS 天线馈线要走独立路径,与数字电路和电源保持距离。
- 冗余参考:时间系统配置至少两个参考源,GPS 加 NTP 或本地高稳晶振。
- 状态上报:接收机定时上报 fix quality、卫星数、HDOP、平均 SNR,异常时告警。
- 日志留存:保存原始 NMEA 日志,便于事后分析。
- 版本冻结:接收机固件和上位机解析库要固定版本,避免升级引入行为变化。
7.3 抗干扰不是把所有希望压在接收机上
接收机内部的抗干扰能力有限,尤其是在低成本模块上。真正有效的方案是系统级设计:天线位置优化、射频前端滤波、干扰检测、传感器融合、时间源冗余。任何一个环节缺失,都可能导致 GPS 受干扰时整个系统瘫痪。
对于时间敏感的业务,尤其要注意:GPS 干扰不是“定位没了重试一下”的问题,而是会直接破坏系统时间基准,影响日志排序、交易时间、数据一致性。因此,时间链路的监测和 failover 必须提前设计。
8. 常见问题排查表和可复用检查清单
8.1 一张表覆盖常见 GPS 干扰现象
| 问题现象 | 常见原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| 所有卫星 SNR 同时下降 | 外部射频干扰 | 查看 GSV 全部通道 SNR,对比不同方向 | 排查附近发射设备,调整天线位置 |
| 卫星数正常但无法固定解 | HDOP 偏高,卫星几何分布差 | 查看 GSA 中的 PDOP/HDOP | 移动到开阔环境,或加天线数量 |
| 定位结果跳变,SNR 正常 | 多径反射 | 改变天线位置观察定位是否稳定 | 安装抗多径天线,或采用 RTK |
| 开机有信号,一段时间后失锁 | 天线供电不稳或接收机发热 | 测量天线电源电压,观察温度 | 改善供电,增加散热 |
| GPS 时间源在 chrony 中不可达 | GPS 失锁或 PPS 信号中断 | 执行chronyc sources -v查看Reach | 检查 GPS 状态,配置备份时间源 |
| 换到室内后 SNR 降为 0 | 天线被遮挡或模块无信号 | 查看 GSV,确认可见卫星状态 | 天线移出室外,或用外部有源天线 |
8.2 发布前的 GPS 抗干扰检查清单
以下清单可以直接用于设备发布、项目上线前的自检:
- GPS 天线安装位置是否避免与 4G/Wi-Fi/对讲机天线过近。
- 馈线是否使用屏蔽线,接头是否拧紧。
- 有源天线供电是否正常,电压是否在模块规格范围内。
- 接收机输出是否开启 GGA、GSA、GSV 语句。
- 是否已采集至少 24 小时基线数据,并保存了平均 SNR 和 HDOP 范围。
- 是否配置了失锁告警,告警阈值是否明确。
- 时间系统是否有 GPS 之外的备份参考源。
- 是否验证过 GPS 断开后 chrony 能自动切换并恢复正常。
- 是否保存原始 NMEA 日志,日志是否带 UTC 时间戳。
- 是否测试过附近常见设备开启时 SNR 的变化。
8.3 落地时的最后一个建议
GPS 干扰排查不能只依赖抓包和看日志。先建立基线、再量化异常、最后逐层排除,是一条通用链路。无论是嵌入式定位、车辆导航还是时间同步服务,这套方法都适用。对于刚接触 GNSS 的开发者,建议从一块廉价接收机和一段串口采集脚本开始,把正常数据、异常数据和干扰源之间的对应关系真正跑一遍,之后再上复杂的抗干扰设计。这种基础判断能力,比直接套用昂贵的抗干扰硬件更有长期价值。