热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

golang实现从串口读取GPS信息

GPS模块的数据格式对GPS模块的数据处理本质上还是串口通信程序设计,只是GPS模块的输出遵循固定的格式,通过字符串检索查找即可从模块发送的数据中找出需要的数据,常用的GP

GPS模块的数据格式

对GPS模块的数据处理本质上还是串口通信程序设计,只是GPS模块的输出遵循固定的格式,通过字符串检索查找即可从模块发送的数据中找出需要的数据,常用的GPS模块大多采用NMEA-0183 协议。NMEA-0183 是美国国家海洋电子协会(National Marine Electronics Association)所指定的标准规格,这一标准制订所有航海电子仪器间的通讯标准,其中包含传输资料的格式以及传输资料的通讯协议。

以下是一组正常的GPS 数据

$GPGGA,082006.000,3852.9276,N,11527.4283,E,1,08,1.0,20.6,M,,,,0000*35

$GPRMC,082006.000,A,3852.9276,N,11527.4283,E,0.00,0.0,261009,,*38

$GPVTG,0.0,T,,M,0.00,N,0.0,K*50

下面分别对每组数据的含义进行分析。

GPS 固定数据输出语句($GPGGA),这是一帧GPS 定位的主要数据,也是使用最广的数据。为了便于理解,下面举例说明$GPGGA语句各部分的含义。

例:$GPGGA,082006.000,3852.9276,N,11527.4283,E,1,08,1.0,20.6,M,,,,0000*35

其标准格式为:

$GPGGA,(1),(2),(3),(4),(5),(6),(7),(8),(9),M,(10),M,(11),(12)*hh(CR)(LF)

各部分所对应的含义为:

(1) 定位UTC 时间:08 时20 分06 秒

(2) 纬度(格式ddmm.mmmm:即dd 度,mm.mmmm 分);

(3) N/S(北纬或南纬):北纬38 度52.9276 分;

(4) 经度(格式dddmm.mmmm:即ddd 度,mm.mmmm 分);

(5) E/W(东经或西经):东经115 度27.4283 分;

(6) 质量因子(0=没有定位,1=实时GPS,2=差分GPS):1=实时GPS;

(7) 可使用的卫星数(0~8):可使用的卫星数=08;

(8) 水平精度因子(1.0~99.9);水平精度因子=1.0;

(9) 天线高程(海平面,-9999.9~99999.9,单位:m);天线高程=20.6m);

(10) 大地椭球面相对海平面的高度(-999.9~9999.9,单位:m):无;

(11) 差分GPS 数据年龄,实时GPS 时无:无;

(12) 差分基准站号(0000~1023),实时GPS 时无:无;

*总和校验域;hh 总和校验数:35(CR)(LF)回车,换行。

GPRMC(建议使用最小GPS 数据格式)

$GPRMC,082006.000,A,3852.9276,N,11527.4283,E,0.00,0.0,261009,,*38

$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>

(1) 标准定位时间(UTC time)格式:时时分分秒秒.秒秒秒(hhmmss.sss)。

(2) 定位状态,A = 数据可用,V = 数据不可用。

(3) 纬度,格式:度度分分.分分分分(ddmm.mmmm)。

(4) 纬度区分,北半球(N)或南半球(S)。

(5) 经度,格式:度度分分.分分分分。

(6) 经度区分,东(E)半球或西(W)半球。

(7) 相对位移速度, 0.0 至1851.8 knots

(8) 相对位移方向,000.0 至359.9 度。实际值。

(9) 日期,格式:日日月月年年(ddmmyy)。

(10) 磁极变量,000.0 至180.0。

(11) 度数。

(12) Checksum.(检查位)

$GPVTG 地面速度信息

例:$GPVTG,0.0,T,,M,0.00,N,0.0,K*50

字段0:$GPVTG,语句ID,表明该语句为Track Made Good and Ground Speed(VTG)地

面速度信息

字段1:运动角度,000 - 359,(前导位数不足则补0)

字段2:T=真北参照系

字段3:运动角度,000 - 359,(前导位数不足则补0)

字段4:M=磁北参照系

字段5:水平运动速度(0.00)(前导位数不足则补0)

字段6:N=节,Knots

字段7:水平运动速度(0.00)(前导位数不足则补0)

字段8:K=公里/时,km/h

字段9:校验值

以环天BU353 路测USB GPS为例,该GPS模块的主要参数:http://jn.goepe.com/apollo/show_product1.php?id=2164028&uid=zhaogis

GPS模块的驱动安装

驱动程序可以去官网下载,下载地址:http://usglobalsat.com/s-24-support-drivers.aspx

