02 GNSS定位调试方法
作者:李源龙
一、GPS 工作原理简介
1.1 GPS 技术的发展历程
全球定位系统(GPS)起初由美国国防部开发,用于提供精确的定位和导航信息。随着时间推移,它从军事应用扩展到商业和民间领域,实现了全天候、全球范围内的精确地理位置服务。
1.2 GPS 系统组成
GPS 系统主要由三个部分构成:太空中的卫星群、地面控制站和接收器。卫星负责发送信号,地面控制站监测卫星运行状态并进行数据修正,而接收器则用于接收信号并计算位置信息。
1.3 信号接收与定位计算
GPS 接收器通过与至少四颗卫星的信号进行交差定位,借助卫星轨道数据、时间戳和用户位置等信息,使用三角测量法计算出接收器的精确位置、速度和时间。
二、不同地球坐标系的区别
- WGS-84:是国际标准,GPS 坐标(Google Earth 使用、或者 GPS 模块)
- GCJ-02:中国坐标偏移标准,Google Map、高德、腾讯使用
- BD-09:百度坐标偏移标准,Baidu Map 使用
具体解释:
- WGS-84 坐标系 即地球坐标系,国际上通用的坐标系。 设备一般包含 GPS 芯片或者北斗芯片获取的经纬度为 WGS-84 地理坐标系。谷歌地图采用的是 WGS-84 地理坐标系(中国范围除外,谷歌中国地图采用的是 GCJ-02 地理坐标系)。
- GCJ-02 坐标系 即火星坐标系,WGS-84 坐标系经加密后的坐标系。 出于国家安全考虑,国内所有导航电子地图必须使用国家测绘局制定的加密坐标系统,即将一个真实的经纬度坐标加密成一个不正确的经纬度坐标。
- BD-09 坐标系 即百度坐标系,GCJ-02 坐标系经加密后的坐标系。搜狗坐标系、图吧坐标系等,估计也是在 GCJ-02 基础上加密而成的。
Air8000 使用国际标准 WGS-84 坐标系,所以开发者在国内常见地图定位时,会发现与实际情况有几十米的误差。这并非模块问题, 而是国内地图采用了非标坐标系所致。
国内常见地图如高德地图使用 GCJ-02 坐标系, 百度地图使用 BD-09 坐标系,故此开发者需要对模块输出的经纬度进行加偏处理,才能在国内的地图上实现精确定位。
坐标系纠偏的话参考:https://docs.openluat.com/file/GPS-Offset.html

三、GNSS 报文格式
Air8000 的 GNSS 输出数据报文符合 NMEA-0183 标准格式。
NMEA(National Marine Electronics Association)是美国国家海洋电子协会制定的标准通信协议,旨在解决航海电子设备间的数据互通问题。其核心价值在于通过统一接口规范,实现不同厂商设备(如 GPS、雷达、声呐)的兼容性,提升系统集成效率。
3.1 通用 NMEA 语句类型

