//////////////////////////////////////////////////////////////// // // Copyright (c) 2009-2010 MetaGeek, LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////// using System; using System.Collections.Generic; using System.Text; using System.Threading; using Inssider.Properties; using System.IO; namespace Inssider { public class Gps { public event EventHandler GpsUpdated; public event EventHandler GpsTimeout; #region Private Data //GPS Objects private NmeaParser _nmea = new NmeaParser(); private double _latitude; private double _longitude; private double _speedKPH; private DateTime _dateTime; private DateTime _satelliteTime; private double _course; private bool _hasGpsFix = false; private double _magVar = double.NaN; private List _satellites = new List(); private int _satVis; private int[] _satIDs; private double _PDOP; private double _HDOP; private double _VDOP; private int _satellitesUsed; private double _altitude; //private bool gpsConnected = false; //GPS variables private System.IO.Ports.SerialPort _gpsSerialPort; private bool _enable = false; private bool _hasTalked = false; private Thread _gpsThread; private WaitHandle[] _waitHandles; AutoResetEvent _terminate = new AutoResetEvent(false); //For GPS timeout private System.Timers.Timer GPSTimeOut = new System.Timers.Timer(2500); #endregion #region Constructors public Gps() { _waitHandles = new WaitHandle[] { _terminate, }; } #endregion #region Public Properties public bool Connected { get { return (_gpsSerialPort != null && _gpsSerialPort.IsOpen); } } public bool Enabled { get { return (_enable); } set { _enable = value; if (!Enabled) { this.TerminateThread(); } } } // Methods for setting GPS variables. They are in methods so the routine can be extended // in the future beyond simple setting of the value. Such as updating controls on the forms or logging etc. public double Latitude { get { return _latitude; } set { _latitude = value; } } public double Longitude { get { return _longitude; } set { _longitude = value; } } public double Altitude { get { return _altitude; } set { _altitude = value; } } public double Speed { get { return _speedKPH; } set { _speedKPH = value; } } public DateTime Time { get { return _dateTime; } set { _dateTime = value; } } public DateTime SatelliteTime { get { return _satelliteTime; } set { _satelliteTime = value; } } public double Course { get { return _course; } set { _course = value; } } public bool HasTalked { get { return _hasTalked; } } public bool HasFix { get { return _hasGpsFix; } //set { _hasGpsFix = value; } } public double MagVar { get { return _magVar; } set { _magVar = value; } } public int SatellitesVisible { get { return _satVis; } set { _satVis = value; } } public double PDOP { get { return _PDOP; } set { _PDOP = value; } } public double VDOP { get { return _VDOP; } set { _VDOP = value; } } public double HDOP { get { return _HDOP; } set { _HDOP = value; } } public int SatellitesUsed { get { return _satellitesUsed; } set { _satellitesUsed = value; } } public double GeoidSeperation { get { return _nmea.GeoIdSeperation; } } public string FixString { get { return _nmea.FixString; } } public double DGPSAge { get { return _nmea.DGPSAge; } } public double DGPSID { get { return _nmea.DGPSID; } } public string PortName { get { string name = string.Empty; if (null != _gpsSerialPort) { name = _gpsSerialPort.PortName; } return name; } } #endregion #region Public Methods public void ClosePort() { if (_gpsSerialPort != null && _gpsSerialPort.IsOpen) { try { _gpsSerialPort.Close(); } catch (IOException) { } catch (ObjectDisposedException) { } _hasTalked = false; OnGpsUpdated(); } } /// /// Initalizes the GPS Serial Port /// /// status public bool ResetPort() { bool status = false; if (_enable) { try { if (_gpsSerialPort != null) { try { _gpsSerialPort.Close(); } catch (IOException) { } catch (ObjectDisposedException) { } _gpsSerialPort = null; } _gpsSerialPort = new System.IO.Ports.SerialPort(Settings.Default.GPSPort, Settings.Default.GPSPortBaud, Settings.Default.GPSPortParity, Settings.Default.GPSPortDataBits, Settings.Default.GPSPortStopBits); _gpsSerialPort.Handshake = Settings.Default.GPSFlowControl; _gpsSerialPort.ReadTimeout = 500; _gpsSerialPort.Open(); _hasTalked = false; status = true; } catch (Exception) { status = false; _gpsSerialPort = null; } } return status; } /// /// Initalizes the GPS Serial Port /// /// status public bool InitializeThread() { bool status = false; if (_enable) { if (ResetPort()) { if (_gpsThread != null) { _terminate.Set(); if (!_gpsThread.Join(1000)) { _gpsThread.Abort(); } } _gpsThread = new Thread(gpsMethod); _gpsThread.Start(); status = true; } else { Settings.Default.gpsEnabled = false; _enable = false; } } return status; } public void TerminateThread() { ClosePort(); if (_gpsThread != null) { _terminate.Set(); if (!_gpsThread.Join(1000)) { _gpsThread.Abort(); } } } #endregion #region Private Methods /// /// GPS Thread, reads data from serial port and updates the GPS information /// For more info on GPS information see this page about NMEA: /// http://www.gpsinformation.org/dale/nmea.htm /// private void gpsMethod() { int gpsTimeoutCounter = 0; _terminate.Reset(); while (true) { try { int eventIdx = WaitHandle.WaitAny(_waitHandles, 100, false); if (eventIdx == WaitHandle.WaitTimeout) { if (_gpsSerialPort != null && _gpsSerialPort.IsOpen) { string sentence = string.Empty; try { sentence = _gpsSerialPort.ReadLine(); gpsTimeoutCounter = 0; _hasTalked = true; } catch (ArgumentOutOfRangeException) { // somehow we got out of sync w/ the serial port stream, try to recover int counter = 0; // line feed is last character of an NMEA sentence try { while (_gpsSerialPort.ReadChar() != '\n') { counter++; if (counter > 82) { // unable to recover... close port ClosePort(); } } } catch (ArgumentException) { // unable to recover... close port ClosePort(); } } catch (ArithmeticException) { // TODO: wtf?? } catch (TimeoutException) { // if the ReadLine() call has timed out 4 times in a row (no data for two seconds), give up if (!_hasTalked && 4 <= gpsTimeoutCounter) { ClosePort(); break; } gpsTimeoutCounter++; } catch (IOException) { ClosePort(); break; } Inssider.NmeaParser.SentenceType type = _nmea.Parse(sentence); //gpsConnected = true; switch (type) { case NmeaParser.SentenceType.GPRMC: Latitude = _nmea.Latitude; Longitude = _nmea.Longitude; Speed = _nmea.Speed; Time = _nmea.Timestamp; SatelliteTime = _nmea.SatelliteTime; Course = _nmea.Course; _hasGpsFix = _nmea.HasFix; MagVar = _nmea.MagVar; break; case NmeaParser.SentenceType.GPGSV: _satellites = _nmea.Satellites; SatellitesVisible = _nmea.SatelliteCount; break; case NmeaParser.SentenceType.GPGSA: _satIDs = _nmea.getSatIDs(); PDOP = _nmea.PDOP; HDOP = _nmea.HDOP; VDOP = _nmea.VDOP; SatellitesUsed = _nmea.SatellitesUsed; break; case NmeaParser.SentenceType.GPGGA: Latitude = _nmea.Latitude; Longitude = _nmea.Longitude; Time = _nmea.Timestamp; Altitude = _nmea.Altitude; break; case NmeaParser.SentenceType.GPVTG: Speed = _nmea.Speed; break; default: //gpsConnected = false; break; } } else { // The port was previously closed due to being out of sync, reopen Thread.Sleep(500); ResetPort(); } OnGpsUpdated(); } else // The terminate event was signaled { break; } } catch (ObjectDisposedException) { break; } } } private void OnGpsUpdated() { if (null != GpsUpdated) { GpsUpdated(this, EventArgs.Empty); } } private void OnGpsTimeout() { if (null != GpsTimeout) { GpsTimeout(this, EventArgs.Empty); } } #endregion } }