using System;
using System.Threading;

namespace Hydra.Framework.Mapping.Gps
{
    //
    // **********************************************************************
    /// <summary>
    /// GPS Handler - GPS Library for Pocket PC
    /// Released under GNU Lesser General Public License
    /// </summary>
    // **********************************************************************
    //
    public class GPSHandler
        : IDisposable
    {
        #region Private Member Variables
        //
        // **********************************************************************
        /// <summary>
        /// GPS Serial Port
        /// </summary>
        // **********************************************************************
        //
        private static SerialPort m_GpsPort = new SerialPort();

        //
        // **********************************************************************
        /// <summary>
        /// Thread Start Methods
        /// </summary>
        // **********************************************************************
        //
        private ThreadStart m_ThreadStart;

        //
        // **********************************************************************
        /// <summary>
        /// Thread
        /// </summary>
        // **********************************************************************
        //
        private Thread m_Thread;

        //
        // **********************************************************************
        /// <summary>
        /// Parent Form
        /// </summary>
        // **********************************************************************
        //
        private System.Windows.Forms.Control m_Parent;

        //
        // **********************************************************************
        /// <summary>
        /// Is this Object Disposed
        /// </summary>
        // **********************************************************************
        //
        private bool m_IsDisposed = false;

        //
        // **********************************************************************
        /// <summary>
        /// enUS Number Format
        /// </summary>
        // **********************************************************************
        //
        public static System.Globalization.NumberFormatInfo m_NumberFormat_EnUS = new System.Globalization.CultureInfo("en-US", false).NumberFormat;

        //
        // **********************************************************************
        /// <summary>
        /// Emulate?
        /// </summary>
        // **********************************************************************
        //
        private bool m_Emulate;

        //
        // **********************************************************************
        /// <summary>
        /// Input File for NMEA
        /// </summary>
        // **********************************************************************
        //
        private static string m_NMEAInputFile;

        //
        // **********************************************************************
        /// <summary>
        /// Recommended minimum specific GPS/Transit data
        /// </summary>
        // **********************************************************************
        //
        private NMEA.GPRMC m_GPRMC;

        //
        // **********************************************************************
        /// <summary>
        /// Global Positioning System Fix Data
        /// </summary>
        // **********************************************************************
        //
        private NMEA.GPGGA m_GPGGA;

        //
        // **********************************************************************
        /// <summary>
        /// Satellites in view
        /// </summary>
        // **********************************************************************
        //
        private NMEA.GPGSV m_GPGSV;

        //
        // **********************************************************************
        /// <summary>
        /// GPS DOP and active satellites
        /// </summary>
        // **********************************************************************
        //
        private NMEA.GPGSA m_GPGSA;

        //
        // **********************************************************************
        /// <summary>
        /// Geographic position, Latitude and Longitude
        /// </summary>
        // **********************************************************************
        //
        private NMEA.GPGLL m_GPGLL;

        //
        // **********************************************************************
        /// <summary>
        /// Estimated Position Error - Garmin proprietary sentence(!)
        /// </summary>
        // **********************************************************************
        //
        public NMEA.GPRME m_PGRME;
        #endregion

        #region Delegates & Events
        //
        // **********************************************************************
        /// <summary>
        /// A delegate type for hooking up change notifications.
        /// </summary>
        // **********************************************************************
        //
        public delegate void NewGPSFixHandler(object sender, GPSEventArgs e);

        //
        // **********************************************************************
        /// <summary>
        /// Overridden. Fires when the GpsHandler has received data from the GPS device.
        /// </summary>
        // **********************************************************************
        //
        public event NewGPSFixHandler NewGPSFix;
        #endregion

        #region Constructors
        //
        // **********************************************************************
        /// <summary>
        /// Initializes a GpsHandler for communication with GPS receiver.
        /// The GpsHandler is used for communication with the GPS device and process information from the GPS revice.
        /// </summary>
        /// <param name="Parent">Parent form which callbacks should be directed to.</param>
        /// <example>
        /// The following example demonstrates how to create an GpsHandler instance that reacts to GPS position events.
        /// <code>
        /// [C#]
        /// public class Form1 : System.Windows.Forms.Form
        /// {
        /// public static GPSHandler GPS;
        /// public Form1()
        /// {
        /// InitializeComponent();
        /// //Initialize GPS handler
        /// GPS = new GPSHandler(this);
        /// //Hook up GPS data events to a handler
        /// GPS.NewGPSFix += new GPSHandler.NewGPSFixHandler(this.GPSEventHandler);
        /// }
        /// public void ExitApp()
        /// {
        /// GPS.Dispose();  //Closes serial port and cleans up
        /// Application.Exit();
        /// }
        /// private void GPSEventHandler(object sender, GPSHandler.GPSEventArgs e)
        /// {
        /// switch (e.TypeOfEvent)
        /// {
        /// case GPSEventType.GPRMC:  //Recommended minimum specific GPS/Transit data
        /// if(GPS.HasGPSFix) //Is a GPS fix available?
        /// {
        /// lbPosition.Text = GPS.RMC.Longitude.ToString() + "," + GPS.RMC.Latitude.ToString();
        /// lbHeading.Text = GPS.RMC.Course.ToString();
        /// lbSpeed.Text = GPS.RMC.Speed.ToString() + " mph";
        /// lbTimeOfFix.Text = GPS.RMC.TimeOfFix + " - " + GPS.RMC.DateOfFix;
        /// }
        /// else
        /// {
        /// lbPosition.Text = "No fix";
        /// lbHeading.Text = "N/A";
        /// lbSpeed.Text = "N/A";
        /// lbTimeOfFix.Text = "N/A";
        /// }
        /// break;
        /// }
        /// }
        /// private void btnStartGPS_Click(object sender, System.EventArgs e)
        /// {
        /// if(!GPS.IsPortOpen)
        /// {
        /// try
        /// {
        /// //Open serial port 1 at 4800baud
        /// GPS.Start("COM1",4800);
        /// }
        /// catch(System.Exception ex)
        /// {
        /// MessageBox.Show("An error occured when trying to open port: " + ex.Message);
        /// }
        /// }
        /// }
        /// </code>
        /// </example>
        // **********************************************************************
        //
        public GPSHandler(System.Windows.Forms.Control Parent)
        {
            m_Parent = Parent;
            m_IsDisposed = false;
            this.NewGPSFix += new NewGPSFixHandler(this.GPSEventHandler);

            //
            // Link event from GPS receiver to process data function
            //
            m_GpsPort.NewGPSData += new SerialPort.NewGPSDataHandler(this.GPSDataEventHandler);
            this.GPRMC = new Hydra.Framework.Mapping.Gps.NMEA.GPRMC();
            this.GPGGA = new Hydra.Framework.Mapping.Gps.NMEA.GPGGA();
            this.GPGSA = new Hydra.Framework.Mapping.Gps.NMEA.GPGSA();
            this.GPRMC = new Hydra.Framework.Mapping.Gps.NMEA.GPRMC();
            this.PGRME = new Hydra.Framework.Mapping.Gps.NMEA.GPRME();
            this.GPGSV = new Hydra.Framework.Mapping.Gps.NMEA.GPGSV();
        }
        #endregion

        #region Dispose
        //
        // **********************************************************************
        /// <summary>
        /// Disposes the GpsHandler and if nessesary calls Stop()
        /// </summary>
        // **********************************************************************
        //
        public void Dispose()
        {
            if (!m_IsDisposed)
            {
                Stop();
                m_GpsPort.Dispose();
                this.GPGGA = null;
                this.GPGLL = null;
                this.GPGSA = null;
                this.GPRMC = null;
                this.PGRME = null;
                m_GpsPort = null;
                m_IsDisposed = true;
            }
            GC.SuppressFinalize(this);
        }

        //
        // **********************************************************************
        /// <summary>
        /// Finalizer
        /// </summary>
        // **********************************************************************
        //
        ~GPSHandler()
        {
            Dispose();
        }
        #endregion

        #region Properties
        //
        // **********************************************************************
        /// <summary>
        /// Gets or sets the GPS port.
        /// </summary>
        /// <value>The GPS port.</value>
        // **********************************************************************
        //
        public static SerialPort GpsPort
        {
            get
            {
                return m_GpsPort;
            }
            set
            {
                m_GpsPort = value;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Gets or sets the NMEA input file.
        /// </summary>
        /// <value>The NMEA input file.</value>
        // **********************************************************************
        //
        public static string NMEAInputFile
        {
            get
            {
                return m_NMEAInputFile;
            }
            set
            {
                m_NMEAInputFile = value;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Recommended minimum specific GPS/Transit data
        /// </summary>
        /// <value>The GPRMC.</value>
        // **********************************************************************
        //
        public NMEA.GPRMC GPRMC
        {
            get
            {
                return m_GPRMC;
            }
            set
            {
                m_GPRMC = value;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Global Positioning System Fix Data
        /// </summary>
        /// <value>The GPGGA.</value>
        // **********************************************************************
        //
        public NMEA.GPGGA GPGGA
        {
            get
            {
                return m_GPGGA;
            }
            set
            {
                m_GPGGA = value;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Satellites in view
        /// </summary>
        /// <value>The GPGSV.</value>
        // **********************************************************************
        //
        public NMEA.GPGSV GPGSV
        {
            get
            {
                return m_GPGSV;
            }
            set
            {
                m_GPGSV = value;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// GPS DOP and active satellites
        /// </summary>
        /// <value>The GPGSA.</value>
        // **********************************************************************
        //
        public NMEA.GPGSA GPGSA
        {
            get
            {
                return m_GPGSA;
            }
            set
            {
                m_GPGSA = value;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Geographic position, Latitude and Longitude
        /// </summary>
        /// <value>The GPGLL.</value>
        // **********************************************************************
        //
        public NMEA.GPGLL GPGLL
        {
            get
            {
                return m_GPGLL;
            }
            set
            {
                m_GPGLL = value;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Estimated Position Error - Garmin proprietary sentence(!)
        /// </summary>
        /// <value>The PGRME.</value>
        // **********************************************************************
        //
        public NMEA.GPRME PGRME
        {
            get
            {
                return m_PGRME;
            }
            set
            {
                m_PGRME = value;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Gets a boolean stating whether the port to the GPS device is open.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is port open; otherwise, <c>false</c>.
        /// </value>
        // **********************************************************************
        //
        public bool IsPortOpen
        {
            get
            {
                return m_GpsPort.IsPortOpen;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Get a boolean stating whether the GPS device has a fix or not.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has GPS fix; otherwise, <c>false</c>.
        /// </value>
        // **********************************************************************
        //
        public bool HasGPSFix
        {
            get
            {
                return (GPGGA.FixQuality != NMEA.FixQualityEnum.Invalid);
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Returns Garmin estimated horisontal error. This is Garmin proprietary message and may not function with all GPS devices.
        /// </summary>
        /// <value>The GPS accuracy.</value>
        // **********************************************************************
        //
        public double GPSAccuracy
        {
            get
            {
                return PGRME.EstHorisontalError;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Indicates whether NMEA input is emulated from file
        /// </summary>
        /// <value><c>true</c> if emulate; otherwise, <c>false</c>.</value>
        /// <returns>true of emulate is on</returns>
        // **********************************************************************
        //
        public bool Emulate
        {
            get
            {
                return m_Emulate;
            }
            set
            {
                if (value)
                {
                    if (!System.IO.File.Exists(m_NMEAInputFile))
                        throw (new System.Exception("Error. File not set or not found"));
                }

                m_Emulate = value;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Gets or sets the GpsHandler TimeOut (default: 5 seconds).
        /// </summary>
        /// <value>The time out.</value>
        // **********************************************************************
        //
        public int TimeOut
        {
            get
            {
                return m_GpsPort.TimeOut;
            }
            set
            {
                m_GpsPort.TimeOut = value;
            }
        }
        #endregion

        #region Private Methods
        //
        // **********************************************************************
        /// <summary>
        /// Parse event from GPS thread to parent thread
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">GPSEventArgs</param>
        // **********************************************************************
        //
        private void GPSEventHandler(object sender, GPSEventArgs e)
        {
            ControlInvoker controlInvoker;
            controlInvoker = new ControlInvoker(this.m_Parent);
            controlInvoker.Invoke(new MethodCallInvoker(ReturnEvent), e); // Send event to parent thread
        }

        //
        // **********************************************************************
        /// <summary>
        /// Returns the event.
        /// </summary>
        /// <param name="arguments">The arguments.</param>
        // **********************************************************************
        //
        private void ReturnEvent(Object[] arguments)
        {
            GPSEventArgs e = (GPSEventArgs)arguments[0];
            NewGPSFix(this, e); // Fire event back to main application
        }

        //
        // **********************************************************************
        /// <summary>
        /// Private method for Firing a serialport timeout event
        /// </summary>
        // **********************************************************************
        //
        private void FireTimeOut()
        {
            GPGGA.FixQuality = NMEA.FixQualityEnum.Invalid;
            GPSEventArgs e = new GPSEventArgs();
            e.TypeOfEvent = GPSEventType.TimeOut;
            NewGPSFix(this, e);
        }

        //
        // **********************************************************************
        /// <summary>
        /// Private method for parsing the GPGLL NMEA sentence
        /// </summary>
        /// <param name="strGLL">GPGLL sentence</param>
        // **********************************************************************
        //
        private void ParseGLL(string strGLL)
        {
            GPGLL = new NMEA.GPGLL(strGLL);
            GPSEventArgs e = new GPSEventArgs();
            e.TypeOfEvent = GPSEventType.GPGLL;
            e.Sentence = strGLL;
            NewGPSFix(this, e);
        }

        //
        // **********************************************************************
        /// <summary>
        /// Private method for parsing the GPGSV NMEA sentence
        /// GPGSV is a bit different, since it if usually made from several NMEA sentences
        /// </summary>
        /// <param name="strGSV">GPGSV sentence</param>
        // **********************************************************************
        //
        private void ParseGSV(string strGSV)
        {
            //
            // fire the event if last GSV message.
            //
            if (GPGSV.AddSentence(strGSV))
            {
                GPSEventArgs e = new GPSEventArgs();
                e.TypeOfEvent = GPSEventType.GPGSV;
                e.Sentence = strGSV;
                NewGPSFix(this, e);
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Private method for parsing the GPGSA NMEA sentence
        /// </summary>
        /// <param name="strGSA">GPGSA sentence</param>
        // **********************************************************************
        //
        private void ParseGSA(string strGSA)
        {
            GPGSA = new NMEA.GPGSA(strGSA);

            //
            // fire the event.
            //
            GPSEventArgs e = new GPSEventArgs();
            e.TypeOfEvent = GPSEventType.GPGSA;
            e.Sentence = strGSA;
            NewGPSFix(this, e);
        }

        //
        // **********************************************************************
        /// <summary>
        /// Private method for parsing the GPGGA NMEA sentence
        /// </summary>
        /// <param name="strGGA">GPGGA sentence</param>
        // **********************************************************************
        //
        private void ParseGGA(string strGGA)
        {
            GPGGA = new NMEA.GPGGA(strGGA);

            //
            // fire the event.
            //
            GPSEventArgs e = new GPSEventArgs();
            e.TypeOfEvent = GPSEventType.GPGGA;
            e.Sentence = strGGA;
            NewGPSFix(this, e);
        }

        //
        // **********************************************************************
        /// <summary>
        /// Private method for parsing the GPRMC NMEA sentence
        /// </summary>
        /// <param name="strRMC">GPRMC sentence</param>
        // **********************************************************************
        //
        private void ParseRMC(string strRMC)
        {
            this.GPRMC = new Hydra.Framework.Mapping.Gps.NMEA.GPRMC(strRMC);

            //
            // fire the event.
            //
            GPSEventArgs e = new GPSEventArgs();
            e.TypeOfEvent = GPSEventType.GPRMC;
            e.Sentence = strRMC;
            NewGPSFix(this, e);
        }

        //
        // **********************************************************************
        /// <summary>
        /// Private method for parsing the PGRME NMEA sentence
        /// </summary>
        /// <param name="strRME">GPRMC sentence</param>
        // **********************************************************************
        //
        private void ParseRME(string strRME)
        {
            this.PGRME = new Hydra.Framework.Mapping.Gps.NMEA.GPRME(strRME);

            //
            // fire the event.
            //
            GPSEventArgs e = new GPSEventArgs();
            e.TypeOfEvent = GPSEventType.PGRME;
            e.Sentence = strRME;
            NewGPSFix(this, e);
        }

        //
        // **********************************************************************
        /// <summary>
        /// Converts GPS position in d"dd.ddd' to decimal degrees ddd.ddddd
        /// </summary>
        /// <param name="DM">The DM.</param>
        /// <param name="Dir">The dir.</param>
        /// <returns></returns>
        // **********************************************************************
        //
        internal static double GPSToDecimalDegrees(string DM, string Dir)
        {
            try
            {
                if (string.IsNullOrEmpty(DM) ||
                    string.IsNullOrEmpty(Dir))
                {
                    return 0.0;
                }

                //
                // Get the fractional part of minutes
                // DM = '5512.45',  Dir='N'
                // DM = '12311.12', Dir='E'
                //
                string t = DM.Substring(DM.IndexOf("."));
                double FM = double.Parse(DM.Substring(DM.IndexOf(".")), GPSHandler.m_NumberFormat_EnUS);

                //
                // Get the minutes.
                //
                t = DM.Substring(DM.IndexOf(".") - 2, 2);
                double Min = double.Parse(DM.Substring(DM.IndexOf(".") - 2, 2), GPSHandler.m_NumberFormat_EnUS);

                //
                // Degrees
                //
                t = DM.Substring(0, DM.IndexOf(".") - 2);
                double Deg = double.Parse(DM.Substring(0, DM.IndexOf(".") - 2), GPSHandler.m_NumberFormat_EnUS);

                if (Dir == "S" ||
                    Dir == "W")
                    Deg = -(Deg + (Min + FM) / 60);
                else
                    Deg = Deg + (Min + FM) / 60;

                return Deg;
            }
            catch
            {
                return 0.0;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Internal class for parsing doubles. This replaces the Double.TryParse() method that isn't supported by CF.
        /// </summary>
        /// <param name="str">string to parse</param>
        /// <param name="result">Output result. 0 if parse failed</param>
        /// <returns>true if parse was succesfull</returns>
        /// <remarks>Uses EN-us format for parsing doubles</remarks>
        // **********************************************************************
        //
        internal static bool dblTryParse(string str, out double result)
        {
            try
            {
                result = double.Parse(str, GPSHandler.m_NumberFormat_EnUS);
                return true;
            }
            catch
            {
                result = 0;
                return false;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Ints the try parse.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <param name="result">The result.</param>
        /// <returns></returns>
        // **********************************************************************
        //
        internal static bool intTryParse(string str, out int result)
        {
            try
            {
                result = int.Parse(str, GPSHandler.m_NumberFormat_EnUS);
                return true;
            }
            catch
            {
                result = 0;
                return false;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Ints the try parse.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <returns></returns>
        // **********************************************************************
        //
        internal static int intTryParse(string str)
        {
            try
            {
                return int.Parse(str, GPSHandler.m_NumberFormat_EnUS);
            }
            catch
            {
                return 0;
            }
        }
        #endregion

        #region Public Methods
        //
        // **********************************************************************
        /// <summary>
        /// Turns on NMEA emulation
        /// </summary>
        /// <param name="FileName">File to read NMEA sentences from</param>
        // **********************************************************************
        //
        public void EnableEmulate(string FileName)
        {
            m_Emulate = true;
            if (System.IO.File.Exists(FileName))
                m_NMEAInputFile = FileName;
            else
                throw (new System.Exception("Error. File not found"));
        }

        //
        // **********************************************************************
        /// <summary>
        /// Starts the GPS thread and opens the port.
        /// </summary>
        /// <param name="serialPort">Serialport number where GPS receiver is connected (ie. "COM1").</param>
        /// <param name="BaudRate">Baudrate (usually 4800).</param>
        // **********************************************************************
        //
        public void Start(string serialPort, int BaudRate)
        {
            m_GpsPort.Port = serialPort;
            m_GpsPort.BaudRate = BaudRate;

            if (!m_Emulate)
                this.m_ThreadStart = new ThreadStart(m_GpsPort.Start);
            else
                this.m_ThreadStart = new ThreadStart(new Hydra.Framework.Mapping.Gps.NMEAEmulator().Emulator);

            this.m_Thread = new Thread(this.m_ThreadStart);

            try
            {
                this.m_Thread.Start();
            }
            catch (System.Exception ex)
            {
                throw (ex);
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Writes data to the GPS device. For instance RTCM data for Differential GPS.
        /// </summary>
        /// <param name="buffer">RTCM or control data to send to GPS</param>
        // **********************************************************************
        //
        public void WriteToGPS(byte[] buffer)
        {
            m_GpsPort.Write(buffer);
        }

        //
        // **********************************************************************
        /// <summary>
        /// Method called when a GPS event occured.
        /// This is where we call the methods that parses each kind of NMEA sentence
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Hydra.Framework.Mapping.Gps.GPSEventArgs"/> instance containing the event data.</param>
        // **********************************************************************
        //
        public void GPSDataEventHandler(object sender, GPSEventArgs e)
        {
            switch (e.TypeOfEvent)
            {
                case GPSEventType.GPRMC:
                    ParseRMC(e.Sentence);
                    break;

                case GPSEventType.GPGGA:
                    ParseGGA(e.Sentence);
                    break;

                case GPSEventType.GPGLL:
                    ParseGLL(e.Sentence);
                    break;

                case GPSEventType.GPGSA:
                    ParseGSA(e.Sentence);
                    break;

                case GPSEventType.GPGSV:
                    ParseGSV(e.Sentence);
                    break;

                case GPSEventType.PGRME:
                    ParseRME(e.Sentence);
                    break;

                case GPSEventType.TimeOut:
                    FireTimeOut();
                    break;

                case GPSEventType.Unknown:
                    GPSEventArgs e2 = new GPSEventArgs();
                    e2.TypeOfEvent = e.TypeOfEvent;
                    e2.Sentence = e.Sentence;
                    NewGPSFix(this, e2);
                    break;

                default:
                    break;
            }
        }

        //
        // **********************************************************************
        /// <summary>
        /// Stops the GPS thread and closes the port.
        /// </summary>
        // **********************************************************************
        //
        public void Stop()
        {
            GPGGA.FixQuality = NMEA.FixQualityEnum.Invalid;
            m_GpsPort.Stop();
            if (this.m_ThreadStart != null)
                this.m_Thread.Abort();

            this.m_ThreadStart = null;
            this.m_Thread = null;
        }

        //
        // **********************************************************************
        /// <summary>
        /// Analyzes a NMEA sentence and returns the corresponding NMEA sentence type
        /// </summary>
        /// <param name="strData">NMEA Sentence</param>
        /// <returns>Sentence type</returns>
        // **********************************************************************
        //
        internal static GPSEventType String2Eventtype(string strData)
        {
            if (strData.StartsWith("$" + GPSEventType.GPGGA.ToString()))
                return GPSEventType.GPGGA;
            else if (strData.StartsWith("$" + GPSEventType.GPGLL.ToString()))
                return GPSEventType.GPGLL;
            else if (strData.StartsWith("$" + GPSEventType.GPGSA.ToString()))
                return GPSEventType.GPGSA;
            else if (strData.StartsWith("$" + GPSEventType.GPGSV.ToString()))
                return GPSEventType.GPGSV;
            else if (strData.StartsWith("$" + GPSEventType.GPRMC.ToString()))
                return GPSEventType.GPRMC;
            else if (strData.StartsWith("$" + GPSEventType.PGRME.ToString()))
                return GPSEventType.PGRME;
            else
                return GPSEventType.Unknown;
        }
        #endregion
    }
}
