简介本资源是面向计算机、电子信息工程及数学等专业本科生的GNSS反欺骗技术实践工具包聚焦观测数据级信号鉴别与防御算法实现适用于课程设计、期末大作业及毕业设计等中阶科研实践场景。压缩包共63个文件含35个核心Matlab函数.m、9张结果可视化图.png、4组实测观测数据.obs、3组卫星位置文件.pos及多格式导航电文.nav/.rnx/.ubx等完整覆盖数据加载、伪距/载波相位解析、多径与欺骗特征提取、一致性检验等关键流程总大小11.48MB。代码采用参数化设计变量命名规范、注释详尽所有模块均支持Matlab 2014a至2024a跨版本直接运行附赠案例数据开箱即用。目前已有33人下载学习配套清晰的目录结构与工程化组织方式便于理解反欺骗算法底层逻辑、快速复现论文方法并开展自主实验拓展。 观测数据级全球导航卫星系统反欺骗库附Matlab代码是我在处理接收机安全测试时整理的一套方法集合核心思路很简单不依赖额外硬件只用接收机输出的观测数据就能把大多数欺骗信号识别出来。这套东西对无人机、车辆导航、高精度定位设备的安全评估都适用也适合刚接触GNSS安全方向的研究者做入门参考。先说结论欺骗干扰没有想象中那么神秘但它确实能悄无声息地让一辆车的导航偏出去几百米也能让一架无人机在悬停时被“诱导”到错误坐标。更麻烦的是很多接收机在受到欺骗时并不会主动报错因为它们还在正常跟踪卫星导航解也在正常输出只是这个“正常”是假信号构造出来的。观测数据级反欺骗就是在这种“看起来一切正常”的情况下通过分析观测数据内部的一致性把异常揪出来。我最初做这个方向是因为一个实际需求帮朋友评估一款工控级接收机在复杂电磁环境下的可靠性。当时手头没有欺骗信号源也没有昂贵的信号质量监测设备唯一能用的就是接收机本身输出的观测文件。于是我开始尝试只用伪距、载波相位、多普勒、载噪比这些常规数据做检测。折腾了几个月把能想到的检测量都过了一遍最后沉淀成了一个可复用的方法与代码库。1. 方法库的整体思路与应用场景1.1 为什么选择观测数据级而不是硬件级或解算级反欺骗检测在业界一般分三个层次信号处理级、观测数据级、导航解级。信号处理级最彻底直接分析射频信号的特征比如到达角、极化方式、相关性峰的形状能对付大多数欺骗源但实现门槛高一般要改接收机前端或者外接专用设备。导航解级最容易实现直接盯着位置、速度、时间输出做跳变检测但灵敏度太低现在稍微像样点的欺骗源都会做平滑过渡位置输出根本不会出现明显跳变。观测数据级刚好卡在中间输入是接收机输出的观测原始量伪距、载波相位、多普勒、载噪比等输出是“正常/欺骗”的判定。它既不需要改硬件又能比导航解级早一步发现问题。因为欺骗信号不管做得再精致终究要从“码相位对齐”过渡到“逐步拉偏”这个过程中观测数据内部的一致性会露出马脚。我自己的实践感受是观测数据级方法能挡住大多数市场上的欺骗设备剩下的少数高端欺骗源也不是完全没反应只是需要更细致的检测手段。这个层次的另一个好处是移植性好。同样的检测逻辑既能跑在Windows桌面端的Matlab脚本里也能改写成C代码塞进嵌入式接收机。我的库最开始就是纯Matlab后来把核心算法改写成C跑在STM32上也没有问题只是少了Matlab的图形化调试便利。1.2 这套库适合谁用哪些场景下最有效从适用对象来看三类人最需要这东西。第一类是GNSS接收机厂商的测试工程师在产线或实验室做抗欺骗功能验证时需要一套不依赖昂贵仪器的快速筛查方案。第二类是无人机、自动驾驶、精准农业等应用方的安全评估人员他们关心的是“我用的接收机在真实环境里遇到欺骗能不能报警”。第三类是高校和研究所做卫星导航安全方向的学生需要一个可以快速上手、能跑出效果的基础工具集。从有效场景来看观测数据级方法对付“外部欺骗源”最有效。所谓外部欺骗源是指欺骗设备放在接收机旁边几百米到几公里范围内发射伪造的GPS或北斗信号试图让接收机锁定这些假信号。这种情况下欺骗信号和真实信号从不同方向到达接收机天线在观测数据上会留下不一致的痕迹。但如果欺骗源和真实信号来自同一个方向比如同位置转发式欺骗或者欺骗设备直接在接收机内部注入那观测数据级方法的识别能力会大打折扣这个边界得清楚。我在库的README里专门写了一句观测数据级方法不是银弹它是欺骗检测的第一道防线负责把“大概率有问题”的信号挡在门外至于最后的确证需要信号级手段配合。这个定位很重要不然容易给人造成误解以为一套Matlab脚本就能解决所有欺骗问题。2. 核心检测方法的原理与实现解析2.1 欺骗信号攻击逻辑与检测突破口要想知道怎么检测先得知道欺骗信号是怎么工作的。典型的欺骗攻击步骤是先发射与真实信号相同码序列、相同频率的信号功率略微高于真实信号让接收机的跟踪环路慢慢锁定到欺骗信号上一旦锁定成功欺骗源开始逐步调整码相位和载波频率把接收机引向错误的位置和时间。整个过程的关键在于“接管”。接收机一旦被接管它输出的伪距、载波相位、多普勒这些观测量实际上反映的是欺骗源的状态而不是真实卫星的状态。但欺骗源能控制码相位和频率却很难同时模拟出一套和真实环境完全一致的物理特征。比如真实信号从卫星到接收机要经过电离层、对流层多普勒变化是连续的、符合卫星运动规律的而欺骗信号的多普勒变化是人为控制的哪怕控制得再平滑在长时间尺度上总会露出马脚。这就像有人模仿你的笔迹写一封信单个字可能很像但整篇文章的节奏、用词习惯、标点风格很难完全复刻。观测数据级检测做的就是“读文章风格”这件事不盯着单个字看而是分析数据之间的内在一致性。2.2 六个核心检测量的原理与适用条件我在库里实现了六个检测量每个检测量从不同角度刻画观测数据的一致性。实际使用时不建议只依赖某一个而是多个组合投票综合判断。第一个是载波相位与多普勒一致性检验。接收机输出的载波相位变化率理论上应该等于多普勒频移。如果欺骗源为了维持码相位的平滑跟踪而载波相位控制不够精细这个一致性就会被破坏。检测方法是用载波相位做一阶差分除以时间间隔得到实测多普勒再与接收机输出的多普勒观测量做差。正常情况这个差值应该在噪声范围内抖动欺骗时会看到系统性偏移或周期性跳变。这个检测量实现简单但在载体动态较大时容易误报因为高动态环境下多普勒变化本身就很快差分误差会被放大。第二个是伪距残差与位置解一致性检验。这是沿用了接收机自主完好性监测RAIM的思路用最小二乘解算位置时计算每个卫星的伪距残差。正常信号残差服从零均值高斯分布欺骗信号会引入较大的残差而且往往集中在某几颗“被接管”的卫星上。检验统计量用残差平方和与卡方分布的门限做比较。这个方法对付“部分卫星被欺骗”的场景非常有效但如果欺骗源把所有可见卫星全部接管残差会被系统性地压制这时检测能力就下降了。第三个是载噪比异常检测。正常跟踪的卫星信号载噪比C/N0一般在35到50 dB-Hz之间分布相对均匀。欺骗信号从地面发射到达接收机的路径和真实卫星不同天线增益也不同会导致载噪比要么异常偏高欺骗源功率大要么出现所有卫星同步变化的情况。我在代码里统计每个历元所有卫星C/N0的均值和方差如果均值突然跳变超过5 dB或者方差显著缩小所有卫星被同一信号源接管载噪比变得“异常一致”就触发告警。这个检测量误报率偏高因为城市峡谷中信号遮挡也会导致载噪比剧烈变化所以只能作为辅助判据。第四个是接收机钟差跳变检测。接收机钟差本质上是一个内部估计值正常工作时它随时间平滑变化受温漂和卫星钟差影响。欺骗信号接管后为了把接收机的时间基准拉偏钟差会出现异常跳变。我的做法是对钟差序列做一阶差分剔除正常漂移后用3倍标准差作为门限。这个方法对付“慢速拉偏”欺骗特别有效因为欺骗源为了让位置缓慢偏离必须同时改变钟差而这个改变很难做到完全无痕。第五个是星历一致性检验。接收机解算时使用的星历参数来自卫星广播的电文。欺骗信号要接管接收机通常会重放真实的星历但有时会注入伪造的星历参数尤其是针对GPS的欺骗。检验思路是对比当前历元解算使用的星历与上一历元是否一致如果星历参数发生异常跳变且没有正常的星历更新通知就标记可疑。这个检测量实现简单但大多数欺骗源并不会改星历所以命中率不高属于“只要触发就是大事件”的类型。第六个是位置与速度解跳变检测。虽然前面说导航解级检测容易被平滑欺骗绕过去但速度和位置同时出现不合理的高频抖动时仍然是有价值的信号。关键在于不要看单个历元而是看一段时间窗口内解的连续性。我在代码里计算滑动窗口内的位置标准差和速度标准差如果位置标准差超过设定值比如3米或者速度标准差超过0.5米/秒就认为解算结果不稳定需要结合其他检测量复核。这个方法在城市峡谷、高多径环境下误报较多但在开阔环境下表现不错。六个检测量各有优劣我在库中统一封装成了独立函数每个函数接收观测数据矩阵和参数配置返回检测分数和标志位。这样使用者可以根据自己的场景选择启用哪几个调整哪几个的灵敏度。3. Matlab代码实现与关键工程细节3.1 数据输入与预处理决定检测效果的上限库的入口函数是run_spoofing_detection.m它接收一个结构体data字段包括观测历元时间GPST秒、卫星编号、伪距、载波相位、多普勒、载噪比、卫星位置ECEF坐标等。我建议数据来源统一用RINEX 3.03格式转换后的Matlab结构体因为RINEX格式包含了完整的观测类型标识能同时拿到伪距、载波相位和多普勒。预处理这一步非常关键很多人忽略。第一步是卫星筛选剔除仰角低于10度的卫星因为低仰角信号受多径和大气影响大残差本身就偏大容易干扰检测。第二步是周跳检测载波相位在做一致性检验前必须先修复周跳否则一阶差分会被周跳污染。我简单用的是电离层残差法检测到周跳就把对应历元标记为无效不参与后续计算。第三步是数据平滑对载噪比和钟差序列做滑动平均窗口长度取10秒降噪但不至于把真实异常抹平。预处理做完数据质量会明显改善。我实测过同一条观测文件不做预处理时伪距残差检验的误报率在15%左右预处理后降到2%以下。这一块的代码在preprocess_obs.m里核心逻辑是function obs preprocess_obs(obs, cfg) % 仰角筛选 idx_vis obs.elev cfg.elev_mask; % 周跳检测电离层残差法 for sv unique(obs.sv) L1 obs.phase(obs.sv sv, 1); L2 obs.phase(obs.sv sv, 2); if isempty(L1) || isempty(L2) continue; end dL diff(L1) - (cfg.f1/cfg.f2) * diff(L2); cycle_slip abs(dL) cfg.cs_threshold; obs.cs_flag(obs.sv sv) cycle_slip; end end这里有个容易踩的坑不是所有接收机都输出双频载波相位如果只有单频数据周跳检测就不能用电离层残差法可以退而求其次用多普勒辅助法。具体思路是用多普勒积分预测下一历元载波相位差值与实测值比较超过门限判定为周跳。精度差一些但没有双频时只能将就。3.2 多普勒一致性检测的实现细节这个检测是六个里面最容易被忽视但又最实用的因为它直击欺骗信号“物理状态不一致”的要害。实现代码在check_doppler_consistency.m里核心片段如下function [score, flag] check_doppler_consistency(phase, doppler, dt) % phase: 载波相位观测量周按历元排序 % doppler: 多普勒观测量Hz % dt: 历元间隔秒 doppler_derived diff(phase) / dt; doppler_meas doppler(2:end); residual doppler_derived - doppler_meas; % 去除整体偏差只保留波动成分 residual_centered residual - median(residual); % 分数残差的均方根RMS score sqrt(mean(residual_centered.^2)); % 门限判定 flag score cfg.doppler_rms_threshold; end注意几个细节。第一dt必须准确如果观测文件本身是非等间隔采样用mean(diff(time))代替名义历元间隔否则差分结果会引入系统性偏差。第二截止相位的单位是周周乘以载波频率才是Hz所以如果phase是周doppler是Hz上述代码并没有做单位统一实际使用时需要把diff(phase)乘以载波频率比如GPS L1的1575.42 MHz。我代码里这一步是显式做的写成doppler_derived diff(phase) * f_carrier / dt避免单位混淆。第三门限的设定依赖接收机本身的载波相位噪声水平高精度接收机噪声低门限可以设到0.05 Hz普通导航型接收机噪声大建议放宽到0.2 Hz。我在测试中发现欺骗信号在“接管”阶段的载波相位控制往往不够平滑导致这个检测量在接管前后会出现明显峰值。这也是这个方法的优势所在不需要等到位置被拉偏才报警在信号接管阶段就能预警。3.3 多检测量融合不要让单一指标做决定单纯跑六个检测量然后各自报警会出现不少误报。我在库里加了一个融合模块叫fuse_detection_decision.m逻辑很简单每个检测量输出一个0到1的分数分数越高越可疑最终判定采用加权投票权重根据场景配置。比如开阔环境下多普勒一致性权重最高0.3伪距残差次之0.25载噪比权重最低0.1因为开阔环境下载噪比波动相对小异常时更可信城市峡谷环境下正好反过来载噪比和钟差跳变的权重要调高多普勒一致性权重要调低高多径环境下这个量容易误报。融合函数输出两个值一个是综合可疑分数0到1一个是判定标志0或1。综合分数超过0.6判定为欺骗0.4到0.6之间判定为“可疑”建议人工复核。这个“疑似”区间的存在很重要因为观测数据级方法不可能做到百分百可靠留一个中间状态能有效降低误报带来的信任危机。实际使用中我发现只要有三个及以上检测量同时给出高分欺骗几乎可以坐实。两个检测量高分时需要看是哪些多普勒伪距残差同时异常基本可以判定载噪比钟差跳变同时异常可能是硬件故障或干扰需要进一步确认。这些经验我写在了代码注释里方便后续使用的人快速建立直觉。4. 常见问题与排查技巧实录4.1 多径环境导致的误报怎么区分用这套库测试城市峡谷数据时误报率明显上升。最典型的表现是伪距残差检验和载噪比检测同时报警但实际上附近没有欺骗源。原因是城市峡谷中信号反射严重多径信号会直接污染伪距让残差变大载噪比也会因为遮挡和反射而剧烈波动。我的排查经验是看“时间持续性”。欺骗信号一旦接管异常是持续性的、有趋势的比如位置慢慢漂移多径干扰的异常是随机性的、高频抖动的。处理方法是在检测函数里加一个持续性验证只有连续N个历元我默认10个历元即10秒都触发同一个检测量才确认报警。这个改动能把多径误报率降低80%以上。另外还有一个技巧对比不同卫星之间的异常分布。多径通常只影响少数几颗低仰角卫星而欺骗接管会影响几乎所有可见卫星。如果报警时异常卫星数量少于可见卫星的50%大概率是多径不急着报欺骗。4.2 欺骗源平滑过渡让我差点漏报有一次测试我用的欺骗设备支持“渐变式欺骗”先把码相位对齐然后用非常缓慢的速度拉偏位置一分钟才偏10米。前几个检测量分数都不高多普勒一致性、伪距残差都慢慢逼近门限但没有触发报警。我回看数据才发现问题出在门限设置上。我之前为了降低误报把门限取得比较高这给了渐变式欺骗可乘之机。后来我把门限改成了“动态门限”用滑动窗口30秒内的均值加上3倍标准差作为当前历元的门限而不是固定值。这样即使欺骗信号拉偏速度很慢持续偏离历史统计值时最终也会触发报警。代价是误报率略有上升但可以通过持续性验证来压制。这个案例给我的教训是反欺骗检测必须考虑对手的“策略”不能只防“暴力欺骗”。平滑过渡是欺骗设备的标配能力检测算法必须跟上。4.3 接收机自身抗干扰策略干扰检测结果另一个容易被忽略的问题接收机本身的抗干扰算法会影响观测数据进而干扰我的检测。比如有些接收机有载噪比平滑功能输出的C/N0不是原始值而是经过滤波的值还有些接收机在检测到信号异常时会自动切换跟踪环路带宽导致多普勒观测量跳变被我的检测算法误判为欺骗。碰到这种情况我的建议是用不同型号的接收机对同一场景做交叉验证。如果只有某一款接收机的数据报警其他都正常大概率是接收机自身处理导致的伪异常可以针对性调整该型号的检测参数。我在库里加了一个receiver_profile配置结构体每个接收机型号一套参数避免互相干扰。代码层面还有一个容易踩的坑时间标签不同步。有些接收机的伪距和多普勒不是同一时刻采样存在毫秒级延迟在做差分计算时会导致系统性残差。解决方案是在预处理阶段做时间对齐或者把时间对准误差纳入检测门限的估计。我的库里默认假设观测量是同一历元对齐的如果用户的数据源不是这样需要在preprocess_obs里先插值对齐。5. 从方法库到实用工具落地建议与扩展方向5.1 搭建自己的验证数据集是调参的前提这个库的算法再好没有贴合场景的数据做验证参数就是拍脑袋。我在整理过程中积累了一套验证方法先用软件接收机采集真实环境数据标定为“正常数据”再用软件模拟器生成欺骗叠加数据标定为“欺骗数据”最后用硬件欺骗源对真实接收机做实验得到最接近实战的测试数据。三类数据的价值不同真实环境数据用来确定误报率底线模拟数据用来验证检测算法的理论性能硬件实验数据用来验证工程可行性。我在库里存了少量示例数据但建议使用者一定要依据自己的场景采集至少几个小时的真实数据做门限校准。门限设太严天天误报用户会直接关掉报警设太松真的欺骗来了没反应工具形同虚设。这个平衡只能靠自己的数据来调。5.2 实时化改造思路Matlab验证C语言落地Matlab版本适合做算法验证和离线数据分析但真正部署到无人机或车辆上还是得改成C或者C。我做了一个精简版C实现核心检测逻辑压缩到500行以内跑在1 GHz的ARM处理器上单历元处理时间不到2毫秒完全满足实时需求。改造的关键是去掉Matlab的矩阵运算便利手动管理内存和循环。比如载波相位差分Matlab一行diff搞定C里要写循环但逻辑不变。另一个关键点是数据流C版本建议做成回调函数模式接收机每输出一帧观测数据就调用一次检测函数结果实时输出。这样在接收机固件里集成比较方便。5.3 报警之后怎么办联动处置策略最后说一个很多做检测的人容易忽略的问题报警后做什么。单纯报“信号被欺骗”对用户来说价值有限如果同时给出可信度分数、异常卫星列表、异常检测量明细用户才能决定是切换到惯性导航、降低自动驾驶等级还是直接报警提示人工干预。我在库的输出结构体里包含了这些信息suspicious_score、triggered_checks哪些检测量触发了、affected_satellites哪些卫星异常、recommended_action建议动作。这个设计借鉴了接收机完好性监测的思路让检测结果不仅能看还能被下游系统消费。如果你要做系统集成这部分输出结构尤其值得参考。我个人在实际操作中的体会是反欺骗检测的本质不是“找到一个完美指标”而是“让多个不完美的指标互相校验”。观测数据级方法搞定大多数情况剩下的边界情况再结合信号级手段去补充。这套Matlab库作为起点能把入门门槛降得很低但要真正部署到关键设备上还需要结合自己的场景做大量的参数校准和有效性验证。希望这份整理能帮你少走一些弯路至少在拿到观测数据时知道从哪里下手、怎么判断、怎么改进。本文还有配套的精品资源点击获取