gis
gis
管理员
管理员
  • 注册日期2003-07-16
  • 发帖数15951
  • QQ
  • 铜币25345枚
  • 威望15368点
  • 贡献值0点
  • 银元0个
  • GIS帝国居民
  • 帝国沙发管家
  • GIS帝国明星
  • GIS帝国铁杆
阅读:1531回复:0

设计文档: GPS应用开发

楼主#
更多 发布于:2005-08-25 14:18
<P><B>关键字:</B></P>
<P>GPS:Global Position System, 全球定位系统</P>
<P>NMEA:National Marine Electronics Association 全国海洋电子协会</P>
<P>SiFR:u-blox公司的GPS模块二进制数据协议</P>
<P>WGS 84:World Geodetic System 1984</P>
<P>LLA: Longitude/Latitude/Altitude经纬度和海拔</P>
<P> </P>
<P><B>简述:</B></P>
<P>本文介绍采用GPS接受模块,GIS控件开发GPS/GIS应用软件的一些要点。不涉及复杂的地理信息理论,主要是针对协议和接口的应用开发</P>
<P> </P>
<H2>一:GPS 模块应用</H2>
<P> </P>
<P  align=left>GPS(全球定位系统)接受硬件上已经模块化,这里主要基于我使用过的u-blox TIM GPS(以下简称TimGPS)来介绍。</P>
<P  align=left>TimGPS模块如图(几乎是1:1比例):</P>
<P  align=left><IMG src="http://photos11.flickr.com/14743635_cbc86d6772_o.jpg" align=left>此模块提供和GPS卫星通信的功能,对外提供FFC20接口,可以给它增加电源,接口板,串口模块,使之可以通过串口和计算机连接。</P>
<P  align=left>如何开发串口程序在本文中不再介绍,无论在windows/linux下开发串口串口程序都是很简单的事情。</P>
<P  align=left> </P>
<P  align=left>TimGPS对外提供标准NMEA协议和厂家自己的SiRF二进制协议。</P>
<P  align=left>1.1 NMEA(National Marine Electronics Association) 0183协议</P>
<P  align=left>NMEA 0183是一种航海、海运方面有关于数字信号传递的标准,此标准定义了电子信号所需要的传输协议,传输数据时间。这个协议是文本格式的。大致如下</P>
<TABLE  cellSpacing=0 cellPadding=0 border=1>

<TR >
<TD  vAlign=top width=95>
<P>Start Sequence</P></TD>
<TD  vAlign=top width=256>
<P>Payload</P></TD>
<TD  vAlign=top width=78>
<P>Checksum</P></TD>
<TD  vAlign=top width=90>
<P>End Sequence</P></TD></TR>
<TR >
<TD  vAlign=top width=95>
<P  align=left>消息头</P>
<P  align=right> </P></TD>
<TD  vAlign=top width=256>
<P>消息体,具体数据和NMEA消息字相关,数据之间用逗号(,)间隔。</P>
<P  align=left>2243.4976,N,11414.7289,E,091828.819,A</P>
<P  align=left> </P></TD>
<TD  vAlign=top width=78>
<P  align=left>校验码,格式为*[16进制数]如*2C</P></TD>
<TD  vAlign=top width=90>
<P  align=left>回车换行</P>
<P  align=left><CR> <LF>4</P>
<P  align=left> </P></TD></TR></TABLE>
<P  align=left>协议头,格式为输出:$GPxxx,xxx为输出消息字如GLL.输入:$PSRF<MID>MID为输入消息字.</P>
<H4>1.1.1NMEA协议输出和输入消息</H4>
<P>NMEA协议有以下输出消息</P>
<TABLE  cellSpacing=0 cellPadding=0 border=1>

