DOC

MobileGPSReader

By Margaret Gordon,2014-06-20 16:28
10 views 0
MobileGPSReader

    核心GPSReader

    // GPSReader.cs

    //

    // Copyright (C) 2003 JW Hedgehog, Inc. All rights reserved//

    // JW Hedgehog, Inc

    // http://www.jwhh.com

    //

    // Direct questions to mailto:jimw@jwhh.com

    //

    // This code, comments and information are provided "AS IS" with// no warrenty of any kind, either expressed or implied, including// but not limited to the implied warranties of merchentability and/or// fitness for a particular purpose

    // ---------------------------------------------------------------------using System;

    using System.Data;

    using System.Windows.Forms ;

    using System.Runtime.InteropServices ;

    using System.Text ;

    using System.Collections ;

    using System.Threading ;

    using System.IO ;

    namespace GPSExample.Util

    {

    ///

    /// Class that manages the GPS reading process.

    ///

    /// To get started using the class do the following

    /// 1) Construct GPSReader passing the port name and baud rate of the GPS device

    ///C#: GPSReader gps = new GPSReader("COM4:", 4800) ;

    ///VB: Dim WithEvents gps As New GPSReader("COM4:", 4800)

    ///2) Handle the OnGPSMessage event

    ///This event will fire each time the GPS sends an update

    ///3) Call gps.StartRead()

    ///Launches the GPS reading process on a background thread

    ///

    /// Use the StartRead and StopRead methods to control the GPS reading process. Before calling

    /// StartRead you must provide at least the port name in the form "COMx" (x is the port number) and

    /// the baud rate. You can do this using either a constructor or the PortName and BaudRate properties.

    /// Each time a GPS message is received, the OnGPSMessage event will fire passing an instance of the

    /// GPSEventArgs class containing the raw GPS sentence along with some of the values already parsed into

    /// read-only fields.

    ///

    /// This class does the actual GPS reading work on a background thread. The individual OnGPSMessage events

    /// are raised in a UI thread safe manner so no special handling is required. Because it is considered unsafe

    /// to interact with UI elements (TextBox, ListBox, etc.) from a thread other then the thread on which they were

    /// created, the GPS reader thread raises the OnGPSMessage event on the UI thread. These is acheived by deriving

    /// the GPSReader class from Control and then using the inherited Invoke method. Calling this.Invoke causes the

    /// event to be raised on the same thread on which the GPSReader was created. Since the GPSReader is usually

    /// created as a member of either a Form or a method on a Form, it is safe to assume that the GPSReader was

    /// created on the same thread as the Form and the Form's associated UI elements.

    ///

    /// Because there can sometimes be a short delay between when StartRead/StopRead are called and when the

    /// action actually occurs on the background thread, OnGPSStartRead and OnGPSStopRead events are provided.

    /// Each fires when the background thread actually performs the action. Like the OnGPSMessage event,

    /// these are raised in a UI thread safe manner.

    ///

    /// To support the broadest number of GPS devices, the class actually supports two different read modes.

    /// The preferred read mode is "MesssageMode". In MessageMode, we let the COMM port driver monitor the

    /// GPS stream watching for the arrival of the carriage-return (\n). Our code blocks until the COMM port

    /// driver notifies us of the carriage-return, at which time we then go read the entire GPS sentence from

    /// the COMM port driver.

    /// The alternative read mode is "CharacterMode". In CharacterMode we read the data character-by-character

    /// from the COMM port driver manually building the GPS sentence and watching for the carriage-return. This

    /// mode was added because experimentation showed that some GPS devices that simulate COMM ports(i.e. the GPS

    /// might be an expansion pack of compact flash card but appears as a COMM port to the device) do not

    /// support letting the COMM port driver monitor for the carriage-return.

    /// Using the PreferredReadMode property, you can set which mode the GPSReader uses If you choose MessageMode

    /// or Auto (the default) The GPSReader class tests to see if the driver supports MessageMode and if so use it.

    /// Otherwise it will downgrade to CharacterMode. The ActiveReadMode property indicates which read mode is

    /// actually being used.

    /// The code that tests for MessageMode support is in the

    "DriverSupportsMessageMode method. Because its not

    /// possible to test every GPS in existence there is no way to be 100% sure that this test will always work but

    /// on the devices tested it has been reliable.

    ///

    ***************************************************************************************************************

    /// Note

    ***********************************************************

    /// If you try reading from a device and the GPSReader never returns any data, the cause may be that MessageMode

    /// support has been falsly indicated as supported. Setting the GPSReader PreferredReadMode to ReadMode.Character

/// should over come the problem. The need to do this has never been

    observered but since its not possible to test

    /// every GPS device the possibility always exists.///

    ********************************************************************

    *******************************************

    ///

    public class GPSReader : Control

    {

    //

    *************************************************************

    // Constructors

    //

    *************************************************************

    ///

    /// Default constructor

    /// At a minimum, will need to set the PortName and BaudRate

    properties before calling StartRead

    ///

    public GPSReader()

    {

    }

    ///

    /// Constructor - Accepts COMM port name (COMx:)

    /// Will need to set the BaudRate properties before calling StartRead

    ///

    ///

    public GPSReader(string portName)

    : this()

    {

    _portName = portName ;

    }

    ///

    /// Constructor - Accepts COMM port name (COMx:) and BaudRate

    /// If default COMM port settings (NoParity, 8 bits/byte and

    OneStopBit) are acceptable,

    /// can call StartRead without setting any of the configuration

    properties

    ///

    ///

    ///

    public GPSReader(string portName, int baudRate)

    : this(portName)

    {

    _baudRate = baudRate ;

    }

    ///

    /// Constructor - verbose

    /// Provides full control over all COMM port settings

    ///

    ///

    ///

    ///

    ///

    ///

    public GPSReader(string portName, int baudRate, ParitySetting parity, byte byteSize, StopBitsSetting stopBits)

    : this(portName, baudRate)

    {

    _parity = parity ;

    _byteSize = byteSize ;

    _stopBits = stopBits ;

    }

    //

    *************************************************************

    // Events

    //

    *************************************************************

    ///

    /// Fires each time a GPS message is received

    ///

    public event GPSEventHandler OnGPSMessage ;

    ///

    /// Fires when the background thread begins the read process

    ///

    public event EventHandler OnGPSReadStart ;

    ///

    /// Fires when the background thread exits the read process

    ///

    public event EventHandler OnGPSReadStop ;

    //

    *************************************************************

    // Start/Stop Reading

    //

    *************************************************************

    ///

    /// Initiate GPS Reading

    /// Actual reading done on a background thread - this method returns

    immediatly

    ///

    /// Throws an error if either PortName or BaudRate not set

    ///

    public void StartRead()

    {

    // Verify that we know the port name and baud rate

    if (_baudRate == baudRateNotSet || _portName == portNameNotSet)

    throw new ApplicationException(" Must set Baud Rate & Port Name before opening the port") ;

    Cursor.Current = Cursors.WaitCursor ;

    _readData = true ;

    _gpsReadThread = new Thread(new

    ThreadStart(this.GPSReadLoop)) ;

    _gpsReadThread.Start() ;

    Cursor.Current = Cursors.Default ;

    }

///

    /// Terminate GPS Reading

    /// Sets _readData to false which exits the underlying read loop

    /// Also closes the COMM port which aborts any pending COMM port

    operations

    ///

    public void StopRead()

    {

    Cursor.Current = Cursors.WaitCursor ;

    _readData = false ;

    Thread.Sleep(500) ;// Give thread time to finish any

    pending work

    ClosePort() ;

    Cursor.Current = Cursors.Default ;}

    //

    *************************************************************

    // Port Setup and configuration//

    *************************************************************

    ///

    /// Set Port Name (COMx:)

    ///

    public string PortName

    {

    get { return _portName ;}

    set {_portName = value ;}

    }

    ///

    /// Set Baud Rate - No Default

    ///

    public int BaudRate

    {

    get { return _baudRate ;}

    set {_baudRate = value ;}

    }

    ///

    /// Set Port Parity - defaults to NoParity///

    public ParitySetting Parity

    {

    get { return _parity ;}

    set {_parity = value ;}

    }

    ///

    /// Set Port StopBits - defaults to OneStopBit

    ///

    public StopBitsSetting StopBits

    {

    get { return _stopBits ;}

    set {_stopBits = value ;}

    }

    ///

    /// Set Port Byte Size (in bits) - defaults to 8

    ///

    public byte ByteSize

    {

    get { return _byteSize ;}

    set {_byteSize = value ;}

    }

    //

    *************************************************************

    // Port Reading

    //

    *************************************************************

    ///

    /// Main Read Loop

    /// After openning the COMM Port, repeatedly retrieves a GPS sentence.

    /// If the sentence appears correct (starts with $GP) the GPSMessage

    event is fired

    /// If an exception should occur, the COMM Port is closed and the exception is propagated

    ///

    private void GPSReadLoop()

    {

    EventHandler GPSMessageHandler = new

    EventHandler(this.DispatchGPSMessage) ;

    EventHandler GPSReadStartHandler = new

    EventHandler(this.DispatchGPSReadStart) ;

    EventHandler GPSReadStopHandler = new

    EventHandler(this.DispatchGPSReadStop) ;

    OpenPort() ;

    // Signal beginning of read process - event fired on UI thread

    this.Invoke(GPSReadStartHandler) ;

    try

    {

    gpsSentence = ReadSentence() ;

    while (gpsSentence != null)// will only be null if StopRead() is called

    {

    // If appears to be valid message, Signal GPS Message received - event fired on UI thread

    // Invoke blocks this thread until the method wrapped by the GPSMessageHandler returns.

    // If this code is ever changed to an asynchronous method of execution then the gpsSentence

    // variable will have to be protected against simultaneous access.

    if (gpsSentence.StartsWith("$GP"))

    this.Invoke(GPSMessageHandler) ;

    gpsSentence = ReadSentence() ;

    }

    }

    catch (Exception e)

    { // If any exception is thrown, close the COMM port and propagate the exception

    ClosePort() ;

    throw e ;

    }

    // Signal end of read process - event fired on UI thread

    this.Invoke(GPSReadStopHandler) ;

    }

    ///

    /// Handoff to the appopriate ReadSentence based on active ReadMode///

    /// GPS Sentence

    private string ReadSentence()

    {

    string returnVal = string.Empty ;

    if (_activeReadMode == ReadMode.Message)

    returnVal = ReadSentence_Message() ;

    else

    returnVal = ReadSentence_Character() ;

    return returnVal ;

    }

    ///

    /// Retrieves the sentence and translates from ASCII to Unicode///

    /// GPS Sentence

    private string ReadSentence_Message()

    {

    Byte[] buffer;

    Byte[] temp = new Byte[1] ;

    int numBytesRead = 0 ;

    Encoding asciiConverter = Encoding.ASCII ;

    // keep reading until we are told to stop or we get something

    // looped read should only occur if read errors are encountered

    bool portReturnedData = ReadPort_Message(MAX_MESSAGE,

    out buffer, out numBytesRead) ;

    while (_readData && ! portReturnedData)

    portReturnedData = ReadPort_Message(MAX_MESSAGE,

    out buffer, out numBytesRead) ;

    // if still reading, Translate from ASCII to Unicode

    return _readData ? asciiConverter.GetString(buffer, 0,

    numBytesRead) : null ;

    }

    ///

    /// Builds the sentence by doing single character reads then translates

    from ASCII to Unicode

    /// Additional code is used to adjust whether translation starts at the

    beginning of the string

    /// or skips the first character. This had to be added because one of

    the GPS simulators we were

    /// using would send an extra character after the carriage-return (\n).///

    /// GPS Sentence

    public string ReadSentence_Character()

    {

    Byte[] data = new Byte[MAX_MESSAGE] ;

    Byte temp = 0 ;

    int pos = 0 ;

    Encoding asciiConverter = Encoding.ASCII ;

    // Build the sentence until carriage-return encountered

    while(_readData && temp != endOfGPSSentenceMarker)

    {

    temp = ReadPort_Character() ;

    data[pos++] = temp ;

    }

    // Translation adjustment to handle extra character sent by some simulation programs

    int translateStartPos = 0 ;

    int translateCount = pos ;

    if (data[0] != (Byte) '$')

    {

    translateStartPos++ ;

    translateCount-- ;

    }

    // Perform translation

    return _readData ? asciiConverter.GetString(data, translateStartPos, translateCount) : null;

    }

    //

    *************************************************************

    // Read mode related methods and properties

    //

    *************************************************************

    ///

    /// Indicates whether the COMM Port driver supports reading entire GPS messages at once

    /// The preferred way to read data is to let the driver signal when a carriage-return (\n)

    /// is received and then we read the whole message at once. Experimentation has shown that

    /// some of the GPS devices that simulate a serial port do not support this mode. In that

    /// case, we need to read the port data character-by-character.

    /// On the GPS devices tested, support for character notification can be verified by attempting

    /// to set the EvtChar (event character) member of the DCB structure using SetCommState and then

    /// reading the DCB back with GetCommState. If character notification is supported the returned

    /// DCB will contain the EvtChar that was set with SetCommState. If it is not supported, EvtChar

    /// will contain 0. Because its not possible to test every GPS in existence there is no way to be

    /// 100% sure that this test will always work but on the devices tested it has been reliable.

    ///

    ///

    public bool DriverSupportsMessageMode()

    {

    // Verify that we know the port name

    if (_portName == portNameNotSet)

    throw new

    ApplicationException(" Must set Port Name before calling this method") ;

    uint localPortHandle = INVALID_FILE_HANDLE ;

    DCB dcb = new DCB() ;

    // Check to see if port is currently open

    bool openPortLocally = _portHandle == INVALID_FILE_HANDLE

    ;

    // If port not open then open it, otherwise use current handle

    localPortHandle = openPortLocally ? OpenPort_Raw(_portName)

    : _portHandle ;

    if (localPortHandle == INVALID_FILE_HANDLE)

    throw new

    ApplicationException(" Invalid port: " + _portName) ;

    // Get current port settings

    GetCommState(localPortHandle, dcb) ;

    // Attempt to set event character

    dcb.EvtChar = endOfGPSSentenceMarker ;

    SetCommState(localPortHandle, dcb) ;

    // Read port settings back

    GetCommState(localPortHandle, dcb) ;

    // if port opened locally - close it

    if (openPortLocally)

    CloseHandle(localPortHandle) ;

    // Check to see if driver accepted event character

     return dcb.EvtChar == endOfGPSSentenceMarker ;

    }

    ///

    /// Preferred Read Mode to use - Defaults to Auto

    /// In Auto, reader will attempt message mode if driver supports it,

    otherwise uses character-by-character mode

    /// Undefined doesn't make sense in this usage, so if someone tries to

    set the value to Undefined, make it Auto

    ///

    public ReadMode PreferredReadMode

    {

    get {return _preferredReadMode;}

    set {_preferredReadMode = (value == ReadMode.Undefined) ? ReadMode.Auto : value ;}

    }

    ///

    /// Read Mode that the GPSReader is actually using

    /// Set to Undefined until reading is started

    ///

    public ReadMode ActiveReadMode

    {

    get {return _activeReadMode;}

    }

    //

    *************************************************************

    // Other COMM port related methods

    //

    *************************************************************

    ///

    /// This method doesn't actually have anything to do with GPS reading

    but is helpful for determining

    /// which ports are available on the device

    /// The code simply attempts to open (and immediatly close) all COMM

    ports between COM1: and COM9:, if it opens

    /// successfully, then it is added to the port list

    ///

    /// string array of available ports

    public static string[] GetPortList()

    {

    ArrayList portList = new ArrayList() ;

    uint hPort = INVALID_FILE_HANDLE ;

    // Walk list of possible ports

    for (int i = 1; i < 10; i++)

    {

    string port = "COM" + i.ToString() + ":" ;

    hPort = OpenPort_Raw(port) ;

    if (hPort != INVALID_FILE_HANDLE)

    {

    portList.Add(port) ;

    CloseHandle(hPort) ;

    }

    }

    // Convert to regular string array

    return (string []) portList.ToArray(typeof(string)) ;

    }

    #region Constants

    private const uint GENERIC_READ= 0x80000000;

    private const uint OPEN_EXISTING= 3;

    private const uint INVALID_FILE_HANDLE= 0xFFFFFFFF ;

    private const uint EV_RXFLAG

    = 0x0002 ;

    private const int baudRateNotSet= 0 ;

    private const string portNameNotSet= "PortNotSet" ;

    private const ParitySetting defaultParity= ParitySetting.NoParity;

    private const StopBitsSetting defaultStopBits= StopBitsSetting.OneStopBit ;

    private const byte defaultByteSize= 8 ;

    private const sbyte endOfGPSSentenceMarker= (sbyte)'\n' ;

    private const int MAX_MESSAGE= 256 ;

    #endregion

    #region Private fields

Report this document

For any questions or suggestions please email
cust-service@docsford.com