Windows Mobile下GPS管理软件NavsGo之GPS监控功能的开发

    技术2024-08-03  68

    Windows Mobile下GPS管理软件NavsGo之GPS监控功能的开发

    前言

    这个软件,前前后后断断续续做了两个星期,现在做成一个基本的版本,把当前功能记录下来,作为进一步开发的基础。我取名为NavsGo等等原因主要是Nav表示Navigator的简称,是导航器的意思。Go是出发的意思。NavsGo,有点像Let's Go的谐音,一起走的意思, 一方面表示这个软件作为GPS管理软件,可以协助多个导航软件一起运作,另一方面也希望该软件和使用者“一起走”。

    背景

    当前几乎所有Windows Mobile机型都自带GPS接收器,GPS渐渐成为我们生活中必不可少的一部分,可是在Windows Mobile直接下使用GPS导航不是一件容易的事情,需要配置硬件端口,软件端口,配置GPS Intermediate Driver, 配置AGPS,有时候甚至要配置GPRS上网(某些导航软件需要连接网络,例如Google Maps),更有些软件需要一个分离端口(splitted port)。因此做一个GPS管理软件,统一管理各项配置功能,调用现有的各种GPS导航软件实现导航。

    关于

    本文讲述一个GPS管理软件的开发,当前版本软件功能包括调用Google Maps,Tomtom,Garmin,iGO8,Route66等导航软件,调用GPS端口配置,AGP,split port等配置功能,基于GPS.net 3.0开发GPS诊断功能和GPS监控功能。界面上实现类iPhone菜单界面。在这篇文章中主要讲述GPS监控功能的实现。

    功能

    先展现一下系统的功能截图,有个整体的感性认识。

    图1 这是系统的整体导航菜单,系统会自动检测已经安装的导航软件,如上图检测到Google Maps,Tomtom,Garmin,iGO8,Route66等导航软件。

    图2 Google Maps,我心目中的NO.1,哪怕在室内也可以根据基站信息定位,是其他离线软件没有实现的。同时支持街景(Stree View)功能,交通状况功能,大大方便寻找,还有定位你自己的功能,把用户当前的位置信息通知朋友,甚至可以直接显示到facebook上 。反正功能多到超出想象,在无线网络日益完善的未来,Google Maps这类在线导航软件必定成为霸主,MS live map也属于这一类软件,大家拭目以待。缺点是需要上网费用,有些地图位置定位不够准确。

    图3 Tomtom,平常一直在用,导航功能很不错,地图数据也比较准确,界面和易用性方面需要提高。

    图4 iGO8,界面和易用性一流,可是导航地图信息有时候有点问题,导致我老是U-turn。    

    图5 Garmin,老牌GPS厂商,本人做的工业GPS产品一直使用Garmin的硬件,Garmin做产品有保障。关于Garmin的导航软件,我感觉易用性没有iGO8好,导航又比不上Tomtom。

    图6 Route66,用的不多,不好评价。

    图7 Port Splitter,一个免费软件,有些导航软件需要开了Port Splitter才能找到GPS receiver(GPS 硬件接收器)。以后版本打算把这个功能加入到NavsGo中,使用virtual com port的开发。

    图8 端口管理和GPS Intermediate Driver管理,以后版本打算把把这个功能加入到NavsGo中。

    图9 AGPS管理,以后版本打算把把这个功能加入到NavsGo中。

    图10 GPS监控功能,后面讲述如何实现。

    图11 GPS诊断功能,后面讲述如何实现。

    以上各个导航软件的评价只是个人使用感觉,不代表各个导航软件的真实情况。

    实现

    GPS诊断功能和GPS监控功能是基于GPS.net 3.0实现的,关于GPS.net 3.0可以参考 GPS.NET 和 GeoFramework开源了。我觉得GPS.net是目前最好的基于.NET开发的GPS开源软件,所以我使用GPS.net作为NavsGo的基础。GPS.net 3.0比GPS.net 2.0做了很大改动,比如说把类结构进行大规模的调整,把NMEA分析功能实行简化,使用了泛型(Generic)封装异常处理等等。也导致了部分功能在 2.0可用,到了3.0却不可以用,例如卫星控件。可能是我使用不当,也可能是源代码的问题。下面讲述如何使用GPS.net 3.0做一个GPS监控功能。

    GPS设备检测功能

    /* GPS.NET provides the ability to quickly discover GPS devices on the * local system. This feature is known as "Automatic Device Discovery" * and greatly simplifies the task of finding a GPS device to communicate * with. Developers can simply call the "Start" method of an interpreter, * and GPS.NET will automatically locate the best GPS Device to work with. * * During Device Discovery, several events are raised to indicate the * progress of detection. This example hooks into almost all of the events * to give users feedback. */ GeoFramework.Gps.IO.Devices.DeviceDetectionStarted += new EventHandler(Devices_DeviceDetectionStarted); GeoFramework.Gps.IO.Devices.DeviceDetected += new EventHandler<GeoFramework.Gps.IO.DeviceEventArgs>(Devices_DeviceDetected); GeoFramework.Gps.IO.Devices.DeviceDetectionCompleted += new EventHandler(Devices_DeviceDetectionCompleted); GeoFramework.Gps.IO.Devices.DeviceDetectionAttempted += new EventHandler<GeoFramework.Gps.IO.DeviceEventArgs>(Devices_DeviceDetectionAttempted); GeoFramework.Gps.IO.Devices.DeviceDetectionAttemptFailed += new EventHandler<GeoFramework.Gps.IO.DeviceDetectionExceptionEventArgs>(Devices_DeviceDetectionAttemptFailed);

    GPS.net可以检测当前设备上的所有端口,这些端口包括GPS Intermediate Driver端口,普通串口,蓝牙串口等等,这个过程是使用多线程在后台运行的,为了处理检测结果,需要注册相关的事件,上面为注册过程,下面是处理函数。关于GPS Intermediate Driver可以参考 30 Days of .NET [Windows Mobile Applications] - Day 03: GPS Compass(GPS指南针)

    #region GPS Device Detection Events private void SearchButton_Click(object sender, EventArgs e) { // Start the GPS device detection process. This operation will complete // in the background, on a separate thread. This method can be called again // to attempt to locate new devices. GeoFramework.Gps.IO.Devices.BeginDetection(); } private void Devices_DeviceDetectionStarted(object sender, EventArgs e) { BeginInvoke(new MethodInvoker(delegate() { StatusBar1.Text = "Detecting GPS devices..."; SearchButton.Enabled = false; })); } private void Devices_DeviceFailure(object sender, GeoFramework.Gps.IO.DeviceEventArgs e) { BeginInvoke(new MethodInvoker(delegate() { StatusBar1.Text = "Not Responding: " + e.Device.ToString(); })); } private void Devices_DeviceDetectionAttemptFailed(object sender, GeoFramework.Gps.IO.DeviceDetectionExceptionEventArgs e) { BeginInvoke(new MethodInvoker(delegate() { StatusBar1.Text = "Error: " + e.Exception.ToString(); })); } private void Devices_DeviceDetectionCompleted(object sender, EventArgs e) { BeginInvoke(new MethodInvoker(delegate() { StatusBar1.Text = "Device detection completed."; SearchButton.Enabled = true; StartButton.Enabled = true; })); } private void Devices_DeviceDetectionAttempted(object sender, GeoFramework.Gps.IO.DeviceEventArgs e) { BeginInvoke(new MethodInvoker(delegate() { StatusBar1.Text = "Detecting " + e.Device.ToString(); })); } private void Devices_DeviceDetected(object sender, GeoFramework.Gps.IO.DeviceEventArgs e) { BeginInvoke(new MethodInvoker(delegate() { StatusBar1.Text = "Found Device: " + e.Device.ToString(); // Add it to the list box ListViewItem DeviceItem = new ListViewItem(e.Device.ToString()); // Associate the detected device with this list view item, so we can connect // to the device later on DeviceItem.Tag = e.Device; // Set the image of the new item DeviceItem.ImageIndex = 0; DevicesListView.Items.Add(DeviceItem); })); } #endregion

    当Devices_DeviceDetected被回调的时候表示有设备被检查出来,并把之加入列表框供用户选择。 由于使用多线程的关系,所有的界面更新需要使用Delegate,该deleagate定义如下:

    /* The GPS.NET device detection process is multithreaded. As a result, we must * use the Invoke and BeginInvoke methods to ensure that any updates to the form * occur on the form's own thread. This delegate is used by such methods. */ public delegate void MethodInvoker();

    监视GPS端口运行情况

    检测当前设备上所有可用GPS端口后,可以绑定监视某个端口,使用NMEA解释器分析该端口的输出。下面是启动和停止监视某个端口的代码。

    private void StartButton_Click(object sender, EventArgs e) { if (DevicesListView.SelectedIndices.Count == 0) { MessageBox.Show("Please Select the device to start."); return; } // Find out which item is selected in the list view ListViewItem SelectedItem = DevicesListView.Items[DevicesListView.SelectedIndices[0]]; // The "tag" property holds the device that was detected Device selectedDevice = (Device)SelectedItem.Tag; try { // Finally, start the interpreter using the newly-assigned Stream Interpreter.Start(selectedDevice); selectedDevice.Connecting += new EventHandler(Device_Connecting); selectedDevice.Connected += new EventHandler(Device_Connected); selectedDevice.Disconnecting += new EventHandler(Device_Disconnecting); selectedDevice.Disconnected += new EventHandler(Device_Disconnected); StartButton.Enabled = false; // Enable the disconnect button StopButton.Enabled = true; } catch (Exception ex) { MessageBox.Show(ex.ToString(), "Unable to Connect", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); } } private void StopButton_Click(object sender, EventArgs e) { // Stop any GPS communications Interpreter.Stop(); StartButton.Enabled = true; // And disable the disconnect button StopButton.Enabled = false; StatusBar1.Text = "Disconnected from GPS device."; }

    通过Interpreter.Start(selectedDevice);把选择的GPS设备端口绑定到NMEA解释器上,这样解释器就会分析该选定端口输出的数据了。同时注册该GPS设备建立链接和端口链接的事件。 当NMEA解释器对指定端口的数据进行分析的时候,可以订阅相关的事件对关心的数据进行处理,下面为NMEA解释器事件的订阅代码。

    Interpreter.SpeedChanged += new System.EventHandler<GeoFramework.SpeedEventArgs>(Interpreter_SpeedChanged); Interpreter.FixAcquired += new System.EventHandler(Interpreter_FixAcquired); Interpreter.FixLost += new System.EventHandler(Interpreter_FixLost); Interpreter.Resumed += new EventHandler(Interpreter_Resumed); Interpreter.FixModeChanged += new System.EventHandler<GeoFramework.Gps.FixModeEventArgs>(Interpreter_FixModeChanged); Interpreter.FixQualityChanged += new System.EventHandler<GeoFramework.Gps.FixQualityEventArgs>(Interpreter_FixQualityChanged); Interpreter.HorizontalDilutionOfPrecisionChanged += new System.EventHandler<GeoFramework.Gps.DilutionOfPrecisionEventArgs>(Interpreter_HorizontalDilutionOfPrecisionChanged); Interpreter.SentenceReceived += new System.EventHandler<GeoFramework.Gps.Nmea.NmeaSentenceEventArgs>(Interpreter_SentenceReceived); Interpreter.AltitudeChanged += new System.EventHandler<GeoFramework.DistanceEventArgs>(Interpreter_AltitudeChanged); Interpreter.PositionChanged += new System.EventHandler<GeoFramework.PositionEventArgs>(Interpreter_PositionChanged); Interpreter.UtcDateTimeChanged += new System.EventHandler<GeoFramework.DateTimeEventArgs>(Interpreter_UtcDateTimeChanged); Interpreter.Paused += new EventHandler(Interpreter_Paused); Interpreter.BearingChanged += new System.EventHandler<GeoFramework.AzimuthEventArgs>(Interpreter_BearingChanged); Interpreter.VerticalDilutionOfPrecisionChanged += new System.EventHandler<GeoFramework.Gps.DilutionOfPrecisionEventArgs>(Interpreter_VerticalDilutionOfPrecisionChanged); Interpreter.SatellitesChanged += new EventHandler<SatelliteListEventArgs>(Interpreter_SatellitesChanged);

                 下面为部分事件处理代码.

    void Interpreter_SentenceReceived(object sender, GeoFramework.Gps.Nmea.NmeaSentenceEventArgs e) { BeginInvoke(new MethodInvoker(delegate() { // Keep the list box from getting too big if (RawDataListBox.Items.Count > 50) RawDataListBox.Items.RemoveAt(0); // Add the sentence to the raw data list box RawDataListBox.Items.Add(e.Sentence.ToString()); RawDataListBox.SelectedIndex = RawDataListBox.Items.Count - 1; })); }

    当NMEA解释器从指定端口中接收到Raw NMEA data时,会回调Interpreter_SentenceReceived,系统会把这些Raw NMEA data打印到列表框中。

    时间变化

    void Interpreter_UtcDateTimeChanged(object sender, GeoFramework.DateTimeEventArgs e){ BeginInvoke(new MethodInvoker(delegate() { // Update the current satellite-derived time SatelliteTime.Text = e.DateTime.ToLocalTime().ToString(); }));}

    时间发生变化时进行回调,一般来说,设备会在每一秒或者每两秒钟输出NMEA data,重要的NMEA句子都会包含GPS时间的字段,关于NMEA可以参考.NET Compact Framework下的GPS NMEA data数据分析。这个输入时间的间隔和硬件有关,是可以设置的。事实上,GPS时间和UTC时间是有时间差的,目前(2009年)GPS时间比UTC时间快15秒,但是做应用开发一般不需要处理这个时间差,GPS硬件厂商的固件(Firmware)在输出NMEA data的时候已经进行调整,可以认为NMEA data的时间是UTC时间。

    经度和纬度变化

    void Interpreter_PositionChanged(object sender, GeoFramework.PositionEventArgs e){ BeginInvoke(new MethodInvoker(delegate() { // Update the latitude and longitude Latitude.Text = e.Position.Latitude.ToString(); Longitude.Text = e.Position.Longitude.ToString(); StatusBar1.Text = "Position has changed."; }));}

    经度和纬度信息发生变化时调用。

         

    海拔变化

    void Interpreter_AltitudeChanged(object sender, GeoFramework.DistanceEventArgs e){ BeginInvoke(new MethodInvoker(delegate() { altimeter1.Value = e.Distance; // Update the current altitude Altitude.Text = e.Distance.ToLocalUnitType().ToString(); StatusBar1.Text = "Altitude has changed."; }));}

    海拔发生变化时调用,同时更新海拔计控件的信息。

    方位变化

    void Interpreter_BearingChanged(object sender, GeoFramework.AzimuthEventArgs e){ BeginInvoke(new MethodInvoker(delegate() { compass1.Value = e.Azimuth; // Output the current bearing as degrees (i.e. 012°) Bearing.Text = e.Azimuth.ToString(); // Output the current bearing as a compass direction (i.e. Southwest) Direction.Text = e.Azimuth.ToString("c"); StatusBar1.Text = "Bearing has changed."; }));}

    方位发生变化时调用,同时更新指南针控件的信息。

    速度变化

    void Interpreter_SpeedChanged(object sender, GeoFramework.SpeedEventArgs e){ BeginInvoke(new MethodInvoker(delegate() { speedometer1.Value = e.Speed; // Update the current speed Speed.Text = e.Speed.ToLocalUnitType().ToString(); StatusBar1.Text = "Speed has changed."; }));}

    速度发生变化时调用,同时更新速度表控件的信息。

    控件的使用

    GPS.net 3.0提供了速度计,海拔计,指南针,卫星方位图和卫星信号图等控件。在上面的图可见,我使用了 速度计,海拔计,指南针等控件,可是我在使用卫星方位图和卫星信号图时发现些问题,我正在联系原作者帮助,可能是我使用的不对,也可能是GPS.net 3.0的bug,我会在下一个版本解决。

    使用GPS.net 3.0的控件十分简单,只要把控件拖拉到winform里面,同时修改控件的IsPaintingOnSeparateThread属性为false。

    compass1.IsPaintingOnSeparateThread = false;altimeter1.IsPaintingOnSeparateThread = false;speedometer1.IsPaintingOnSeparateThread = false;//satelliteViewer1.IsPaintingOnSeparateThread = false;//satelliteSignalBar1.IsPaintingOnSeparateThread = false;

    在目前版本,如果不把IsPaintingOnSeparateThread修改为false会抛出一个多线程的异常信息。

    at Microsoft.AGL.Common.MISC.HandleAr(PAL_ERROR ar)at System.Windows.Forms.Control.get_Visible()at GeoFramework.Drawing.DoubleBufferedControl.Repaint()at GeoFramework.Drawing.DoubleBufferedControl.PaintingThreadProc()

    这个问题我正在处理。  

    To-Do-List

    当前只是第一个版本,有些功能需要继续不断完善,待完善功能如下: 1.实现端口管理功能。 2.实现AGPS功能。 3.实现检查飞行模式功能,因为飞行模式下GPS不能使用。 4.实现管理GPRS,3G网络链接功能。 5.在GPS监控模块增加对卫星的监控。 6.实现保存导航信息到Google maps及其他地图格式的功能。 7.优化UI处理。   ......

    关于项目的发展

    我把项目host到codeplex了,打算不断改进。项目主页链接如下:

    NavsGo - GPS management software 下载当前版本的源代码链接如下

     

    http://navsgo.codeplex.com/SourceControl/ListDownloadableCommits.aspx#DownloadLatest

     

    检查和下载最新版本链接如下

    http://navsgo.codeplex.com/SourceControl/ListDownloadableCommits.aspx

    安装包  NavsGo.zip 由于博客园不能直接上传cab文件,请解压成 NavsGo.cab,然后拷贝到Windows Mobile设备上安装,谢谢。

    下篇文章会讲一下GPS诊断功能的开发,然后讲类iPhone界面的开发。

    作者: Jake Lin( Jake's Blog on 博客园) 出处: http://procoder.cnblogs.com 本 作品由 Jake Lin创作,采用 知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。 任何转载必须保留完整文章,在显要地方显示署名以及原文链接。如您有任何疑问或者授权方面的协商,请 给我留言。
    最新回复(0)