<TR>
<TD  vAlign=top width=97>
<P>消息字</P></TD>
<TD  vAlign=top width=422>
<P>内容</P></TD></TR>
<TR>
<TD  vAlign=top width=97>
<P>GGA </P></TD>
<TD  vAlign=top width=422>
<P>时间,位置,定位数据</P></TD></TR>
<TR>
<TD  vAlign=top width=97>
<P>GLL</P></TD>
<TD  vAlign=top width=422>
<P>经纬度,UTC格式时间,位置和状态数据</P></TD></TR>
<TR>
<TD  vAlign=top width=97>
<P>GSA</P></TD>
<TD  vAlign=top width=422>
<P>接收机模式和卫星工作数据,包括位置和水平/竖直稀释精度等。稀释精度(Dilution of Precision)是个地理定位术语.一个接收器可以在同一时间得到许多颗卫星定位信息,但在精密定位上,只要四颗卫星讯号即已足够了</P></TD></TR>
<TR>
<TD  vAlign=top width=97>
<P>GSV</P></TD>
<TD  vAlign=top width=422>
<P>接收机能接收到的卫星信息,包括卫星ID,海拔,方位角,信噪比等</P></TD></TR>
<TR>
<TD  vAlign=top width=97>
<P>MSS</P></TD>
<TD  vAlign=top width=422>
<P>信号比(SNR),信号强度,频率,比特率</P></TD></TR>
<TR>
<TD  vAlign=top width=97>
<P>RMC</P></TD>
<TD  vAlign=top width=422>
<P>日期,时间,位置,方向,速度数据。是最常用的一个消息</P></TD></TR>
<TR>
<TD  vAlign=top width=97>
<P>VTG</P></TD>
<TD  vAlign=top width=422>
<P>相对地面的方向和速度数据</P></TD></TR>
<TR>
<TD  vAlign=top width=97>
<P>ZDA</P></TD>
<TD  vAlign=top width=422>
<P>时间和日期数据。</P></TD></TR></TABLE>
<P  align=left> </P>
<P>和地理信息密切的相关的消息如下,各消息之间包含的信息字段有出入也有重复,在一轮消息循环里,各消息相同的字段中包含相同的地理数据。可以综合多个消息,来获取完整的数据。</P>
<TABLE  cellSpacing=0 cellPadding=0 width=526 border=0>

<TR >
<TD  noWrap width=42>
<P  align=left> </P></TD>
<TD  noWrap width=44>
<P  align=left>日期</P></TD>
<TD  noWrap width=44>
<P  align=left>时间</P></TD>
<TD  noWrap width=44>
<P  align=left>纬度</P></TD>
<TD  noWrap width=44>
<P  align=left>经度</P></TD>
<TD  noWrap width=44>
<P  align=left>海拔</P></TD>
<TD  noWrap width=75>
<P  align=left>定位状态</P></TD>
<TD  noWrap width=63>
<P  align=left>卫星数</P></TD>
<TD  noWrap width=70>
<P  align=left>地面速度</P></TD>
<TD  noWrap width=56>
<P  align=left>方向角</P></TD></TR>
<TR >
<TD  noWrap width=42>
<P  align=left>GGA </P></TD>
<TD  noWrap width=44>
<P  align=left><B> </B></P></TD>
<TD  noWrap width=44>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=44>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=44>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=44>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=75>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=63>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=70>
<P  align=left> </P></TD>
<TD  noWrap width=56>
<P  align=left> </P></TD></TR>
<TR >
<TD  noWrap width=42>
<P  align=left>GLL</P></TD>
<TD  noWrap width=44>
<P  align=left><B> </B></P></TD>
<TD  noWrap width=44>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=44>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=44>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=44>
<P  align=left> </P></TD>
<TD  noWrap width=75>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=63>
<P  align=left> </P></TD>
<TD  noWrap width=70>
<P  align=left> </P></TD>
<TD  noWrap width=56>
<P  align=left> </P></TD></TR>
<TR >
<TD  noWrap width=42>
<P  align=left>RMC</P></TD>
<TD  noWrap width=44>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=44>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=44>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=44>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=44>
<P  align=left> </P></TD>
<TD  noWrap width=75>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=63>
<P  align=left> </P></TD>
<TD  noWrap width=70>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=56>
<P  align=left><B>√</B></P></TD></TR>
<TR >
<TD  noWrap width=42>
<P  align=left>VTG</P></TD>
<TD  noWrap width=44>
<P  align=left> </P></TD>
<TD  noWrap width=44>
<P  align=left> </P></TD>
<TD  noWrap width=44>
<P  align=left> </P></TD>
<TD  noWrap width=44>
<P  align=left> </P></TD>
<TD  noWrap width=44>
<P  align=left> </P></TD>
<TD  noWrap width=75>
<P  align=left> </P></TD>
<TD  noWrap width=63>
<P  align=left> </P></TD>
<TD  noWrap width=70>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=56>
<P  align=left><B>√</B></P></TD></TR>
<TR >
<TD  noWrap width=42>
<P  align=left>ZDA</P></TD>
<TD  noWrap width=44>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=44>
<P  align=left><B>√</B></P></TD>
<TD  noWrap width=44>
<P  align=left> </P></TD>
<TD  noWrap width=44>
<P  align=left> </P></TD>
<TD  noWrap width=44>
<P  align=left> </P></TD>
<TD  noWrap width=75>
<P  align=left> </P></TD>
<TD  noWrap width=63>
<P  align=left> </P></TD>
<TD  noWrap width=70>
<P  align=left> </P></TD>
<TD  noWrap width=56>
<P  align=left> </P></TD></TR></TABLE>
<P> </P>
<P>以下为u-lox厂商扩充的输出消息</P>
<TABLE  cellSpacing=0 cellPadding=0 border=1>