备用下载地址:http://usglobalsat.com/s-172-bu-353-s4-support.aspx

GPS模块的应用程序设计

实现读取并解析GPS信息的代码如下:

package model

import (
//"fmt"
//"infrastructure/log"
//"io"
"math"
"strconv"
"strings"
"time"

"infrastructure/github.com/serial"
)

type GpsInfo struct {
Longitude string
Latitude string
LonDirection string
LatDirection string
LongitudeRadian float64
LatitudeRadian float64
IsGpsNormal bool
}

type ComObject struct {
ComName string
Baudrate int
//Com io.ReadWriteCloser
Com *serial.SerialPort
IsComNormal bool
CloseChan chan bool
}

var (
ComName = "COM3"
ComObj = &ComObject{ComName: ComName, Baudrate: 4800, IsComNormal: false, CloseChan: make(chan bool, 1)}

GpsObj = &GpsInfo{IsGpsNormal: false}
BsGpsObj = &GpsInfo{IsGpsNormal: true, LonDirection: "E", LongitudeRadian: 116.63, LatDirection: "N", LatitudeRadian: 40.32}
UeBsDistance float64 = 0

directiOnMap= map[string]string{
"N": "北纬",
"S": "南纬",
"E": "东经",
"W": "西经",
}
)

func StartGpsModule() {
ComObj.ComName = ComName
if true == ComObj.GetPortName() {
ComName = ComObj.ComName
}
for {
if true == ComObj.IsComNormal {
<-ComObj.CloseChan
ComObj.IsComNormal = false
GpsObj.IsGpsNormal = false
continue
}
time.Sleep(time.Second * 5)
if false == ComObj.GetPortName() {
continue
}
err := ComObj.OpenCom()
if nil != err {
GpsObj.IsGpsNormal = false
continue
} else {
ComObj.IsComNormal = true
}
go ComObj.ReceiveFromCom()
}
}

func (this *ComObject) GetPortName() bool {
ports, err := serial.GetPortsList()
if nil != err || 0 == len(ports) {
return false
}
this.ComName = ports[0]
return true
//for _, port := range ports {
//fmt.Printf("Found port: %v\n", port)
//}
}

func (this *ComObject) OpenCom() (err error) {
mode := &serial.Mode{
BaudRate: 4800,
DataBits: 8,
Parity: serial.PARITY_NONE,
StopBits: serial.STOPBITS_ONE,
}
s, err := serial.OpenPort(this.ComName, mode)
if nil != err {
//log.Error("pkg: model, func: OpenCom, method: goserial.OpenPort, errInfo:", err)
return
}
this.Com = s
return nil
}

func (this *ComObject) Close() {
this.Com.Close()
this.CloseChan <- true
}

func (this *ComObject) ReceiveFromCom() {
defer this.Close()
buf := make([]byte, 512)
for {
time.Sleep(time.Second)
n, err := this.Com.Read(buf[0:])
if nil != err {
//log.Error("pkg: model, func: ReceiveFromCom, method: this.Com.Read, errInfo:", err)
return
}
parseGpsInfo(string(buf[:n]))
//fmt.Println("parseRst:", GpsObj)
}
}

func parseGpsInfo(gpsInfo string) {
var parseSuccessfulFlag bool = false
strLineSlice := strings.Split(gpsInfo, "\n")
if 0 == len(strLineSlice) {
GpsObj.IsGpsNormal = false
return
}
for _, oneLine := range strLineSlice {
if 0 == len(oneLine) {
continue
}
if '$' != oneLine[0] {
continue
}
if !strings.Contains(oneLine, "*") {
continue
}
if !strings.Contains(oneLine, "N") && !strings.Contains(oneLine, "S") {
continue
}
if !strings.Contains(oneLine, "E") && !strings.Contains(oneLine, "W") {
continue
}
if strings.Contains(oneLine, "GPGGA") {
if false == parseLongitudeAndLatitudeFromGpgga(oneLine) {
continue
}
parseSuccessfulFlag = true
break
}
if strings.Contains(oneLine, "GPRMC") {
if false == parseLongitudeAndLatitudeFromGprmc(oneLine) {
continue
}
parseSuccessfulFlag = true
break
}
}
if true == parseSuccessfulFlag {
GpsObj.IsGpsNormal = true
UeBsDistance = CalcDistByLongitudeLantitude(*GpsObj, *BsGpsObj)
} else {
GpsObj.IsGpsNormal = false
UeBsDistance = 0
}
}

func parseLongitudeAndLatitudeFromGpgga(gpggaInfo string) bool {
strSlice := strings.Split(gpggaInfo, ",")
if 3 > len(strSlice[2]) || 4 > len(strSlice[4]) {
return false
}
GpsObj.LatDirection = strSlice[3]
GpsObj.LOnDirection= strSlice[5]
GpsObj.Latitude = directionMap[strSlice[3]] + strSlice[2][:2] + "度" + strSlice[2][2:] + "分"
GpsObj.LOngitude= directionMap[strSlice[5]] + strSlice[4][:3] + "度" + strSlice[4][3:] + "分"

tmpIntPartLat, _ := strconv.ParseFloat(strSlice[2][:2], 32)
tmpDecimalPartLat, _ := strconv.ParseFloat(strSlice[2][2:], 32)
GpsObj.LatitudeRadian = tmpIntPartLat + tmpDecimalPartLat/60

tmpIntPartLon, _ := strconv.ParseFloat(strSlice[4][:3], 32)
tmpDecimalPartLon, _ := strconv.ParseFloat(strSlice[4][3:], 32)
GpsObj.LOngitudeRadian= tmpIntPartLon + tmpDecimalPartLon/60
return true
}

func parseLongitudeAndLatitudeFromGprmc(gprmcInfo string) bool {
strSlice := strings.Split(gprmcInfo, ",")
if 3 > len(strSlice[3]) || 4 > len(strSlice[5]) {
return false
}
GpsObj.LatDirection = strSlice[4]
GpsObj.LOnDirection= strSlice[6]
GpsObj.Latitude = directionMap[strSlice[4]] + strSlice[3][:2] + "度" + strSlice[3][2:] + "分"
GpsObj.LOngitude= directionMap[strSlice[6]] + strSlice[5][:3] + "度" + strSlice[5][3:] + "分"

tmpIntPartLat, _ := strconv.ParseFloat(strSlice[3][:2], 32)
tmpDecimalPartLat, _ := strconv.ParseFloat(strSlice[3][2:], 32)
GpsObj.LatitudeRadian = tmpIntPartLat + tmpDecimalPartLat/60

tmpIntPartLon, _ := strconv.ParseFloat(strSlice[5][:3], 32)
tmpDecimalPartLon, _ := strconv.ParseFloat(strSlice[5][3:], 32)
GpsObj.LOngitudeRadian= tmpIntPartLon + tmpDecimalPartLon/60
return true
}

func CalcDistByLongitudeLantitude(gpsPointA, gpsPointB GpsInfo) (distance float64) {
if false == gpsPointA.IsGpsNormal || false == gpsPointB.IsGpsNormal {
return 0
}
lonA, latA := getFormatedLongitudeLantitude(gpsPointA)
lonB, latB := getFormatedLongitudeLantitude(gpsPointB)
c := math.Sin(latA*math.Pi/180)*math.Sin(latB*math.Pi/180)*math.Cos((lonA-lonB)*math.Pi/180) + math.Cos(latA*math.Pi/180)*math.Cos(latB*math.Pi/180)
distance = 6371004 * math.Acos(c)
return
}

func getFormatedLongitudeLantitude(gpsPoint GpsInfo) (lon, lat float64) {
if "E" == gpsPoint.LonDirection {
lon = gpsPoint.LongitudeRadian
} else {
lon = 0 - gpsPoint.LongitudeRadian
}
if "N" == gpsPoint.LatDirection {
lat = 90 - gpsPoint.LatitudeRadian
} else {
lat = 90 + gpsPoint.LatitudeRadian
}
return
}

可参考:http://studygolang.com/articles/302

golang实现对串口的操作的库:https://github.com/huin/goserial


推荐阅读
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • imx6ull开发板驱动MT7601U无线网卡的方法和步骤详解
    本文详细介绍了在imx6ull开发板上驱动MT7601U无线网卡的方法和步骤。首先介绍了开发环境和硬件平台,然后说明了MT7601U驱动已经集成在linux内核的linux-4.x.x/drivers/net/wireless/mediatek/mt7601u文件中。接着介绍了移植mt7601u驱动的过程,包括编译内核和配置设备驱动。最后,列举了关键词和相关信息供读者参考。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • 本文讨论了编写可保护的代码的重要性,包括提高代码的可读性、可调试性和直观性。同时介绍了优化代码的方法,如代码格式化、解释函数和提炼函数等。还提到了一些常见的坏代码味道,如不规范的命名、重复代码、过长的函数和参数列表等。最后,介绍了如何处理数据泥团和进行函数重构,以提高代码质量和可维护性。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
author-avatar
小小贤
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有