Working with GPS in WinCE (C #)

    Introduction


    Hello!
    In this article I want to consider the implementation of access to GPS data in devices based on WindowsCE. When creating the SCOUT-Navigator product, it was necessary to develop an application that works both in WinCE version 5.0 and in WinCE version 6.0, which can receive NMEA data from the navigation receiver and write it to the log.



    Decision


    To work with GPS in WinCE both version 5.0 and version 6.0, it is easiest to use work with a COM port. You can find in the device which COM port provides GPS data using the program: DeviceManager.


    Often, firmware manufacturers have already ensured that there are two GPS COM ports. This allows you to breed software that requires GPS and navigation so that they do not struggle for access to the COM port. Suppose that we will use the COM port in exclusive access.
    To get NMEA data (http://ru.wikipedia.org/wiki/NMEA_0183), we just need to open the COM port, read the data from it, then close the COM port. What in C # looks like this:

    ///
    /// Чтение данных COM порта
    ///
    ///Имя COM-порта
    ///Скоростьобмена
    private void ReadData(string comPortName,int baudRate)
    {
        var serialPort = newSerialPort(comPortName)
        {
            BaudRate = baudRate,
            DataBits = 8,
            Parity = Parity.None,
            StopBits = StopBits.One,
            RtsEnable = true
        };
        serialPort.Open();
        //Чтение из COM-порта
        //.....
        var line=serialPort.ReadLine(); 
        //.....
        serialPort.Close();
    }
    

    Despite the fact that everything looks very trivial, the above code often does not work due to access errors to the COM port. (For example, an error often occurs: "UnauthorizedAccessException: Access to the port is denied").

    Let's not get upset, there is another approach that works.

    Great people from the OpenNetCf project carefully provide the source code for their own SerialPort.

    http://serial.codeplex.com/SourceControl/changeset/view/25883#435389

    Add the OpenNetCf.IO.Serial assembly to the project.

    The class for working with the GPS COM port will look like this:
    ///
    /// Класс работы с GPS COM-портом
    ///
    public class GpsPort: IDisposable
    {
        private Port _serialPort;
        private bool _disposed;
        private readonly object _syncObject = new object();
        public bool IsOpen { get; private set; }
        ///
        ///Порт чтения данных GPS
        ///
        ///Наименованиепорта
        ///Скоростьпорта
        public GpsPort(string serialPortName, int baudRate)
        {
            _serialPort = newPort(serialPortName, newDetailedPortSettings { BasicSettings = newBasicPortSettings { BaudRate = (BaudRates)baudRate }, EOFChar = '\n' });
            _disposed = false;
        }
        public void Dispose()
        {
            if (!_disposed)
            {
                Close();
                _serialPort = null;
                _disposed = true;
            }
            GC.SuppressFinalize(this);
        }
        ///
        ///Destructor
        ///
        ~GpsPort()
        {
            Dispose();
        }
        ///
        /// Открываем порт для чтения данных
        ///
        public void Open()
        {
            try
            {
                _serialPort.Open();
                _serialPort.DataReceived += SerialPortDataReceived;
                IsOpen = true;
            }
            catch (Exception ex)
            {
                throw new ApplicationException("Could not open com port", ex);
            }   
        }
        ///
        /// Получение данных с порта
        ///
        private void SerialPortDataReceived()
        {
            lock (_syncObject)
            {
                if (_serialPort == null || !_serialPort.IsOpen) return;
                var realPortData = _serialPort.Input;
                if (realPortData.Length == 0) return;
                Debug.Write(Encoding.GetEncoding("ASCII").GetString(realPortData, 0, realPortData.Length));
            }
        }
        ///
        ///Закрытиепорта
        ///
        public void Close()
        {
            IsOpen = false;
            if (_serialPort != null)
            if (_serialPort.IsOpen)
            {
                _serialPort.DataReceived -= SerialPortDataReceived;
                _serialPort.Close();
            }
        }
    }
    

    In the SerialPortDataReceived method, we write, in fact, the parsing of NMEA strings.
    To do this, you can:
    1. Write your NMEA parser;
    2. Use SharpGps ;
    3. Use the NMEA-0183-2-0-sentense-parser-builder ( Here Developer article on Habré);
    4. Any other parser.

    I have not tried using Sentenseparserbuilder, but there is something to tell about SharpGps.

    I will make a small digression from the topic. The library has a funny mistake in calculating the checksum, although many of my colleagues believe that this is not a mistake, but a logical behavior. But first things first:

    In the NMEA 0183 protocol ( http://www.tronico.fi/OH6NT/docs/NMEA0183.pdf ) the checksum is described as a 2-digit hexadecimal number - the XOR checksum of all bytes in a line between "$" And "*".

    SharpGps has a function for checking the correctness of the checksum in the package:
    private bool CheckSentence(string strSentence)
    {
        int iStart = strSentence.IndexOf('$');
        int iEnd = strSentence.IndexOf('*');
        //If start/stop isn't found it probably doesn't contain a checksum,
        //or there is no checksum after *. In such cases just return true.
        if (iStart >= iEnd || iEnd + 3 > strSentence.Length) return true;
        byte result = 0;
        for (int i = iStart + 1; i < iEnd; i++)
        {
            result ^= (byte)strSentence[i];
        }
        return (result.ToString("X") == strSentence.Substring(iEnd + 1, 2));
    }
    

    This function works great if the navigation receiver transmits a checksum in the form of two numbers (0x01, 0x02, etc.), as stated in the protocol. But any ideal code is broken into reality, in which navigation receivers transmit packets with a checksum without adding a leading zero (0x1,0x2).

    In a running application, it turns out that some of the packets are eliminated. In this case, the feeling that everything seems to be working. But the track, although it is, is of very poor quality.

    To make it work, the last line can be rewritten, at least like this:

    var packCrc = byte.Parse(strSentence.Substring(iEnd + 1, 2), 
           System.Globalization.NumberStyles.AllowHexSpecifier);
    return (result == packCrc);
    

    With a retreat all.

    To store navigation data, it was decided to use SqlServer Compact Edition. It is very easy to integrate into the application, and use it in development. I did not plan to describe the use of SqlServer Compact in this article, if you want to see an article on using SqlServer Compact in WinCe applications, you can mark it in the comments.

    Conclusion



    In this article I gave a solution to the problem of accessing GPS data on WinCe devices, the solution was tested on navigators of various manufacturers (Prestigio, Texet, Shturmann, Mio) with different versions of WinCE. I hope that it will save a part of the rake awaiting you on the way of development under WinCE.
    Thanks for attention. Waiting for questions and comments in the comments.


    Also popular now: