// // Copyright (c) Microsoft Corporation. All rights reserved. // // // Use of this sample source code is subject to the terms of the Microsoft // license agreement under which you licensed this sample source code. If // you did not accept the terms of the license agreement, you are not // authorized to use this sample source code. For the terms of the license, // please see the license agreement between you and Microsoft or, if applicable, // see the LICENSE.RTF on your install media or the root of your tools installation. // THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES. // using System; using System.Runtime.InteropServices; using System.Text; #if PocketPC using OpenNETCF.ComponentModel; using OpenNETCF.Threading; using Thread=OpenNETCF.Threading.Thread2; using System.Diagnostics; #endif namespace GMap.NET.GPS { public delegate void LocationChangedEventHandler(object sender, GpsPosition args); public delegate void DeviceStateChangedEventHandler(object sender, GpsDeviceState args); /// /// Summary description for GPS. /// public class Gps { // handle to the gps device IntPtr gpsHandle = IntPtr.Zero; // handle to the native event that is signalled when the GPS // devices gets a new location IntPtr newLocationHandle = IntPtr.Zero; // handle to the native event that is signalled when the GPS // device state changes IntPtr deviceStateChangedHandle = IntPtr.Zero; // handle to the native event that we use to stop our event // thread IntPtr stopHandle = IntPtr.Zero; // holds our event thread instance Thread gpsEventThread = null; event LocationChangedEventHandler locationChanged; /// /// Event that is raised when the GPS locaction data changes /// public event LocationChangedEventHandler LocationChanged { add { locationChanged += value; // create our event thread only if the user decides to listen CreateGpsEventThread(); } remove { locationChanged -= value; } } event DeviceStateChangedEventHandler deviceStateChanged; /// /// Event that is raised when the GPS device state changes /// public event DeviceStateChangedEventHandler DeviceStateChanged { add { deviceStateChanged += value; // create our event thread only if the user decides to listen CreateGpsEventThread(); } remove { deviceStateChanged -= value; } } /// /// True: The GPS device has been opened. False: It has not been opened /// public bool Opened { get { return gpsHandle != IntPtr.Zero; } } ~Gps() { // make sure that the GPS was closed. Close(); } /// /// Opens the GPS device and prepares to receive data from it. /// 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(); } } } /// /// Closes the gps device. /// public void Close() { if(gpsHandle != IntPtr.Zero) { GPSCloseDevice(gpsHandle); gpsHandle = IntPtr.Zero; } // Set our native stop event so we can exit our event thread. if(stopHandle != IntPtr.Zero) { EventModify(stopHandle, eventSet); } // wait exit if(gpsEventThread != null && gpsEventThread.IsAlive) { //gpsEventThread.Join(4444); } } /// /// Get the position reported by the GPS receiver /// /// GpsPosition class with all the position details public GpsPosition GetPosition() { return GetPosition(TimeSpan.Zero); } /// /// Get the position reported by the GPS receiver that is no older than /// the maxAge passed in /// /// Max age of the gps position data that you want back. /// If there is no data within the required age, null is returned. /// if maxAge == TimeSpan.Zero, then the age of the data is ignored /// GpsPosition class with all the position details public GpsPosition GetPosition(TimeSpan maxAge) { GpsPosition gpsPosition = null; if(Opened) { // allocate the necessary memory on the native side. We have a class (GpsPosition) that // has the same memory layout as its native counterpart IntPtr ptr = Utils.LocalAlloc(Marshal.SizeOf(typeof(GpsPosition))); // fill in the required fields gpsPosition = new GpsPosition(); gpsPosition.dwVersion = 1; gpsPosition.dwSize = Marshal.SizeOf(typeof(GpsPosition)); // Marshal our data to the native pointer we allocated. Marshal.StructureToPtr(gpsPosition, ptr, false); // call native method passing in our native buffer int result = GPSGetPosition(gpsHandle, ptr, 500000, 0); if(result == 0) { // native call succeeded, marshal native data to our managed data gpsPosition = (GpsPosition) Marshal.PtrToStructure(ptr, typeof(GpsPosition)); if(maxAge != TimeSpan.Zero) { // check to see if the data is recent enough. if(!gpsPosition.Time.HasValue || DateTime.UtcNow - maxAge > gpsPosition.Time) { gpsPosition = null; } } } // free our native memory Utils.LocalFree(ptr); } return gpsPosition; } /// /// Queries the device state. /// /// Device state information public GpsDeviceState GetDeviceState() { GpsDeviceState device = null; // allocate a buffer on the native side. Since the IntPtr pGpsDevice = Utils.LocalAlloc(GpsDeviceState.GpsDeviceStructureSize); // GPS_DEVICE structure has arrays of characters, it's easier to just // write directly into memory rather than create a managed structure with // the same layout. Marshal.WriteInt32(pGpsDevice, 1); // write out GPS version of 1 Marshal.WriteInt32(pGpsDevice, 4, GpsDeviceState.GpsDeviceStructureSize); // write out dwSize of structure int result = GPSGetDeviceState(pGpsDevice); if(result == 0) { // instantiate the GpsDeviceState class passing in the native pointer device = new GpsDeviceState(pGpsDevice); } // free our native memory Utils.LocalFree(pGpsDevice); return device; } /// /// Creates our event thread that will receive native events /// 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 Thread(new System.Threading.ThreadStart(WaitForGpsEvents)); gpsEventThread.IsBackground = false; gpsEventThread.Name = "GMap.NET GpsEvents"; gpsEventThread.Start(); } } /// /// Method used to listen for native events from the GPS. /// private void WaitForGpsEvents() { //lock (this) { bool listening = true; // allocate 3 handles worth of memory to pass to WaitForMultipleObjects IntPtr handles = Utils.LocalAlloc(12); // write the three handles we are listening for. Marshal.WriteInt32(handles, 0, stopHandle.ToInt32()); Marshal.WriteInt32(handles, 4, deviceStateChangedHandle.ToInt32()); Marshal.WriteInt32(handles, 8, newLocationHandle.ToInt32()); while(listening) { int obj = WaitForMultipleObjects(3, handles, 0, -1); if(obj != waitFailed) { switch(obj) { case 0: // we've been signalled to stop listening = false; break; case 1: // device state has changed if(deviceStateChanged != null) { deviceStateChanged(this, GetDeviceState()); } break; case 2: // location has changed if(locationChanged != null) { locationChanged(this, GetPosition()); } break; } } } // free the memory we allocated for the native handles Utils.LocalFree(handles); if(newLocationHandle != IntPtr.Zero) { CloseHandle(newLocationHandle); newLocationHandle = IntPtr.Zero; } if(deviceStateChangedHandle != IntPtr.Zero) { CloseHandle(deviceStateChangedHandle); deviceStateChangedHandle = IntPtr.Zero; } if(stopHandle != IntPtr.Zero) { CloseHandle(stopHandle); stopHandle = IntPtr.Zero; } // clear our gpsEventThread so that we can recreate this thread again // if the events are hooked up again. gpsEventThread = null; Debug.WriteLine("gps device stopped..."); } } public double GetDistance(double p1Lat, double p1Lng, double p2Lat, double p2Lng) { double dLat1InRad = p1Lat * (Math.PI / 180); double dLong1InRad = p1Lng * (Math.PI / 180); double dLat2InRad = p2Lat * (Math.PI / 180); double dLong2InRad = p2Lng * (Math.PI / 180); double dLongitude = dLong2InRad - dLong1InRad; double dLatitude = dLat2InRad - dLat1InRad; double a = Math.Pow(Math.Sin(dLatitude / 2), 2) + Math.Cos(dLat1InRad) * Math.Cos(dLat2InRad) * Math.Pow(Math.Sin(dLongitude / 2), 2); double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); double dDistance = EarthRadiusKm * c; return dDistance; } /// /// Radius of the Earth /// public double EarthRadiusKm = 6378.137; // WGS-84 #region PInvokes to gpsapi.dll [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); #endregion #region PInvokes to coredll.dll [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); #endregion } }