3.2 NMEA 语句详解
参考文档:https://docs.openluat.com/luatos_lesson/010_luatos_GNSS1/#28-nmea
四、Air8000 无法定位情况分析
在使用模组的过程中总会有很多客户遇到无法定位的情况,现在此总结下一般遇到无法定位的情况。
4.1 软件排查是否有开启 GNSS
软件的话,就直接用我们提供的 demo 直接进行测试即可,如果 demo 测试 GNSS 定位没问题的话,那大概率是在组合项目的时候,哪里软件写出了问题,可以着重排查下软件代码部分的差异。
软件代码地址:Air8000 代码仓库地址
底层固件地址:Air8000A/Air8000D固件地址
1、软件代码方面排查
软件代码方面,我们目前主要确定的问题是:GNSS 是否有正常的开启,这个主要是软件层面需要确定的问题,如果要确定是否正常的开启,最直接的我们是可以通过 GNSS 芯片打开之后输出的原始 NMEA 数据来判断,如果还不了解什么是 GNSS 的原始 NMEA 数据,可以看下该链接里面的 NMEA 数据详解部分 GNSS 培训文档
我们第一步先进行代码的调试,选择 exgnss 文件夹里面的 combination 文件夹,进入 main 文件,选择 require"normal"
--[[
@module main
@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
@version 1.0
@date 2025.07.27
@author 李源龙
@usage
本demo演示的功能为:
使用Air8000整机开发板,通过exgnss扩展库,开启GNSS定位,展示模块的三种功耗模式:正常模式,低功耗模式,PSM+模式
]]
--[[
必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
PROJECT:项目名,ascii string类型
可以随便定义,只要不使用,就行
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
PROJECT = "gnsstest"
VERSION = "001.000.000"
--添加硬狗防止程序卡死
if wdt then
wdt.init(9000)--初始化watchdog设置为9s
sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
end
-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
-- 启动errDump日志存储并且上传功能,600秒上传一次
-- if errDump then
-- errDump.config(true, 600)
-- end
-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
-- 可以使用合宙的iot.openluat.com平台进行远程升级
-- 也可以使用客户自己搭建的平台进行远程升级
-- 远程升级的详细用法,可以参考fota的demo进行使用
-- 启动一个循环定时器
-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
-- 方便分析内存使用是否有异常
-- sys.timerLoopStart(function()
-- log.info("mem.lua", rtos.meminfo())
-- log.info("mem.sys", rtos.meminfo("sys"))
-- end, 3000)
exgnss=require("exgnss")
--以下功能每次只能打开一个用于测试,选择对应的功能进行测试
require"normal" --正常模式,搭配GNSS定时开启发送经纬度数据到服务器,不进入任何低功耗模式
-- require"lowpower" -- 低功耗模式,搭配GNSS定时开启发送经纬度数据到服务器,定位成功之后关闭GNSS进入低功耗模式
-- require"psm" -- PSM+模式,唤醒开启GNSS定位,定位成功之后发送经纬度数据到服务器,然后关闭GNSS进入PSM+模式
-- require"vibration" -- 振动检测模式,搭配GNSS触发震动检测之后,持续发送经纬度数据到服务器
-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
第二步,我们使用 luatools 工具进行烧录,luatools 下载工具,在主页面点击项目管理,放入代码和固件,点击下载底层和脚本,进行烧录

烧录成功之后,查看日志,如果每秒有输出 GNSS 的原始 NMEA 数据,则证明,GNSS 可以正常开启

至此可以看到 GNSS 可以正常开启,证明软件部分没有什么问题,如果到这个步骤软件没有输出该内容,可以看下 gnssotps 的 debug=true 这个有没有打开注释,这个是用于确定 GNSS 是否开启的重要判断依据。如果 gnss 开启之后过了很长时间还是没有定位成功,就可以往下排查下是否是环境因素的影响了。
如果打开了 debug,在 luatools 日志里面还是没有原始 NMEA 日志输出的话,请联系我们的技术支持,我们会对该情况进行分析
4.2 地理环境因素的影响
为什么开发板不能在室内使用 gps 定位?
为什么手机可以定位,模块无法定位呢?
这究竟是为什么呢?
很多开发者在测试 GPS 的时候,总是发觉无法定位,甚至无法搜星。经过技术支持的解答才明白,只有戒掉懒癌,去室外测试,才能有良好的效果。究其原因,还是 GPS 的原理所致。
以最简单的几何来说,两点确定一条线,三个点确定一个面;那么逆推一下,就是三个点确定一个位置(基站定位的原理);四个点确定精确位置(含高程):

导航卫星不断地向地球发射导航电文(卫星的速度、角速度、空间相对位置等信息)
GPS 芯片收到不同卫星的数据后,进行解算,就能得到当前接收器在地球的绝对位置了。根据三点定位的原理,同时使用 3 颗卫星,可以实现 2D FIX(不含高程);只有同时使用 4 颗或以上的卫星,才能实现 3D FIX(含高程)。
不过凡事都有例外,如果开发者在飘窗进行测试,会搜到卫星,甚至超过 4 颗,但是仍然无法定位。这是为什么呢?这是因为 GPS 天线的“可视角”有限,而这片星域的卫星角度相距太近,间隔太小,无法精确解算,故此无法实现定位。
GPS 卫星运行在距地 36000KM 的轨道上,信号强度相当弱(GPS 卫星的功率有多大?)。GPS 的民用 C/A 码从卫星发出来的时候信号只有 27W 左右,达到地球的时候在-158.5dBW 以上。用对数形式表示可能不直观,换算成十进制等于将近 0.0000000000000001W,相当小。所以,只有室外开阔的、无遮挡、晴好的地方,才能搜到更多的卫星,SNR 值更高(阴天都会有影响哦),GPS 芯片才能更快、更好的实现定位。
而室内是没有 GPS 信号的,所以不论开发者如何调整代码、修正天线,都无法实现 GPS 定位。如果开发者懒癌爆发,不想去室外测试怎么办呢?不用担心,万能的淘宝给大家带来了福音:

不过,有的开发者肯定要反驳我:为什么我的手机在室内就能定位,而且特别准呢?
这个问题的答案很简单,手机使用的是多重定位,如果要单纯的测试手机的 GPS 定位,需要这样做:首先“三清”,仅打开 GPS,然后拔卡,飞行模式,再用专业软件如 GPS Test+ 试一试,你就明白啦~~
这种情况下,室内,手机也是无法定位的。

所以说,手机在室内之所以可以定位,实际上是它不仅使用了 GPS,还使用了很多其他的辅助定位技术,如 LBS(基站定位)、Wi-Fi(wifi 定位)、BLE(蓝牙)等
至此,开发者应该可以明白为什么手机可以定位,而开发板无法定位了。
PS:如果有手机同样的预算,开发板也能做到同样的“室内定位”效果
还有一种室内情况是人在室内,把天线甩到窗外,这种情况主要还是看当地环境因素了,如果搜到的星比较多,有足够的卫星来进行位置计算,是可以定位成功的,但是也有定位不成功的情况,没办法保证这种方式是一定可以定位的,如果要验证环境因素是否有影响,可以尝试去空旷地带和天台进行测试,如果使用开发板在同样地方可以定位成功,但是使用自己做的板子定位不成功,那么就可以往下继续看天线的部分了。
4.3 天线使用问题
1. 有源/无源天线混淆
有部分开发者经常遇到,自己去了户外,按理说应该在 35S 左右就能定位成功了啊,怎么自己一两分钟都没几颗星,等了 10 多 20 分钟依旧还是定位不成功,同步对比手机,发现差距不止一点点,此时应该先检查 GNSS 天线设计问题,看看自己是不是将有源天线插给了无源天线预留的底座,或者无源天线插给了有源天线预留的底座。

2. 天线设计问题
更多客户遇到的,不是户外定位不到,而是户外定位速度极其的慢的问题,常见于无源天线(因为无源天线对结构、PCB、走线要求都比较高),如果自己设计没有注意下面几点,是很有可能定位不到/定位极其的慢的。
GPS 天线设计参考下面这篇文章:GNSS 天线
同时建议联系天线厂天线厂联系方式以及服务,可以使用我们推荐的,或者是长期自己合作的,天线厂会根据当前的 PCB 布局,匹配天路建议,然后做出适合底板的天线,然后天线厂会根据当前的底板进行 gnss 部分的测试,保证经过调试的天线的性能各方面和底板匹配度达到最高。
到了天线这部分,只能联系天线厂,他们可以根据你的硬件做出更专业的建议,因为经过天线厂匹配的天线,会针对你的设备结构、性能指标和具体工作环境进行优化,确保信号稳定性和高精度。而网上标品是通用设计,性能一致性和专业支持有限,通俗点说在信号好的情况下,随便接根 gnss 天线都能定位成功,在信号较弱的情况下,定制天线凭借其更高的增益 和更好的抗干扰特性,就能更有效地从噪声中提取微弱信号,从而实现稳定连接,而标品天线可能已经无法有效工作。
4.4 星系切换问题
有很多客户遇到过,模组默认固件,只打开 GNSS 电源,35S 左右就能定位到了,但是切换成单北斗,就需要 2 分钟多甚至更长时间才能定位成功。
首先明确一点,合宙的大多数模组,均使用的单频(L1)GNSS 芯片,所以内部能搜到的北斗卫星,只有 B1C 或者 B1I,这两个频段的北斗卫星,由于北斗卫星为高轨卫星,在同一片区域内,卫星数可能不会很多,实测在笔者附近的广场上,单频(L1)GNSS 芯片,只能搜到这几颗北斗卫星

所以,在明确自己是真正需要单北斗/单 GPS 或者其他星系前,尽量不要将模块切换为单星系状态,如果客户对单北斗需求非常明确,建议选择真正的单北斗芯片,杜绝后患,因为很多单北斗应用是需要进实验室过多项认证的,使用多星系 GNSS 芯片,有极大概率过不去单北斗的认证。如果需要单北斗模块,可以选择 Air8000DB 或者是 Air8000AB 这两款:https://docs.openluat.com/air8000/product/
4.5 外部干扰源问题
此种情况不能说常见,但是确实客观存在,之前有部分客户就遇到了,在他们公司附近一直定位不到,但是客户放在自己小区前面广场上就能定位成功,查看地图得知,客户的公司附近,有"中国军工"单位,不只是 GNSS 定位不到,偶尔自己的手机 5G/4G 信号也没有,此种情况定位不到的原因不言而喻了。
不过还有少量客户遇到的干扰源还是比较明显,例如只针对 GPS 频段发射的干扰源,此时切换为单北斗模式,即使是单频模组,在部分情况下,还是能够正常定位成功的。还有部分情况是因为硬件设计问题,比如 GNSS 天线背面有 LCD 屏,LCD 屏通常是一个比较大的干扰源,这个时候需要去掉屏幕尝试下看看是否可以定位成功,如果可以的话,就需要和天线厂针对当前的硬件做一个硬件改版优化,避免掉类似的干扰。
以上四点是最为常见的四种无法定位的情况,如果你使用合宙的 GNSS 模组排除了这四点,依旧无法定位,欢迎你来找合宙,我们将会竭力为您排查您所遇到的问题
五、Air8000 的 GNSS 测试环境
有部分客户,需要测试 Air8000 内部 GNSS 的稳定性,但因为 Air8000 UART2(也就是 GPS 对应的串口)RX 不能直接和外部通讯,只能通过 cat.1 主控给它发指令控制,所以使用合宙提供的测试工具,不能直接测试 100 次或者 1000 次冷热启动,需要使用 LUA 脚本控制模块对接 PC 端测试工具,如果客户只是想看看,CN 值、当前位置,那可以直接接 uart2 的 TX 出来对接 PC 端工具
5.1 软件环境
1. 烧录工具 Luatools;
2. 内核固件文件(底层 core 固件文件):固件下载地址;此页面有新版本固件的话选用最新版本固件。
3. LuatOS 需要的脚本和资源文件:代码地址
4. lib 脚本文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件;
准备好软件环境之后,接下来查看如何烧录项目文件到 Air8000 开发板中,将本篇文章中演示使用的项目文件烧录到 Air8000 开发板中。
5. GNSS PC 端测试工具:- iNavTool-V4261
5.2 硬件环境
Air8000A 整机开发板
淘宝购买链接:Air8000A 整机开发板淘宝购买链接 ;
整机开发板板使用教程参考:Air8000A 整机开发板板使用说明

GPS 天线
上面购买链接里面包含一个 gps 天线。
TTL 转 USB 工具
将设备组装好并连接 USB 数据线,将 TTL 转 USB 连接到 Air8000A 的整机开封板的 uart2 上面。
需要注意的是需要将 TTL 转 USB 的 RX 与整机开发板的 CAM_SPI_DO 连接,TX 与和整机开发板的 CAM_SPI_DI 相连接(这是因为 gps 芯片串口与 cat 1 芯片的串口是交叉相连的,所以 TTL 转 USB 的串口只需和 cat 1 的串口 RX 接 RX,TX 接 TX),连接好后如下图所示:

5.3 测试现象
- 下载好的 GNSS PC 端测试工具为一个压缩包,需要解压后打开如图所示 EXE 文件

打开测试工具后可以看见如下界面:

选择左上角的"打开串口"后,选择对应的端口号以及波特率即可,Air8000 的 gnss 对应的波特率为 115200,其他的不用管,默认即可。打开端口后,等待片刻,即可看见模块定位成功输出的位置信息以及其他信息,左上角为 NMEA 原始数据,下面的均为从 NMEA 数据中解析出的各种信息(注:此处地图视图需要电脑链接网络才可找到对应经纬度的坐标),如果没有输出经纬度,则证明 GNSS 未打开,需要检查脚本 GPS 电源是否打开,如果长时间没有定位成功,可以参考上一章“Air8000 无法定位情况分析”进行排查。

如果连接成功,则可以看到下面的现象:

图中的卫星视图,地图视图等功能可以通过视图选项进行开启和关闭

5.4 进阶教学
如果客户不满足只是查看当前 CN 值、可见卫星数、当前经纬度对应坐标等信息,还需要测试 GNSS 性能,还要有个直观的展示界面,可以参考如下方法:
点击最上面一排工具栏中的"工具"选项,再在二级菜单中找到 TTFF 选项,点击后,会弹出如图所示的弹窗,看不清没关系,在弹窗的右上角选择放大该窗口

放大后的界面如下所示:

如上图所示,你可以选择测试 冷启动、热启动、温启动,可以选择测试次数、超时时间、以及每次测试间隔
由于 GNSS 芯片在 Air8000 内部,如果你想纯粹的测试内部 GNSS 性能,则按照如下配置来

特别注意,时间辅助和位置辅助在测试 GNSS 工作性能时,影响较大,如果你只想测试单 GNSS 性能,不要勾选。
这个测试工具其原理本质上就是通过 USB 转 TTL 模块给 Air8000A 内部 GNSS 发送冷/热/温启动指令,然后根据 NMEA 原始数据输出,来分析成功率、CPE50、误差、最大定位时长、平均定位时长等数据。
但是由于 Air8000A uart2 的 RX 被内部 Cat.1 芯片占用,无法直接发送命令给内部 GNSS 芯片,所以我们需要曲线救国一下,将 uart2 上的数据转发给 uart1,再将 uart1 收到的指令转发给 uart2,这样就能实现通过 uart1 给内部 GNSS 芯片发送指令,并且通过 uart1 观察 GNSS 芯片吐出的 NMEA 数据,来分析 Air8000A 内部 GNSS 芯片性能的目的了
需要注意的是需要将 TTL 转 USB 的 RX 与整机开发板的 UART1_TXD 连接,TX 与和整机开发板的 UART1_RXD 相连接,连接好后如下图所示:

完整测试代码如下:
--[[
@module main
@summary gnss正常测试功能模块
@version 1.0
@date 2025.07.27
@author 李源龙
@usage
使用Air8000整机开发板,测试GNSS的性能
]]
PROJECT = "gnsstest"
VERSION = "001.000.000"
exgnss=require("exgnss")
local function normal_cb(tag)
log.info("TAGmode1_cb+++++++++",tag)
local rmc=exgnss.rmc(0) --获取rmc数据
log.info("nmea", "rmc", json.encode(exgnss.rmc(0)))
local data=string.format('{"lat":%5f,"lng":%5f}', rmc.lat, rmc.lng)
end
local function gnss_fnc()
log.info("gnss_fnc111")
local gnssotps={
gnssmode=1, --1为卫星全定位,2为单北斗
-- agps_enable=true, --是否使用AGPS,开启AGPS后定位速度更快,会访问服务器下载星历,星历时效性为北斗1小时,GPS4小时,默认下载星历的时间为1小时,即一小时内只会下载一次
debug=true, --是否输出调试信息
-- uart=2, --使用的串口,780EGH和8000默认串口2
-- uartbaud=115200, --串口波特率,780EGH和8000默认115200
-- bind=1, --绑定uart端口进行GNSS数据读取,是否设置串口转发,指定串口号
-- rtc=false --定位成功后自动设置RTC true开启,flase关闭
----因为GNSS使用辅助定位的逻辑,是模块下载星历文件,然后把数据发送给GNSS芯片,
----芯片解析星历文件需要10-30s,默认GNSS会开启20s,该逻辑如果不执行,会导致下一次GNSS开启定位是冷启动,
----定位速度慢,大概35S左右,所以默认开启,如果可以接受下一次定位是冷启动,可以把auto_open设置成false
----需要注意的是热启动在定位成功之后,需要再开启3s左右才能保证本次的星历获取完成,如果对定位速度有要求,建议这么处理
-- auto_open=false,
-- 定位频率,指gnss每秒输出多少次的定位数据,1hz=1次/秒,默认1hz,可选值:1/2/4/5
-- hz=1,
}
exgnss.setup(gnssotps) --配置GNSS参数
exgnss.open(exgnss.DEFAULT,{tag="normal",cb=normal_cb}) --打开一个60s的TIMERORSUC应用,该模式定位成功关闭
end
uart.setup(2,115200)
uart.on(2, "receive", function(id, len)
local s = ""
repeat
s = uart.read(id, 2048)
if #s > 0 then -- #s 是取字符串的长度
-- 如果传输二进制/十六进制数据, 部分字符不可见, 不代表没收到
-- 关于收发hex值,请查阅 https://doc.openluat.com/article/583
log.info("uart2", "receive", id, #s, s)
uart.write(1, s)
end
until s == ""
end)
local uartid = 1 -- 根据实际设备选取不同的uartid
--初始化
uart.setup(
uartid,--串口id
115200,--波特率
8,--数据位
1--停止位
)
-- 收取数据会触发回调, 这里的"receive" 是固定值
uart.on(uartid, "receive", function(id, len)
local s = ""
repeat
s = uart.read(id, 2048)
if #s > 0 then -- #s 是取字符串的长度
-- 如果传输二进制/十六进制数据, 部分字符不可见, 不代表没收到
-- 关于收发hex值,请查阅 https://doc.openluat.com/article/583
log.info("uart1", "receive", id, #s, s)
uart.write(2, s)
end
until s == ""
end)
-- 并非所有设备都支持sent事件
uart.on(uartid, "sent", function(id)
log.info("uart", "sent", id)
end)
sys.taskInit(gnss_fnc)
-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
测试冷启动的演示:

关于这个测试工具的其他用法,可以参考,压缩包内的 iNavTool_Manual.pdf
六、更多 GNSS 知识学习
我们针对 GNSS 也做了相对的技术直播和文档,欢迎大家来学习
直播第一讲:GNSS 培训 1
直播第二讲:GNSS 培训 2