Работа с GPS в WinCE (C#)

    Введение


    Всем привет!
    В этой статье я хочу рассмотреть вопрос реализации доступа к данным GPS в устройствах на базе WindowsCE. При создании продукта СКАУТ-Навигатор, необходимо было разработать приложение, работающее как в WinСЕ версии 5.0, так и в WinCE версии 6.0, которое умеет получать данные NMEA с навигационного приемника, и записывать их в журнал.



    Решение


    Для работы с GPS в WinCE как версии 5.0, так и версии 6.0 проще всего использовать работу с COM портом. Найти в устройстве, какой COM-порт предоставляет данные GPS, можно при помощи программы: DeviceManager.


    Часто производители прошивок уже позаботились о том, чтобы COM портов GPS было два. Это позволяет развести ПО, которому требуется GPS и навигационное, чтобы они не боролись за доступ к COM-порту. Предположим, что COM порт мы будем использовать в монопольном доступе.
    Чтобы получить данные NMEA (http://ru.wikipedia.org/wiki/NMEA_0183), нам нужно всего-то открыть COM порт, прочитать с него данные, потом закрыть COM порт. Что на C# выглядит так:

    ///<summary>
    /// Чтение данных COM порта
    ///</summary>
    ///<param name="comPortName">Имя COM-порта</param>
    ///<param name="baudRate">Скоростьобмена</param>
    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();
    }
    

    Несмотря на то, что всё выглядит весьма тривиально, приведенный код часто не работает из-за ошибок доступа к COM-порту. (Например, часто возникает ошибка: «UnauthorizedAccessException: Access to the port is denied»).

    Не будем расстраиваться, есть другой подход, который работает.

    Замечательные люди из проекта OpenNetCf заботливо предоставляют исходные коды собственного SerialPort.

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

    Добавляем в проект сборку OpenNetCf.IO.Serial

    Класс работы с COM-портом GPS будет выглядеть так:
    ///<summary>
    /// Класс работы с GPS COM-портом
    ///</summary>
    public class GpsPort: IDisposable
    {
        private Port _serialPort;
        private bool _disposed;
        private readonly object _syncObject = new object();
    
        public bool IsOpen { get; private set; }
    
        ///<summary>
        ///Порт чтения данных GPS
        ///</summary>
        ///<param name="serialPortName">Наименованиепорта</param>
        ///<param name="baudRate">Скоростьпорта</param>
        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);
        }
    
        ///<summary>
        ///Destructor
        ///</summary>
        ~GpsPort()
        {
            Dispose();
        }
    
        ///<summary>
        /// Открываем порт для чтения данных
        ///</summary>
        public void Open()
        {
            try
            {
                _serialPort.Open();
                _serialPort.DataReceived += SerialPortDataReceived;
                IsOpen = true;
            }
            catch (Exception ex)
            {
                throw new ApplicationException("Could not open com port", ex);
            }   
        }
    
        ///<summary>
        /// Получение данных с порта
        ///</summary>
        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));
    
            }
        }
    
        ///<summary>
        ///Закрытиепорта
        ///</summary>
        public void Close()
        {
            IsOpen = false;
            if (_serialPort != null)
            if (_serialPort.IsOpen)
            {
                _serialPort.DataReceived -= SerialPortDataReceived;
                _serialPort.Close();
            }
        }
    }
    

    Вметоде SerialPortDataReceived пишем, собственно, парсинг NMEA строк.
    Для этого можно:
    1. Написать свой парсер NMEA;
    2. Использовать SharpGps;
    3. Использовать NMEA-0183-2-0-Sentense-parser-builder (Тут статья разработчика на Хабре);
    4. Любой другой парсер.

    Sentenseparserbuilder я использовать не пробовал, а вот про SharpGps есть, что рассказать.

    Сделаю небольшое отступление от темы. В библиотеке есть забавная ошибка в подсчете контрольной суммы, хотя многие мои коллеги считают, что это не ошибка, а логичное поведение. Но обо всём по порядку:

    В протоколе NMEA 0183 (http://www.tronico.fi/OH6NT/docs/NMEA0183.pdf) контрольная сумма описывается как 2-значное 16-ричное число — контрольная XOR-сумма всех байт в строке между «$» и «*».

    В SharpGps есть функция проверки корректности контрольной суммы в пакете:
    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));
    }
    

    Эта функция великолепно работает, если навигационный приемник передает контрольную сумму в виде двух чисел (0x01, 0x02 и т.д.), как и заявлено в протоколе. Но любой идеальный код разбивается о реальность, в которой навигационные приемники передают пакеты с контрольной суммой, не добавляя ведущий ноль (0x1,0x2).

    В работающем приложении получается, что часть пакетов отсеивается. При этом ощущение, что всё вроде бы работает. Но трек, хоть он и есть, но очень плохого качества.

    Чтобы всё заработало, последнюю строчку можно переписать, хотя бы так:

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

    С отступлением всё.

    Для хранения навигационных данных было решено использовать SqlServer Compact Edition. Его очень просто интегрировать в приложение, и использовать в разработке. Описывать использование SqlServer Compact в данной статье я не планировал, если есть желание увидеть статью по использованию SqlServer Compact в приложениях на WinCe можете его обозначить в комментариях.

    Заключение



    В данной статье я привел решение проблемы доступа к GPS данным на WinCe устройствах, решение опробовано на навигаторах различных производителей (Prestigio, Texet, Shturmann, Mio) с разными версиями WinCE. Надеюсь что от части граблей подстерегающих вас на пути разработки под WinCE она избавит.
    Спасибо за внимание. Жду вопросов и замечаний в комментариях.

    ГК «СКАУТ»
    0.00
    Company
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 2

      0
      Интересная статья! Я сейчас занимаюсь проектом по связыванию автомагнитолы с навигацией на win ce 5.0 с автомобилем. Пока планирую сделать управление автозапуском двигателя с помощью связки usb-uart + исполнительная часть на attiny13 с транзисторными ключами и реле.
      Загвоздка в том, что я совершенно не представляю как в win ce работать с usb-uart. Он должен выглядеть тоже как com-port?
      Если не сложно, прошу поделиться информацией или ссылками, где можно почитать по теме!
        0
        По поводу контрольной суммы — корректнее всего byte.Parse() скормить строчку между '*' и концом посылки, а что туда попадет 0x2 или 0x02 — не суть важно, ибо byte.Parse() это съедает.

        Only users with full accounts can post comments. Log in, please.