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,A,0.00,0.0,261009,*38
$GPVTG,T,K*50
下面分别对每组数据的含义进行分析。
GPS 固定数据输出语句($GPGGA),这是一帧GPS 定位的主要数据,也是使用最广的数据。为了便于理解,下面举例说明$GPGGA语句各部分的含义。
例:$GPGGA,Arial">其标准格式为:
$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 数据格式)
(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,Arial">字段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,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],_ := strconv.ParseFloat(strSlice[3][2:],_ := strconv.ParseFloat(strSlice[5][:3],_ := 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