前几天,有朋友托我给他写个GPS程序,就取经纬度坐标,以及将之转换成高斯直角坐标。花了一些时间,给他做了个小程序。
后来总结时,想起,很多网上朋友都会问及关于GPS开发的一些事。我这里先将我的程序解释下,然后再总结下,相关经验及个人看法。
目前在一些移动设备中,都提供GPS功能,设备中都需要一个接收器,用来接收GPS信号。(类似于GPRS工作方式)。GPS一旦启动后,会自动连接卫星,接收信号,通过算法计算出位置等信息,然后以NMEA data的格式输出。GPS receiver就是接收卫星信号转换成NMEA data的设备。
开发GPS有3种选择:
1。直接使用串口连接GPS接收器
2。GPS Intermediate Driver
2。使用第三方类库(目前opennetcf提供相应类库)
目前,WM5.0以上系统,都内置了GPS Intermediate Driver。通过它,我们能够很方便的取道GPS数据。
关于GPS方面的文章可以参考:
1。30 Days of .NET [Windows Mobile Applications] - Day 03: GPS Compass(GPS指南针)
2。.NET Compact Framework下的GPS NMEA data数据分析
虽然GPS Intermediate Driver提供了我们非常快捷的取得GPS信息,但同时也有一定的弊端。
那下面我讲介绍我如何在该项目中使用GPS的。
我使用GPS Intermediate Driver,它能够快速开发,MS也提供了很强大的例子来方便我们使用。
在微软的WM SDK安装目录下有GPS工程。(Windows Mobile 6 SDK\Samples\PocketPC\CS\GPS)
该Demo中
GPS.cs:封装了GPS的操作类,比如Open(),Close(),Connect()。可以很快捷的使用。
GpsDeviceState.cs:用于取得目前GPS设备的状态信息。
GpsPosition.cs:每次GPS数据取得后,都会放入该类。
LocationChangedEventArgs.cs:一旦位置改变,即可将新的GPSPosition取得到。
public
void
Open()
{
if
(
!
Opened)
{
//
create handles for GPS events
newLocationHandle
=
CreateEvent(IntPtr.Zero,
0
,
0
,
null
);
deviceStateChangedHandle
=
CreateEvent(IntPtr.Zero,
0
,
0
,
null
);
stopHandle
=
CreateEvent(IntPtr.Zero,
0
,
0
,
null
);
gpsHandle
=
GPSOpenDevice(newLocationHandle, deviceStateChangedHandle,
null
,
0
);
//
if events were hooked up before the device was opened, we'll need
//
to create the gps event thread.
if
(locationChanged
!=
null
||
deviceStateChanged
!=
null
)
{
CreateGpsEventThread();
}
}
}
通过调用CreateEvent,创建handles,然后调用GPSOpenDevice API,将handle传入,取到gps设备的handle
得到handle后,再创建一个线程来监听GPS数据及设备状态。通过调用CreateGpsEventThread方法。
private
void
CreateGpsEventThread()
{
//
we only want to create the thread if we don't have one created already
//
and we have opened the gps device
if
(gpsEventThread
==
null
&&
gpsHandle
!=
IntPtr.Zero)
{
//
Create and start thread to listen for GPS events
gpsEventThread
=
new
System.Threading.Thread(
new
System.Threading.ThreadStart(WaitForGpsEvents));
gpsEventThread.Start();
}
}
在WaitForGpsEvents方法中,就while不听的监听。
当deviceStateChanged,就调用deviceStateChanged事件,然后取得当前设备状态。
当locationChanged,就调用locationChanged事件,取得当前坐标。
这样一个基本的GPS数据取得流程就算完成了。
以下为调用API的函数声明。
Code
[DllImport("gpsapi.dll")]
static extern IntPtr GPSOpenDevice(IntPtr hNewLocationData, IntPtr hDeviceStateChange, string szDeviceName, int dwFlags);
[DllImport("gpsapi.dll")]
static extern int GPSCloseDevice(IntPtr hGPSDevice);
[DllImport("gpsapi.dll")]
static extern int GPSGetPosition(IntPtr hGPSDevice, IntPtr pGPSPosition, int dwMaximumAge, int dwFlags);
[DllImport("gpsapi.dll")]
static extern int GPSGetDeviceState(IntPtr pGPSDevice);
[DllImport("coredll.dll")]
static extern IntPtr CreateEvent(IntPtr lpEventAttributes, int bManualReset, int bInitialState, StringBuilder lpName);
[DllImport("coredll.dll")]
static extern int CloseHandle(IntPtr hObject);
const int waitFailed = -1;
[DllImport("coredll.dll")]
static extern int WaitForMultipleObjects(int nCount, IntPtr lpHandles, int fWaitAll, int dwMilliseconds);
const int eventSet = 3;
[DllImport("coredll.dll")]
static extern int EventModify(IntPtr hHandle, int dwFunc);
有时候,我们往往不需要简单的GPS的经纬度,我们或许需要更多的数据。这时候就需要转换。
比如,在我这个应用中,需要取得对应的高斯直角坐标。
当然我们需要通过一系列的数学运算得到。
我们需要设置一个重要子午线的坐标,然后根据该坐标计算得到。
if
(enableBLToXY)
{
try
{
//
a2 输入中央子午线,以度.分形式输入,如115度30分则输入115.30; 起算数据l0
//
f2 以度小数形式输入经度值, l
//
e2 以度小数形式输入纬度值,b
//
s2 计算结果,横坐标y
//
t2 计算结果,纵坐标x
//
投影带号计算 n=[l/6]+1 如:测得经度103.xxxx,故n=[103.x/6]+1=17+1=18
//
中央经线经度 l0 = n*6-3 = [l/6]*6+3
double
a2, f2, e2, s2, t2;
a2
=
centerLine;
f2
=
bl1;
e2
=
bl2;
t2
=
x;
s2
=
y;
double
b2, h2, i2, j2, k2, l2, m2, n2, o2, p2, q2, r2;
b2
=
(
int
)(a2)
+
((
int
)(a2
*
100
)
-
(
int
)(a2)
*
100
)
/
60
+
(a2
*
10000
-
(
int
)(a2
*
100
)
*
100
)
/
3600
;
//
把l0化成度(a2)
//
g2 = f2 - b2 ' l -l0
//
h2 = g2 / 57.2957795130823 '化作弧度
//
将经差的单位化为弧度
h2
=
(f2
-
b2)
/
57.2957795130823
;
i2
=
Math.Tan(e2
/
57.2957795130823
);
j2
=
Math.Cos(e2
/
57.2957795130823
);
k2
=
0.006738525415
*
j2
*
j2;
l2
=
i2
*
i2;
m2
=
1
+
k2;
n2
=
6399698.9018
/
Math.Sqrt(m2);
o2
=
h2
*
h2
*
j2
*
j2;
p2
=
i2
*
j2;
q2
=
p2
*
p2;
r2
=
(
32005.78006
+
q2
*
(
133.92133
+
q2
*
0.7031
));
s2
=
((((l2
-
18
)
*
l2
-
(
58
*
l2
-
14
)
*
k2
+
5
)
*
o2
/
20
+
m2
-
l2)
*
o2
/
6
+
1
)
*
n2
*
(h2
*
j2);
//
在计算的基础上加上了“带号”(18)和“东移”(500km)
s2
=
s2
+
18500000
;
//
计算结果,横坐标y
t2
=
6367558.49686
*
e2
/
57.29577951308
-
p2
*
j2
*
r2
+
((((l2
-
58
)
*
l2
+
61
)
*
o2
/
30
+
(
4
*
k2
+
5
)
*
m2
-
l2)
*
o2
/
12
+
1
)
*
n2
*
i2
*
o2
/
2
;
//
计算结果,纵坐标x
x
=
s2;
y
=
t2;
}
catch
(Exception ex)
{
MessageBox.Show(ex.Message);
x
=
0
;
y
=
0
;
}
}
以上就是我的GPS应用程序的主体代码介绍。希望对大家有用。
可能根据每个项目的实际状况不一致,我的个人意见如下:
1。项目中GPS部分取得的数据简单的话,可以直接采用GPS Intermediate Driver
2。最好自己封装下直接使用串口连接GPS接收器的功能。
3。地图定位,可以借助Google。
4。注意释放内存,因为多数都需要调用非托管
5。一定要用线程处理。
最后,附上应用程序CAB包。很多人写打包程序觉得有时候有困难,其实,一个简单的打包CAB程序相对很简单。现在很多WM设备,基本稳妥地还是用基于.net cf2.0为主。所以大家工程发布最好还是以.net cf2.0吧,因为有些设备安装.net cf3.5不稳定导致。
程序下载:GPSSystem.rar
运行环境:VS2008 + WM6.0 + .net cf2.0
Author:AppleSeeker(冯峰)
Date:2009-05-30
文章导读:移动开发索引贴