<TR>
<TD  vAlign=top width=100>
<P>PSRF150</P></TD>
<TD  vAlign=top width=355>
<P  align=left>OK-to-send指令,在节电模式中表示进入工作状态</P>
<P> </P></TD></TR>
<TR>
<TD  vAlign=top width=100>
<P>PSRF161</P></TD>
<TD  vAlign=top width=355>
<P>硬件状态报告</P></TD></TR></TABLE>
<P> </P>
<P>工作在NMEA模式时,TimGPS可以有以下输入消息.输入消息一般是用于控制GPS的运行的。</P>
<TABLE  cellSpacing=0 cellPadding=0 width="54%" border=0>

<TR >
<TD  noWrap width="17%">
<P  align=left>消息字</P></TD>
<TD  noWrap width="82%">
<P  align=left>内容</P></TD></TR>
<TR >
<TD  noWrap width="17%">
<P  align=right>100</P></TD>
<TD  noWrap width="82%">
<P  align=left>设置串口参数和协议</P></TD></TR>
<TR >
<TD  noWrap width="17%">
<P  align=right>101</P></TD>
<TD  noWrap width="82%">
<P  align=left>XYZ导航坐标初始化。输入参数按 WGS84坐标系确定</P></TD></TR>
<TR >
<TD  noWrap width="17%">
<P  align=right>102</P></TD>
<TD  noWrap width="82%">
<P  align=left>设置DGPS端口</P></TD></TR>
<TR >
<TD  noWrap width="17%">
<P  align=right>103</P></TD>
<TD  noWrap width="82%">
<P  align=left>设置或查询数据输出频率。可以设置每个NMEA消息的是否输出和输出频率</P></TD></TR>
<TR >
<TD  noWrap width="17%">
<P  align=right>104</P></TD>
<TD  noWrap width="82%">
<P  align=left>输入当前经纬度和海拔来初始化模块</P></TD></TR>
<TR >
<TD  noWrap width="17%">
<P  align=right>105</P></TD>
<TD  noWrap width="82%">
<P  align=left>开发调试数据开关</P></TD></TR>
<TR >
<TD  noWrap width="17%">
<P  align=right>106</P></TD>
<TD  noWrap width="82%">
<P  align=left>(u-blox)可以改变大地基准坐标系,默认是WGS-84坐标系</P></TD></TR>
<TR >
<TD  noWrap width="17%">
<P  align=right>107</P></TD>
<TD  noWrap width="82%">
<P>(u-blox)配置节电模式</P></TD></TR>
<TR >
<TD  noWrap width="17%">
<P  align=right>108</P></TD>
<TD  noWrap width="82%">
<P  align=left>(u-blox)进入下载模式,更新Flash</P></TD></TR>
<TR >
<TD  noWrap width="17%">
<P  align=left>MSK</P></TD>
<TD  noWrap width="82%">
<P  align=left>设置MSK信号接收机参数</P></TD></TR></TABLE>
<P> </P>
<P>每个协议的具体格式,可以到网上下载具体的NMEA协议。</P>
<P> </P>
<H4>1.1.2 NMEA消息的接收和解析</H4>
<P>当接收机工作在NMEA协议时,以一定的速率向外输出数据。如</P>
<P>$GPGGA,140144.000,2232.2513,N,11401.6763,E,1,04,,20.3,M,,M,,0000*58</P>
<P>$GPGLL,2232.2513,N,11401.6763,E,140144.000,A*35</P>
<P>$GPRMC,140144.000,A,2232.2513,N,11401.6763,E,9.17,,140703,,*12</P>
<P>$GPVTG,,T,,M,9.17,N,17.0,K*47</P>
<P>$GPGGA,140148.000,2232.2654,N,11401.6738,E,1,04,,36.1,M,,M,,0000*5F</P>
<P>$GPGLL,2232.2654,N,11401.6738,E,140148.000,A*37</P>
<P>$GPRMC,140148.000,A,2232.2654,N,11401.6738,E,12.95,,140703,,*20</P>
<P>$GPVTG,,T,,M,12.95,N,24.0,K*77</P>
<P> </P>
<P>最好将所有的消息输出速率设置为相同的(使用输入指令103),或者根据具体情况打开或关闭一些不不需要的消息。</P>
<P>消息里各字段的格式都不复杂,以下几个稍微注意:</P>
<P >l          经纬度的表示法为ddmm.mmmm和方向指示,以纬度举例:当纬度为9730.765,方向指示为N,则表示为北半球的97度30.765分.有的地理信息组件使用经纬度的时候不用度分秒表示法,而是用浮点数表示法,那么97度30.765分就约等于97.5(97+30.765/60)度。</P>
<P >l          方向角以正北向为0度,正东向为90度,也就是顺时针方向从0度增加到359度</P>
<P >l          校验码为一个消息包(包括消息头和体)里每个ASCII字符的值依次进行异或得到,算法(c++代码)如下</P>
<P>     unsigned char CheckSum(string s)</P>
<P>     {</P>
<P>         unsigned char c = 0;</P>
<P>         for(int o=1;o<s.size();o++)</P>
<P>         {</P>
<P>              unsigned char h = s[o];</P>
<P>              c ^= h;</P>
<P>         }</P>
<P>         return c;</P>
<P>     }</P>
<P>     sprintf(StrCheckSum,"*%02X",CheckSum(data) );</P>
<P> </P>
<P>一般的地理数据,只取RMC消息包就够了,如果还需要其他的数据,比如当前卫星数,卫星状态等等,又或者接受机本身关闭了RMC消息包,只输出其他的消息包,那么就需要解析其他消息包。</P>
<P>应用程序逐行分析消息包。我的做法是先将消息包按逗号分割原则解析成一个字符数组,然后根据包头指令字,按协议含义取数组里的字符串。</P>
<P>ParseMessage(sMessage,Array)); //将一行消息解析到字符串数组Array里</P>
<P>if (Array[0].CompareTo("$GPRMC") ==0  ) //是RMC消息</P>
<P>{</P>
<P>    if(Array.size()!=12) //要求有12个字段</P>
<P>         return -1;</P>
<P>   //以下依次填充RMC数据结构</P>
<P>    recRMC Rmc</P>
<P>    Rmc.UTCTime                  = Array[1];</P>
<P>    Rmc.Status                   = Str2Char(Array[2]);</P>
<P>    Rmc.Latitude                 = Array[3];</P>
<P>    Rmc.Latitude.DirIndicator    = Str2Char(Array[4]);</P>
<P>    Rmc.Longitude                = Array[5];</P>
<P>    Rmc.Longitude.DirIndicator   = Str2Char(Array[6]);</P>
<P>    Rmc.SpeedOverGround          = Str2Num(Array[7]);</P>
<P>    Rmc.CourseOverGround         = Str2Num(Array[8]);</P>
<P>    Rmc.Date                    = Array[9];</P>
<P>    Rmc.Magnetic[0]              = Str2Char(Array[10]);</P>
<P>    Rmc.Checksum                 = (int)Str2Hex(Array[11]);</P>
<P>     //检查CheckSum,</P>
<P>    return  CheckSum(Rmc.CheckSum,sMessage);</P>
<P>}</P>
<P>以类似的方法构造解析其他消息的函数。组消息包则是依次将字段转成字符串连接起来,最后附加校验码即可。</P>
<P> </P>
<H2>1.2 SiFR协议.</H2>
<P>SiFR协议是GPS模块厂商制定的二进制协议,提供了对GPS模块操作的更多接口,厂家号称此协议(用于操作本家的模块)更稳定,更高效,更容易操作。</P>
<H4>1.2.1协议格式</H4>
<TABLE  cellSpacing=0 cellPadding=0 border=0>

