Введение
Всем привет!
В этой статье я хочу рассмотреть вопрос реализации доступа к данным 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 строк.
Для этого можно:
- Написать свой парсер NMEA;
- Использовать SharpGps;
- Использовать NMEA-0183-2-0-Sentense-parser-builder (Тут статья разработчика на Хабре);
- Любой другой парсер.
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 она избавит.
Спасибо за внимание. Жду вопросов и замечаний в комментариях.
ссылка на оригинал статьи http://habrahabr.ru/company/scout/blog/157975/
Добавить комментарий