From c570597145dc5c33ea4ebd08292e736180de8736 Mon Sep 17 00:00:00 2001 From: "jayceon.fu" Date: Mon, 3 Apr 2023 09:42:33 +0800 Subject: [PATCH 1/2] =?UTF-8?q?docs=20(GNSS):=20=E5=A2=9E=E5=8A=A0GNSS?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E6=8C=87=E5=AF=BC=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 固件版本: N/A 是否需要文案翻译: 是 --- docs/Application_guide/zh/Note.md | 24 +- docs/Application_guide/zh/README.md | 2 +- docs/Application_guide/zh/gnss/README.md | 59 +++ .../zh/gnss/coordinate_transform.md | 0 docs/Application_guide/zh/gnss/gnss.md | 229 +++++++++ docs/Application_guide/zh/gnss/quecgnss.md | 452 ++++++++++++++++++ docs/Application_guide/zh/sidebar.yaml | 4 +- 7 files changed, 748 insertions(+), 22 deletions(-) create mode 100644 docs/Application_guide/zh/gnss/README.md create mode 100644 docs/Application_guide/zh/gnss/coordinate_transform.md create mode 100644 docs/Application_guide/zh/gnss/gnss.md create mode 100644 docs/Application_guide/zh/gnss/quecgnss.md diff --git a/docs/Application_guide/zh/Note.md b/docs/Application_guide/zh/Note.md index 20451afc..935e0665 100644 --- a/docs/Application_guide/zh/Note.md +++ b/docs/Application_guide/zh/Note.md @@ -41,30 +41,14 @@ README.md作为板块的首页入口,需要对该板块做综合性描述, 1. 若该技术板块存在多种应用场景,每种应用场景均需对应一篇文档,如socket编程,会存在 tcp client,tcp server 和 udp 三种不同场景,需编写3篇文档。 2. 每篇文档按照常规的应用开发流程描述即可,可粘贴每个开发环节对应的代码片段,完整的代码由于篇幅过程,禁止粘贴在文档中,而是在文末给出代码链接。 3. 代码链接必须指向[https://github.com/QuecPython](https://github.com/QuecPython)组织中的仓库,禁止将代码仓库放在任何其他组织中。代码仓库的建立,联系Chavis。 +4. 所有md文档中引用的图片,都应根据文档所属功能在media目录下新建合适的子目录,并将图片文件存放在该子目录下: -> 代码仓库的建立,联系Chavis。 - -4. 应根据文档所属功能在当前目录下新建对应子目录,如网络相关的md文件,则新建network目录,将对应md文件存在在network目录下;若同一个功能模块需要编写多篇文档,如socket模块,需要编写tcp client.md,tcp server.md 和 udp.md 3篇文档,则应该在network目录下再新建socket子目录用于存放这3篇文档。 - -5. 所有md文档中引用的图片,都应根据文档所属功能在media目录下新建合适的子目录,并将图片文件存放在该子目录下: +* 图片格式统一为png格式; +* 图片命名符合见名知意的要求,如bsp.uart.xxx.png,其中`xxx`表示用简短英文编写的图片作用描述。 - * 图片格式统一为png格式; - * 图片命名符合见名知意的要求,如bsp.uart.xxx.png,其中`xxx`表示用简短英文编写的图片作用描述。 - -6. 应用指导文档的编写应包含功能简介、应用场景说明、功能实现、注意事项几个部分,注意事项部分可选,没有则不写。 - - * 若该技术板块只需要写一篇md文档,则该文档中需要包含:功能简介、应用场景说明、功能实现、注意事项(可选); - * 若该技术板块需要按应用场景编写多篇md文档,则功能简介应放到README.md中,其他几项放到各自的md中,如socket示例如下: +> 代码仓库的建立,联系Chavis。 - ``` - socket - ├-- README.md :socket简介 - ├-- tcp_client.md :tcp client 应用场景、功能实现、注意事项 - ├-- tcp_server.md :tcp server 应用场景、功能实现、注意事项 - └-- udp.md :udp 应用场景、功能实现、注意事项 - ``` - ## QuecPython 应用指导 diff --git a/docs/Application_guide/zh/README.md b/docs/Application_guide/zh/README.md index d00e01ac..f3b9cbea 100644 --- a/docs/Application_guide/zh/README.md +++ b/docs/Application_guide/zh/README.md @@ -8,5 +8,5 @@ QuecPython 应用指导,是对QuecPython常用功能模块如何使用的指 * [bsp](./bsp/README.md) * [bluetooth](./bluetooth/README.md) * [fota](./fota/README.md) -* [network](./network/README.md) +* [gnss](./gnss/README.md) * [peripherals](./peripherals/README.md) \ No newline at end of file diff --git a/docs/Application_guide/zh/gnss/README.md b/docs/Application_guide/zh/gnss/README.md new file mode 100644 index 00000000..9c6a2df1 --- /dev/null +++ b/docs/Application_guide/zh/gnss/README.md @@ -0,0 +1,59 @@ +# GNSS应用指导说明 + +本文中对GNSS一些基础概念进行了简要说明,描述了如何使用 QuecPython 的`gnss`模块和`quecgnss`模块的功能来获取定位信息,同时包含了一些注意事项。 + + + +## 1. 基础概念说明 + +### 1.1 什么是GNSS + +GNSS是指全球导航卫星系统(Global Navigation Satellite System),它是泛指所有的卫星导航系统。主要包括美国的全球定位系统(GPS)、欧洲的伽利略卫星导航系统(GALILEO)、俄罗斯的格洛纳斯卫星导航系统(GLONASS)、中国的北斗卫星导航系统(BDS)以及区域系统和增强系统。 + + + +### 1.2 什么是AGPS + +AGPS是指辅助全球卫星定位系统(Assisted Global Positioning System)。它本身并不是一种定位系统,而是一种辅助定位的方式,用来加快定位速度。传统GPS定位中需要全频段搜索以找到可用卫星而导致耗时较长,而AGPS可以通过无线网络直接下载当前地区的可用卫星信息,提高了搜星的速度,也就加快了定位速度,同时起到了减少设备电量消耗的作用。 + + + +> 目前以下型号模组均内置GNSS定位功能,并默认都支持AGPS: +> +> EC200UCNAA/EC200UCNLA/EC200UEUAA/EC800MCNGA/EC800GCNGA/EG810MCNGA/EG915NEUAG + + + +### 1.3 参考坐标系 + +坐标都是相对于某一参考系而言,才有意义,GNSS定位坐标也不例外。我们通过各种渠道获取到的经纬度坐标,很可能并不是同一个坐标系下的数据。如果想利用这些坐标在地图上做可视化相关功能,就需要经过计算转换为对应地图坐标系下的坐标。目前国内比较常见的坐标系有: + +| 坐标系 | 说明 | +| ------ | ------------------------------------------------------------ | +| WGS-84 | 世界大地测量系统(World Geodetic System 1984),是全球通用的坐标系,也是使用最广泛的坐标系。一般GPS定位设备得到的经纬度坐标都是使用WGS-84坐标系。 | +| GCJ-02 | 由中国国家测绘局制定的地理信息系统的坐标系,它是由WGS-84坐标加密得到。国内的高德地图、腾讯地图使用的都是GCJ-02坐标系。 | +| BD-09 | 由百度开发定制的坐标系统,它是在GCJ-02坐标的基础上再次加密得到。百度地图使用的是BD-09坐标系。 | + +通过上述说明,应明确一点,通过GNSS定位芯片拿到的经纬度坐标是WGS-84坐标系下的数据,如果用户需要将获取的经纬度坐标放到地图上显示位置,则需要将WGS-84坐标进行计算转换为对应坐标系下的坐标,才可用于在地图上拾取位置。 + + + +### 1.4 QuecPython `gnss`和`quecgnss`的区别 + +QuecPython 的内置库中,有`gnss`模块和`quecgnss`模块,都是和GNSS定位相关的功能模块。具体区别如下: + +* `gnss` - 外置GNSS功能模块。 + +* `quecgnss` - 内置GNSS功能模块。 + +所谓的“内置”和“外置”,是相对模组而言。如果模组内部集成了GNSS定位功能,则为“内置”,须使用QuecPython的`quecgnss`模块来获取定位信息;如果模组是通过串口外接了L76K系列定位芯片,则为“外置”,须使用QuecPython的`gnss`模块来获取定位信息。 + + + +## 2. GNSS应用指导文档列表 + +* [外置GNSS功能应用指导](./gnss.md) + +* [内置GNSS功能应用指导](./quecgnss.md) + + diff --git a/docs/Application_guide/zh/gnss/coordinate_transform.md b/docs/Application_guide/zh/gnss/coordinate_transform.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/Application_guide/zh/gnss/gnss.md b/docs/Application_guide/zh/gnss/gnss.md new file mode 100644 index 00000000..d2d60e6c --- /dev/null +++ b/docs/Application_guide/zh/gnss/gnss.md @@ -0,0 +1,229 @@ +# 1. 简介 + +QuecPython提供了`gnss`功能模块来获取外置GNSS模块的定位数据。该功能模块直接在内部完成了原始定位数据的处理解析工作,将用户关心的一些定位参数提取出来并提供对应接口让用户可直接获取。避免了用户自己通过串口去读取原始的定位数据,并进行复杂的正则匹配查找和解析的情况,提高了用户开发效率。 + +目前该模块自动解析的NEMA语句包括:GGA、RMC和GSV。 + +> 本文档中示例代码前面有 `>>> `字符串的,表示在QuecPython的命令交互界面输入的代码。 + + + +# 2. 使用说明 + +`gnss`模块接口的详细说明,请参考QuecPython官网的Wiki文档中相关部分的说明。下面以L76K定位芯片为例,说明如何使用`gnss`模块的相关功能。 + +## 2.1 获取定位数据 + +### 2.1.1 使用步骤 + +步骤1:确定串口信息 + +即确定L76K定位芯片接到模组哪个串口,以及使用的波特率等信息,本示例中将L76K接到了模组的UART2,波特率默认为9600bps(可在L76K使用手册中查询到)。 + + + +步骤2:实例化一个对象 + +```python +>>> from gnss import GnssGetData +>>> gnss_obj = GnssGetData(2, 9600, 8, 0, 1, 0) +``` + + + +步骤3:读取数据并解析 + +用户只需要调用如下接口,该接口直接完成定位数据的读取和解析工作: + +```python +>>> gnss_obj.read_gnss_data() +822 +``` + +上述接口将读取原始数据、以及复杂的解析操作都放在接口内部实现,只返回了通过串口读取的数据长度。如用户想看本次读取解析的原始数据,可调用如下接口: + +```python +>>> data = gnss_obj.getOriginalData() +>>> print(data) +$GNGGA,063957.000,3802.01852,N,11437.92027,E,1,13,1.2,129.2,M,-15.7,M,,*62 +$GNGLL,3802.01852,N,11437.92027,E,063957.000,A,A*40 +$GNGSA,A,3,01,03,06,14,30,194,,,,,,,1.8,1.2,1.4,1*00 +$GNGSA,A,3,13,23,33,38,40,43,59,,,,,,1.8,1.2,1.4,4*3C +$GPGSV,3,1,11,01,38,044,12,03,44,101,24,06,30,233,31,14,79,184,34,0*6F +$GPGSV,3,2,11,17,58,319,,19,40,296,04,21,11,051,,30,14,202,34,0*67 +$GPGSV,3,3,11,194,44,156,27,195,68,074,,199,44,160,25,0*6F +$BDGSV,3,1,11,03,44,186,,07,83,121,05,10,65,309,,13,37,206,31,0*79 +$BDGSV,3,2,11,23,21,181,31,28,69,354,14,33,24,115,27,38,65,190,30,0*7A +$BDGSV,3,3,11,40,70,093,18,43,28,065,15,59,38,143,29,0*44 +$GNRMC,063957.000,A,3802.01852,N,11437.92027,E,0.69,168.35,260422,,,A,V*0B +$GNVTG,168.35,T,,M,0.69,N,1.29,K,A*2F +$GNZDA,063957.000,26,04,2022,00,00*44 +$GPTXT,01,01,01,ANTENNA OPEN*25 +``` + + + +步骤4:确认是否定位成功 + +如用户只关心是否定位到经纬度坐标以及坐标是否有效,可使用如下接口,返回1即表示定位成功且有效: + +```python +>>> gnss_obj.isFix() +1 +``` + + + +步骤5:获取坐标信息 + +调用如下接口即可获取到定位坐标: + +```python +>>> gnss_obj.getLocation() +(114.6320045, 'E', 38.033642, 'N') +``` + + + +> 上述接口获取的坐标是WGS-84坐标系下的经纬度数据,不可直接用于高德地图、腾讯地图以及百度地图等地图上拾取位置信息,必须先转换为对应地图参考坐标系下的坐标。 + + + +### 2.1.2 示例代码 + +如下代码是一个完整的使用`gnss`模块方法来获取定位坐标的例程: + +```python + +import utime +from gnss import GnssGetData + + +def main(): + gnss_obj = GnssGetData(2, 9600, 8, 0, 1, 0) + while True: + try: + read_size = gnss_obj.read_gnss_data() + except Exception: + print('数据异常,解析出错!') + data = gnss_obj.getOriginalData() + print('===============================================') + print(data) + print('===============================================') + utime.sleep(2) + continue + + if read_size > 0: + if gnss_obj.isFix(): + coordinate = gnss_obj.getLocation() + longitude = coordinate[0] + latitude = coordinate[2] + print('定位成功,当前经纬度:({}, {})'.format(longitude, latitude)) + utime.sleep(10) + else: + print('定位中,请稍后...') + utime.sleep(2) + else: + print('未读取到定位数据...') + utime.sleep(2) + + +if __name__ == '__main__': + main() +``` + + + +## 2.2 配置NEMA串口波特率 + +查阅L76K的使用手册,确定默认使用的波特率为9600bps,如用户需要修改波特率,则需要使用PCAS语句来发送一条修改波特率的指令给L76K芯片。 + +### 2.2.1 PCAS01指令格式 + +PCAS01语句即用来配置L76K的NEMA串口波特率。格式如下: + +``` +$PCAS01,* +``` + +参数说明: + +| 字段 | 描述 | +| -------- | ------------------------------------------------------------ | +| CMD | 支持如下波特率:
0 - 4800
1 - 9600
2 - 19200
3 - 38400
4 - 57600
5 - 115200 | +| Checksum | 校验和,校验和是对语句中所有字符的 8 位(不包括起始和结束位)进行异或运算,所有字符是指在定界符“$”与“*”之间,但不包括这些定界符的全部字符,包括“,”在内。 | +| CR和LF | NEMA语句的结束字符,即`\r\n` | + + + +### 2.2.2 步骤说明 + +步骤1:确定波特率 + +即确定需要配置的波特率,以确定CMD的值。假如需要配置L76K NEMA串口波特率为115200,即CMD为5。则PCAS01语句为: + +``` +$PCAS01,5*\r\n +``` + +步骤2:checksum计算 + +确定波特率后,PCAS01语句就只差checksum待计算,可按照如下算法计算: + +```python + +""" +参数说明 +dstr - 需要计算校验和的字符串,对NEMA语句而言,即“$”与“*”之间的字符串 +返回值:返回十进制的校验和 +""" +def get_checksum(dstr): + lista = list(dstr) + list_len = len(lista) - 1 + checksum = ord(lista[0]) + i = 0 + while i < list_len: + checksum = checksum ^ ord(lista[i+1]) + i = i + 1 + return checksum + +# 取“$”与“*”之间的字符串 +dstr = 'PCAS01,5' +checksum = get_checksum(dstr) +print('checksum={},{}'.format(checksum, hex(checksum))) + +#计算结果 +checksum=25,0x19 +``` + +得到对应的checksum之后,可确定完整的PCAS01语句如下: + +``` +$PCAS01,5*19\r\n +``` + +步骤3:发送指令 + +使用machine类下的UART功能模块来发送该指令到L76K,本示例中将L76K接到了模组的UART2,具体如下: + +```python + +from machine import UART +from gnss import GnssGetData + +# L76K默认波特率是9600,故先需要配置9600波特率来打开串口 +uart2 = UART(UART.UART2, 9600, 8, 0, 1, 0) +# 发送波特率配置指令 +uart2.write('$PCAS01,5*19\r\n') +# 配置成功后须先关闭当前串口 +uart2.close() +# 按照115200波特率来示例话一个gnss对象 +gnss_obj = GnssGetData(2, 115200, 8, 0, 1, 0) +``` + + + + + + + diff --git a/docs/Application_guide/zh/gnss/quecgnss.md b/docs/Application_guide/zh/gnss/quecgnss.md new file mode 100644 index 00000000..a0c9fc5b --- /dev/null +++ b/docs/Application_guide/zh/gnss/quecgnss.md @@ -0,0 +1,452 @@ +# 1. 简介 + +QuecPython提供了`quecgnss`功能模块来获取模组内置GNSS的定位数据。当前`quecgnss`模块暂不支持像`gnss`模块那样直接完成了原始定位数据的处理解析功能,而是需要用户自己来完成原始数据的处理解析工作。为了提高用户开发效率和使用便捷性,本指导文档中会直接给出较为完整的数据处理解析例程,供用户参考使用。 + +> 本文档中示例代码前面有 `>>> `字符串的,表示在QuecPython的命令交互界面输入的代码。 + + + +# 2. 使用说明 + +quecgnss模块的接口详细说明,请参考QuecPython官网的Wiki文档中相关部分的说明。本文档中主要从整体流程上说明quecgnss模块的使用方式。 + + + +## 2.1 获取定位数据 + +### 2.2.1 使用步骤 + +步骤1:功能初始化 + +内置GNSS功能默认是关闭的,需要用户主动初始化并开启。直接调用quecgnss模块的init接口即可完成初始化并打开的工作。如果初始化成功,则会返回0,失败返回-1。 + +```python +>>> import quecgnss +>>> quecgnss.init() +0 +``` + + + +步骤2:确定GNSS的状态 + +内置GNSS功能初始化后,应确定GNSS当前的状态是否为”定位中“,只有处于这种状态,才可以开始读取NEMA数据。使用如下接口确定状态,返回值为2说明已经处于”定位中“的状态: + +```python +>>> quecgnss.get_state() +2 +``` + + + +步骤3:读取NEMA数据 + +当GNSS状态值为”定位中“时,即可使用如下接口去读取定位数据,读取出来的是尚未处理过的原始数据: + +```python +>>> data = quecgnss.read(2048) +>>> print(data) +(2048, '$GNRMC,020943.00,A,3149.30070,N,11706.93208,E,0.052,,310323,,,A,V*15\r\n$GNGGA,020943.00,3149.30070,N,11706.93208,E,1,29,0.54,101.2,M,,M,,*52\r\n$GNGSA,A,3,07,08,09,16,21,27,31,04,194,199,195,50,1.06,0.54,0.91,1*3B\r\n$GNGSA,A,3,02,03,05,10,11,25,36,,,,,,1.06,0.54,0.91,3*0A\r\n$GNGSA,A,3,78,88,67,66,76,86,87,68,,,,,1.06,0.54,0.91,2*0C\r\n$GPGSV,5,1,17,04,48,238,45,07,20,314,40,08,63,215,48,09,37,285,44,0*65\r\n$GPGSV,5,2,17,16,50,034,45,21,09,175,40,27,77,054,48,31,15,124,43,0*63\r\n$GPGSV,5,3,17,18,08,045,,26,24,068,,194,65,058,43,199,51,161,41,0*6A\r\n$GPGSV,5,4,17,195,47,127,43,41,37,232,45,42,45,141,,50,51,161,45,0*59\r\n$GPGSV,5,5,17,40,15,254,41,0*55\r\n$GAGSV,3,1,09,02,21,281,36,03,42,271,40,05,33,201,42,10,34,093,42,0*7F\r\n$GAGSV,3,2,09,11,32,127,37,25,57,330,43,36,09,172,36,08,13,317,36,0*78\r\n$GAGSV,3,3,09,12,26,065,,0*49\r\n$GLGSV,3,1,10,66,13,216,32,67,26,269,37,68,13,319,34,76,40,058,36,0*7D\r\n$GLGSV,3,2, ......')# 数据较多,仅列出部分数据 +``` + +需要注意的是,`quecgnss.read`接口返回值是一个元组,第一个参数是读取的数据长度,第二参数才是读取的数据,调试过程中,如果想让数据看起来整洁清晰,可按如下方式打印: + +```python +>>> print(data[1].decode()) +$GNRMC,020943.00,A,3149.30070,N,11706.93208,E,0.052,,310323,,,A,V*15 +$GNGGA,020943.00,3149.30070,N,11706.93208,E,1,29,0.54,101.2,M,,M,,*52 +$GNGSA,A,3,07,08,09,16,21,27,31,04,194,199,195,50,1.06,0.54,0.91,1*3B +$GNGSA,A,3,02,03,05,10,11,25,36,,,,,,1.06,0.54,0.91,3*0A +$GNGSA,A,3,78,88,67,66,76,86,87,68,,,,,1.06,0.54,0.91,2*0C +$GPGSV,5,1,17,04,48,238,45,07,20,314,40,08,63,215,48,09,37,285,44,0*65 +$GPGSV,5,2,17,16,50,034,45,21,09,175,40,27,77,054,48,31,15,124,43,0*63 +$GPGSV,5,3,17,18,08,045,,26,24,068,,194,65,058,43,199,51,161,41,0*6A +$GPGSV,5,4,17,195,47,127,43,41,37,232,45,42,45,141,,50,51,161,45,0*59 +...... # 数据较多,仅列出部分数据 +``` + +我们需要的定位信息就在上述这些NEMA语句中,比如经纬度坐标、海拔等。后面我们将讲述如何对这些数据进行处理解析,提取出我们需要的信息。 + + + +### 2.1.2 示例代码 + +如下例程,即是按照上述流程编写的较为完整的使用例程。 + +```python + +""" +本例程示范了如何使用quecgnss模块的方法 +例程中设定10s获取一次定位信息,仅为示例,实际可由用户自行决定获取定位信息的周期 +""" +import utime +import quecgnss + + +def main(): + if quecgnss.get_state() == 0: + ret = quecgnss.init() + if ret == 0: + print('GNSS 初始化成功') + else: + print('GNSS 初始化失败,请检查问题') + return -1 + + while True: + gnss_state = quecgnss.get_state() + if gnss_state == 2: + print('GNSS 开始定位') + break + elif gnss_state == 1: + print('GNSS 固件烧录中,请稍后') + utime.sleep(2) + continue + else: + print('GNSS 初始化异常,请检查问题') + return -1 + + while True: + try: + data = quecgnss.read(2048) + except Exception as e: + print('读取NEMA数据异常:{}'.format(e)) + utime.sleep(2) + continue + data_len = data[0] + nema_data = data[1] + if data_len == 0: + print('未读到定位数据,重试中') + utime.sleep(2) + else: + print('===============================================') + print(nema_data.decode()) + print('===============================================') + # 用户自行决定多久获取一次定位数据,此处10s仅为示例 + utime.sleep(10) + # 注[1] + try: + quecgnss.read(4096) + except Exception as e: + print('{}'.format(e)) + utime.sleep(2) + continue + + +if __name__ == '__main__': + main() + +``` + + + +> 注[1] +> +> 上述示例代码中,在休眠一段时间后,读取了4K的定位数据并丢弃了。这段代码的作用如下: +> +> 实际使用中,设备可能会间隔较长一段时间才会获取一次定位数据。有的模组,串口部分底层驱动代码缓存机制是如果缓存满了,就不会再接收新的数据;这就导致如果设备在移动中,间隔较长一段时间才获取一次定位信息时,获取到的是较长时间之前缓存的定位数据,而不是当前位置实时的定位信息。所以为了确保这种情形下,每次获取的定位信息都是实时的,需要在每次获取定位数据之前,先读一次串口数据,把串口缓存中的数据读出并丢弃,休眠1~2s后再读定位数据。 +> +> 如设备获取定位信息较为频繁,可去掉上述代码片段。 + + + +## 2.2 数据解析 + +NEMA语句都是以字符`$`开头,并且以`\r\n`作为结束。而从一堆原始数据中找到我们需要的某一条NEMA语句,本质上利用的就是NEMA语句的该特点,比如正则匹配或是判断字符串头尾。 + +下面以获取经纬度坐标信息为例,说明对NEMA语句的处理解析过程。经纬度坐标信息在RMC和GGA语句中都有包含,下面例程中以从RMC语句中提取经纬度信息为例进行说明。 + +### 2.2.1 解析步骤 + +步骤1:查找相关RMC语句 + +从读取的原始数据中找出RMC语句,这里使用的方式为——先分割再查找。 + +在这种先分割再查找的方式中,需要注意的是,对原始数据的分割,必须使用字符`$`来分割。理论上每一条NEMA语句都应以字符`$`开始,以`\r\n`结束。但在实际的定位数据中,会出现一些NEMA语句不完整的情况,即某一条或几条NEMA语句缺少了一部分,并且直接和下一条语句粘在了一起。比如: + +``` +$GPGSV,4,2,16,21,2$GNRMC,024843.00,A,3149.30313,N,11706.92780,E,0.157,,310323,,,A,V*16 +``` + +这种情况下,使用字符`$`来分割原始字符串数据,可以将这种两条粘在一起的语句分割开,再结合”NEMA语句都应以字符`$`开始,以`\r\n`结束“的特性加以判断,基本可以避免获取到不正常的语句导致后续解析出错的情况。 + +下面示例中data中就是通过`quecgnss.read`接口读出的原始数据,数据如下: + +```python +(2048,'*3B\r\n$GNGSA,A,3,02,11,25,30,34,36,,,,,,,1.20,0.65,$GNRMC,060154.00,A,3149.30510,N,11706.93089,E,0.016,,310323,,,A,V*17\r\n$GNGGA,0$GNRMC,062306.00,A,3149.30472,N,11706.93365,E,0.053,,310323,,,A,V*15\r\n$GNGGA,062306.00,3149.30472,N,11706.93365,E,1,28,0.59,91.9,M,,M,,*6C\r\n$GNGSA,A,3,01,03,07,14,17,21,30,194,199,195,19,06,0.99,0.59,0.80,1*3D\r\n$GNGSA,A,3,02,11,25,30,34,36,15,,,,,,0.99,0.59,0.80,3*07\r\n$GNGSA,A,3,78,80,79,88,82,81,,,,,,,0.99,0.59,0.80,2*0C\r\n$GPGSV,5,1,17,01,63,040,46,03,31,131,42,06,08,220,40,07,26,193,44,0*63\r\n$GPGSV,5,2,17,14,60,326,45,17,39,294,43,19,15,273,43,21,35,041,42,0*6E\r\n$GPGSV,5,3,17,30,43,234,45,08,14,073,,194,61,126,45,199,51,161,40,0*64\r\n$GPGSV,5,4,17,195,69,065,46,42,45,141,,50,51,161,44,40,15,254,41,0*53\r\n$GPGSV,5,5,17,41,37,232,44,0*51\r\n$GAGSV,2,1,07,02,82,078,40,11,12,040,32,15,10,223,35,25,33,129,36,0*7D\r\n$GAGSV,2,2,07,30,37,319,39,34,61,229,44,36,60,034,41,0*4D\r\n$GLGSV,2,1,07,78,52,161,45,79,68,316,41,80,15,330,34,81,61,325,38,0*79\r\n$GLGSV,2,2,07,82,34,252,33,88,26,031,33,65,04,073,,0*43\r\n$GNRMC,062307.00,A,3149.30472,N,11706.93366,E,0.023,,310323,,,A,V*10\r\n$GNGGA,062307.00,3149.30472,N,11706.93366,E,1,28,0.59,91.9,M,,M,,*6E\r\n$GNGSA,A,3,01,03,07,14,17,21,30,194,199,195,19,06,0.99,0.59,0.80,1*3D\r\n$GNGSA,A,3,02,11,25,30,34,36,15,,,,,,0.99,0.59,0.80,3*07\r\n$GNGSA,A,3,78,80,79,88,82,81,,,,,,,0.99,0.59,0.80,2*0C\r\n$GPGSV,5,1,17,01,63,040,46,03,31,131,42,06,08,220,40,07,26,193,44,0*63\r\n$GPGSV,5,2,17,14,60,326,44,17,39,294,45,19,15,273,43,21,35,041,42,0*69\r\n$GPGSV,5,3,17,30,43,234,44,08,14,073,,194,61,126,47,199,51,161,40,0*67\r\n$GPGSV,5,4,17,195,69,065,46,42,45,141,,50,51,161,43,40,15,254,42,0*57\r\n$GPGSV,5,5,17,41,37,232,44,0*51\r\n$GAGSV,2,1,07,02,82,078,40,11,12,040,32,15,10,223,34,25,33,129,36,0*7C\r\n$GAGSV,2,2,07,30,37,319,39,34,61,229,44,36,60,034,42,0*4E\r\n$GLGSV,2,1,07,78,52,161,45,79,68,316,41,80,15,330,35,81,61,325,38,0*78\r\n$GLGSV,2,2,07,82,34,252,33,88,26,031,32,65,04,073,,0*42\r\n$GNRMC,062308.00,A,3149.30473,N,11706.93366,E,0.032,,310323,,,A,V*1E\r\n$GNGGA,062308.00,3149.30473,N,11706.9336') +``` + +解析代码如下: + +```python + +rmc = '' +split_data = data[1].split('$') +for i in split_data: + # 查找RMC语句并进行数据完整性检查 + if i.startswith('GNRMC') and i.endswith('\r\n'): + rmc = i + split_rmc = rmc.split(',') + break +print(rmc) +print(split_rmc) + +############执行结果如下################ +GNRMC,060154.00,A,3149.30510,N,11706.93089,E,0.016,,310323,,,A,V*17 +['GNRMC', '060154.00', 'A', '3149.30510', 'N', '11706.93089', 'E', '0.016', '', '310323', '', '', 'A', 'V*17\r\n'] + +``` + + + +步骤2:语句完整性校验 + +上述解析代码中,判断是否以`GNRMC`开头并且以`\r\n`结尾,本身也是一种完整性检查。如果用户希望更准确的检查某一条NEMA语句的完整性,那就需要对该条语句进行checksum计算,并将计算结果与该条语句中的checksum进行比较,如果相等,则说明该条语句是完整且正确的。 + +这里提供一个校验NEMA语句checksum的接口: + +```python + +""" +功能:checksum计算 +参数说明: +dstr - 需要计算校验和的字符串,应取NEMA语句的”$“字符和”*“字符之间的部分,不包含”$“字符和”*“字符 +返回值:返回十六进制字符串类型的checksum +""" +def checksum(dstr): + lista = list(dstr) + list_len = len(lista) - 1 + tmp = ord(lista[0]) + i = 0 + while i < list_len: + tmp = tmp ^ ord(lista[i+1]) + i = i + 1 + strtmp = str(hex(tmp)) + return strtmp.replace('0x', '') + +""" +功能:NEMA语句checksum确认 +参数说明: +nema - 一条以‘$’字符开始和‘\r\n’字符结束的NEMA语句 +返回值:checksum校验通过返回Ture,否则返回False +""" +def checksum_verify(nema): + find_sck = ure.search("\*(.+?)\r\n", nema) + if find_sck: + sck = find_sck.group(1) + else: + return False + data = ure.search("\$(.+?)\*", nema) + if data: + print('待计算数据:{}'.format(nema)) + ck = checksum(data.group(1)) + print('计算checksum为:{}'.format(ck)) + if ck.upper() == sck.upper(): + return True + else: + return False + else: + return False + +``` + + + +步骤3:确认定位有效性 + +在获取经纬度数据之前,应先确认当前定位数据是有效的,这里是通过检查RMC语句中的`Status`位来判断是否有效,该位表示定位系统状态。当`Status`为字符`A`时,即表示有效。 + +RMC语句格式: + +``` +$RMC,,,,,,,,,,,,,* +``` + +以步骤1中查找到的RMC数据为例: + +```python + +# 通过步骤1可知split_rmc如下 +split_rmc = ['GNRMC', '060154.00', 'A', '3149.30510', 'N', '11706.93089', 'E', '0.016', '', '310323', '', '', 'A', 'V*17\r\n'] + +if split_rmc[2] == 'A': + print('有效定位') +else: + print('无效定位') + +``` + + + +步骤4:提取经纬度坐标 + +确定定位数据有效后,即可提取经纬度相关信息。需注意的是,提取出的数据默认都是字符串,需要转换为浮点数: + +```python + +lat = float(split_rmc[3]) +lon = float(split_rmc[5]) +print('经度信息:{}'.format(lon)) +print('纬度信息:{}'.format(lat)) + +############执行结果如下################ +经度信息:11706.93089 +纬度信息:3149.3051 + +``` + + + +步骤5:将经纬度数据转换为单位为”度“的坐标 + +查阅NEMA语句格式说明可知,NEMA语句中的经纬度格式如下: + +| 字段 | 格式 | 说明 | +| ---- | ----------- | ------------------------------------------------------------ | +| Lat | ddmm.mmmmm | 纬度
dd - 度(00-90)
mm - 分(00-59)
mmmmm - 分的十进制小数 | +| Lon | dddmm.mmmmm | 经度
ddd - 度(000-180)
mm - 分(00-59)
mmmmm - 分的十进制小数 | + +转换计算如下: + +```python + +longitude = lon // 100 + (lon % 100) / 60 +latitude = lat // 100 + (lat % 100) / 60 +print('({}, {})'.format(longitude, latitude)) + +############执行结果如下################ +(117.1155148333333, 31.82175166666667) + +``` + + + +> 此处计算出来的是WGS-84坐标系下经纬度坐标,不可直接用于高德、腾讯、百度等地图上。 + + + +### 2.2.2 示例代码 + +下面示例是基于2.1.2章节中的代码基础上,增加了数据解析部分的代码。 + +```python + +""" +本例程示范了如何使用quecgnss模块的方法 +例程中设定10s获取一次定位信息,仅为示例,实际可由用户自行决定获取定位信息的周期 +""" +import ure +import utime +import quecgnss + +cycle = 10 + +def checksum(dstr): + lista = list(dstr) + list_len = len(lista) - 1 + tmp = ord(lista[0]) + i = 0 + while i < list_len: + tmp = tmp ^ ord(lista[i+1]) + i = i + 1 + strtmp = str(hex(tmp)) + return strtmp.replace('0x', '') + +def checksum_verify(nema): + find_sck = ure.search("\*(.+?)\r\n", nema) + if find_sck: + sck = find_sck.group(1) + else: + return False + data = ure.search("\$(.+?)\*", nema) + if data: + print('待计算数据:{}'.format(nema)) + ck = checksum(data.group(1)) + print('计算checksum为:{}'.format(ck)) + if ck.upper() == sck.upper(): + return True + else: + return False + else: + return False + + +def main(): + global cycle + if quecgnss.get_state() == 0: + ret = quecgnss.init() + if ret == 0: + print('GNSS 初始化成功') + else: + print('GNSS 初始化失败,请检查问题') + return -1 + + while True: + gnss_state = quecgnss.get_state() + if gnss_state == 2: + print('GNSS 开始定位') + break + elif gnss_state == 1: + print('GNSS 固件烧录中,请稍后') + utime.sleep(2) + continue + else: + print('GNSS 初始化异常,请检查问题') + return -1 + + while True: + try: + data = quecgnss.read(2048) + except Exception as e: + print('读取NEMA数据异常:{}'.format(e)) + utime.sleep(2) + continue + data_len = data[0] + nema_data = data[1] + if data_len == 0: + print('未读到定位数据,重试中') + utime.sleep(2) + else: + print('===============================================') + print(nema_data.decode()) + print('===============================================') + split_nema = nema_data.split('$') + for i in split_nema: + if i.startswith('GNRMC') and i.endswith('\r\n'): + split_rmc = i.split(',') + if split_rmc[2] == 'A': + print('获取到有效定位') + # 确认定位有效后,再计算checksum确认语句完整性,避免对无效数据进行计算 + # 用户自行决定是否需要计算,如不需要可屏蔽checksum校验相关代码 + rmc = i.replace('GNRMC', '$GNRMC') + if not checksum_verify(rmc): + continue + print('checksum 校验通过') + + lat = float(split_rmc[3]) + lon = float(split_rmc[5]) + longitude = lon // 100 + (lon % 100) / 60 + latitude = lat // 100 + (lat % 100) / 60 + print('经纬度坐标:({}, {})'.format(longitude, latitude)) + break + else: + continue + # 用户自行决定多久获取一次定位数据,此处10s仅为示例 + utime.sleep(cycle) + # 下面这段代码是否需要,取决于用户获取定位的频繁程度。 + if cycle > 60: + try: + quecgnss.read(4096) + except Exception as e: + print('{}'.format(e)) + utime.sleep(2) + + continue + + +if __name__ == '__main__': + main() + +``` + + + diff --git a/docs/Application_guide/zh/sidebar.yaml b/docs/Application_guide/zh/sidebar.yaml index 8934089b..caf23d1e 100644 --- a/docs/Application_guide/zh/sidebar.yaml +++ b/docs/Application_guide/zh/sidebar.yaml @@ -2,4 +2,6 @@ items: - label: 音频 file: audio/README.md - label: 蓝牙 - file: bluetooth/README.md \ No newline at end of file + file: bluetooth/README.md +- label: GNSS定位 + file: gnss/README.md \ No newline at end of file -- Gitee From 49b3bc359a0fb88e10f042b1157a65d781e3be6e Mon Sep 17 00:00:00 2001 From: "jayceon.fu" Date: Mon, 3 Apr 2023 10:01:13 +0800 Subject: [PATCH 2/2] =?UTF-8?q?docs=20(wifiScan):=20=E5=86=8D=E6=AC=A1?= =?UTF-8?q?=E6=8C=89=E7=85=A7ST=E8=A6=81=E6=B1=82=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 支持wifiScan功能的模组的格式应加灰色底框图; 2. 删除wifiScan.setCfgParam方法下返回值描述中的注的表述中“如果填写了也没有影响”; 3. wifiScan.getCfgParam方法下返回值描述建议优化。 固件版本: N/A 是否需要文案翻译: 是 --- .../wifiScan.md" | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git "a/docs/API_reference/zh/QuecPython\347\261\273\345\272\223/wifiScan.md" "b/docs/API_reference/zh/QuecPython\347\261\273\345\272\223/wifiScan.md" index bb916973..83829cbc 100644 --- "a/docs/API_reference/zh/QuecPython\347\261\273\345\272\223/wifiScan.md" +++ "b/docs/API_reference/zh/QuecPython\347\261\273\345\272\223/wifiScan.md" @@ -2,15 +2,17 @@ `wifiScan`模块提供了同步和异步两种方式来扫描模组周边的WiFi热点信息。 -支持`wifiScan`功能的模组: -EC100Y/EC200N/EC600N/EC600S/EC600M部分系列/EC800M部分系列/EC800N/EG912N/EG915N/EG810M/EC600G/EC800G/EC200U/EC600U部分系列/EG912U/EG915U系列模组。 -EC600M系列模组中,EC600MCN_LC/EC600MCN_LF不支持`wifiScan`; - -EC800M系列模组中,EC800MCN_GC/EC800MCN_LC/EC800MCN_LF不支持`wifiScan`; - -EC600U系列模组中,EC600UEC_AC不支持`wifiScan`。 +> 支持`wifiScan`功能的模组: +> +> EC100Y/EC200N/EC600N/EC600S/EC600M部分系列/EC800M部分系列/EC800N/EG912N/EG915N/EG810M/EC600G/EC800G/EC200U/EC600U部分系列/EG912U/EG915U系列模组。 +> +> EC600M系列模组中,EC600MCN_LC/EC600MCN_LF不支持`wifiScan`; +> +> EC800M系列模组中,EC800MCN_GC/EC800MCN_LC/EC800MCN_LF不支持`wifiScan`; +> +> EC600U系列模组中,EC600UEC_AC不支持`wifiScan`。 @@ -81,7 +83,7 @@ wifiScan.setCfgParam(timeout, round, maxNums[, priority]) -> EC200U/EC600U/EG912U/EG915U/EC600G/EC800G系列模组不支持priority参数,使用时可不填该参数,如果填写了也没有影响。 +> EC200U/EC600U/EG912U/EG915U/EC600G/EC800G系列模组不支持priority参数,使用时可不填该参数。 @@ -95,7 +97,7 @@ wifiScan.getCfgParam() **返回值描述:** -成功返回一个元组,失败返回整型` -1`。返回元组格式如下,返回值参数描述见`wifiScan.setCfgParam`方法的参数描述: +成功返回一个元组,失败返回整型` -1`。返回值参数描述见`wifiScan.setCfgParam`方法的参数描述,返回元组格式如下: `(timeout, round, maxNums, priority)` -- Gitee