<TR >
<TD  noWrap>
<P  align=left>消息头</P></TD>
<TD  noWrap>
<P  align=left>消息体长度</P></TD>
<TD  noWrap>
<P  align=left>消息体</P></TD>
<TD  noWrap>
<P  align=left>校验码</P></TD>
<TD  noWrap>
<P  align=left>消息结束符</P></TD></TR>
<TR >
<TD >
<P  align=left>双字节</P>
<P  align=left>0xA0</P>
<P  align=left>0xA2</P></TD>
<TD >
<P  align=left>双字节</P></TD>
<TD >
<P  align=left>长度小于1023字节的数据</P></TD>
<TD >
<P  align=left>双字节</P></TD>
<TD >
<P  align=left>双字节</P>
<P  align=left>0xB0</P>
<P  align=left>0xB3</P></TD></TR></TABLE>
<P>具体消息格式可以参考厂家提供的协议文档(GPS.G2-X-01003-E.pdf)。</P>
<H4>1.2.2 日志操作</H4>
<P>SiFR包括了读取地理数据,刷写Flash,配置GPS模块参数,读取/擦写日志等一系列的操作。以日志操作最为有用。TimGPS可以脱离计算机运行,将所到之处的地理信息保存在内部存储器里,然后再接上计算机,获取地理信息日志,得到携带该GPS的载体在一段时间里运行轨迹。</P>
<P>读取Log的大致过程是 :</P>
<P>读取Log Sector信息,得到最小Sector号和最大Sector号,依次发指令读取每个Sector的Log信息,并解析转换成NMEA协议消息。</P>
<P>擦除Log时,虽然u-blox提供了全部擦除指令,实际应用的时候发现该指令成功率不高。还是依次发指令逐个擦除比较好。</P>
<P>TimGPS的log格式定义在厂家提供的TIM_Data_Logger_Manual(GPS.G2-SW-02015).pdf里。</P>
<P>日志中包含的消息如下</P>
<TABLE  cellSpacing=0 cellPadding=0 width=526 border=0>

<TR >
<TD  noWrap width=57>
<P>消息号</P></TD>
<TD  noWrap width=89>
<P>消息名</P></TD>
<TD  noWrap width=51>
<P>字节数</P></TD>
<TD  noWrap width=329>
<P>内容</P></TD></TR>
<TR >
<TD  noWrap width=57>
<P>111</P></TD>
<TD  noWrap width=89>
<P>NONE  </P></TD>
<TD  noWrap width=51>
<P>1</P></TD>
<TD  noWrap width=329>
<P>无数据</P></TD></TR>
<TR >
<TD  noWrap width=57>
<P>100</P></TD>
<TD  noWrap width=89>
<P>FIX_FULL</P></TD>
<TD  noWrap width=51>
<P>9</P></TD>
<TD  noWrap width=329>
<P>全部地理定位信息</P></TD></TR>
<TR >
<TD  noWrap width=57>
<P>100</P></TD>
<TD  noWrap width=89>
<P>FIX_INCL</P></TD>
<TD  noWrap width=51>
<P>5</P></TD>
<TD  noWrap width=329>
<P>地理定位信息,增量数据</P></TD></TR>
<TR >
<TD  noWrap width=57>
<P>000</P></TD>
<TD  noWrap width=89>
<P>FIX_INCM</P></TD>
<TD  noWrap width=51>
<P>4</P></TD>
<TD  noWrap width=329>
<P>地理定位信息,增量数据, 在字节上比FIX_INCL少一个</P></TD></TR>
<TR >
<TD  noWrap width=57>
<P>110</P></TD>
<TD  noWrap width=89>
<P>FIX_INCS</P></TD>
<TD  noWrap width=51>
<P>3</P></TD>
<TD  noWrap width=329>
<P>地理定位信息,增量数据,使用更少的字节数</P></TD></TR>
<TR >
<TD  noWrap width=57>
<P>101</P></TD>
<TD  noWrap width=89>
<P>GPIO_FULL</P></TD>
<TD  noWrap width=51>
<P>3  </P></TD>
<TD  noWrap width=329>
<P>GPIO( General-purpose_input/output)数据</P></TD></TR>
<TR >
<TD  noWrap width=57>
<P>011</P></TD>
<TD  noWrap width=89>
<P>GPIO_INC</P></TD>
<TD  noWrap width=51>
<P>2</P></TD>
<TD  noWrap width=329>
<P>GPIO数据,增量数据</P></TD></TR>
<TR >
<TD  noWrap width=57>
<P>001</P></TD>
<TD  noWrap width=89>
<P>ESCAPE</P></TD>
<TD  noWrap width=51>
<P  align=left>不定</P></TD>
<TD  noWrap width=329>
<P>用于扩展</P></TD></TR></TABLE>
<P  align=left>以上所有消息都是二进制编码,按位存储,解析消息需要使用二进制位操作。</P>
<P  align=left>消息解析成功后,u-blox建议按以下逻辑处理这些消息</P>
<P  align=left> </P>
<P  align=left>WHILE (Data) --读入新数据</P>
<P  align=left>--获取消息类型,并根据消息类型得到对应消息的字节数</P>
<P  align=left>IF (Type = EMPTY) THEN --  空数据,跳过</P>
<P  align=left>ELSE IF (Type = FIX_FULL) THEN - 保存时间信息,保存位置,速度和其他模式信息</P>
<P  align=left>ELSE IF ((Type = FIX_INCL) OR (Type = FIX_INCM) OR (Type = FIX_INCS)) THEN</P>
<P  align=left>-- 将时间和位置增量数据叠加到最近一个定位记录上,保存速度数据</P>
<P  align=left>ELSE IF (Type = GPIO_FULL) THEN -- 保存时间和GPIO数据</P>
<P  align=left>ELSE IF (Type = GPIO_INC) THEN  -- 叠加时间到最近一个GPIO数据的时间字段上,保存GPIO数据</P>
<P  align=left>ELSE IF (Type = ESCAPE) THEN   --处理自定义数据</P>
<P  align=left>-- END IF</P>
<P  align=left>END WHILE</P>
<P> </P>
<P> </P>
<H4>1.2.3WGS84->LLA转换问题</H4>
<P>在分析其日志的过程中,遇到一个问题,就是TimGPS二进制日志保存的坐标数据是以WGS84大地坐标系为准的,大地坐标系的XYZ轴如下:</P>
<P> </P>
<P><IMG src="http://photos10.flickr.com/14743637_225dc2f557_o.jpg"></P>
<P>要得到经纬度和海拔(LLA:Longitude/Latitude/Altitude)坐标,需要一些椭球体、基准面及地图投影的知识。</P>
<P>后来从网上找到一个Fortune77写的WGS84->GLL换算函数,将其转成C/C++的函数:</P>
<P>void wgsxyz2lla(double x,double y,double z,double *lat,double *lon,double *alt)</P>
<P>{</P>
<P>         double pi = 3.14159265357;</P>
<P>         longA_EARTH = 6378137;</P>
<P>         double  flattening = 1/298.257223563;</P>
<P>         double NAV_E2 = (2-flattening)*flattening; // also e^2</P>
<P>         double rad2deg = 180/pi;</P>
<P>         double wlon,wlat,walt,rhosqrd,rho,templat,tempalt,rhoerror,zerror;</P>
<P>         double slat,clat,q,r_n,drdl,invdet,aa,bb,cc,dd;</P>
<P>         if ((x == 0.0) ; (y == 0.0))</P>
<P>                   wlon = 0.0;</P>
<P>         else</P>
<P>                   wlon = atan2(y, x)*rad2deg;</P>
<P>         if ((x == 0.0) ; (y == 0.0) ; (z == 0.0))</P>
<P>         {</P>
<P>                   printf("WGS xyz at center of earth");</P>
<P>                   wlon = 360;</P>
<P>                   wlat = 360;</P>
<P>                   walt = 6378137;</P>
<P>         }</P>
<P>         else </P>
<P>         {</P>
<P>                   rhosqrd = x*x + y*y;</P>
<P>                   rho = sqrt(rhosqrd);</P>
<P>                   templat = atan2(z, rho);</P>
<P>                   tempalt = sqrt(rhosqrd + z*z) - A_EARTH;</P>
<P>                   rhoerror = 1000.0;</P>
<P>                   zerror   = 1000.0;</P>
<P> </P>
<P>                   while ((Dabs(rhoerror) > 1e-6) | (Dabs(zerror) > 1e-6)) </P>
<P>                   {</P>
<P>                            slat = sin(templat);</P>
<P>                            clat = cos(templat);</P>
<P>                            q = 1 - NAV_E2*slat*slat;</P>
<P>                            r_n = A_EARTH/sqrt(q);</P>
<P>                            drdl = r_n*NAV_E2*slat*clat/q; // d(r_n)/d(latitutde) </P>
<P>                            rhoerror = (r_n + tempalt)*clat - rho;</P>
<P>                            zerror   = (r_n*(1 - NAV_E2) + tempalt)*slat - z;</P>
<P>                            aa = drdl*clat - (r_n + tempalt)*slat;</P>
<P>                            bb = clat;</P>
<P>                            cc = (1 - NAV_E2)*(drdl*slat + r_n*clat);</P>
<P>                            dd = slat;</P>
<P>                            invdet = 1.0/(aa*dd - bb*cc);</P>
<P>                            templat = templat - invdet*(+dd*rhoerror -bb*zerror);</P>
<P>                            tempalt = tempalt - invdet*(-cc*rhoerror +aa*zerror);</P>
<P>                   }</P>
<P>                   wlat = templat*rad2deg;</P>
<P>                   walt = tempalt;</P>
<P>         }</P>
<P>         if(lon )*lon = wlon;</P>
<P>         if(lat )*lat = wlat;</P>
<P>         if(alt )*alt = walt;</P>
<P>}</P>
<P> </P>
<P>结语:开发GPS应用软件的要点在于理解相关的协议和接口,仔细处理协议字段。</P>
喜欢0 评分0
GIS麦田守望者,期待与您交流。
游客

返回顶部