Пишем свой компас для HTC

Однажды я обзавёлся чудесным девайсом HTC HD2, и обнаружил на нём приложение «Компас». После недолгого изучения этой маленькой но очень интересной утилитки, был сделан вывод о том, что с GPS она никак не связана а действуюет сама по себе, причём показывает направление очень точно. Так вот. С тех самых пор, как я обнаружил эту замечательную способность у аппарата, мне жуть как хотелось написать такую же программку которая будет показывать направление на стороны света, не то чтобы оно очень надо — но вот просто хочется разобраться и всё тут.
Короче, разобрался! И ниже приведу листинг библиотеки для работы с компасом (она оказалась очень простой), и листинг небольшой программки которая использует компас.

Итак, первая часть, библиотека для работы с Компасом в HTC:
using System;
using System.Runtime.InteropServices;

namespace CompassHTC
{

    public interface ICompass
    {
        /// <summary>
        /// Заголовки.
        /// </summary>
        double Angle { get; }
        /// <summary>
        /// Правильный угол
        /// </summary>
        bool IsValid { get; }
        /// <summary>
        /// Запуск сенсора компаса
        /// </summary>
        void Start();
        /// <summary>
        /// Остановка сенсора компаса
        /// </summary>
        void Stop();
        /// <summary>
        /// Перезапуск сенсора компаса
        /// </summary>
        void Reset();
    }

    public class HTCCompass : ICompass
    {
        private IntPtr devHandle;
        private int srvHandle;

        public short[] buffer = new short[5];

        #region ICompass

        public double Angle
        {
            get
            {
                // Пытаемся присоедениться - если возможно
                if (!ready) ready = initCompass();
                return ready && updateData() ? buffer[3] : -1.0;
            }
        }

        bool ready = false;
        public bool IsValid { get { return ready; } }
        public void Start() { ready = initCompass(); }
        public void Stop() { shutdownCompass(); ready = false; }
        public void Reset() { Stop(); Start(); }

        #endregion

        private const int DIO_INIT = 0x3000001;
        private const int DIO_QUIT = 0x3000002;
        private const int DIO_READ = 0x3000003;
        private const long INVALID_HANDLE_VALUE = -1L;
        //private const int LMEM_FIXED = 0;
        //private const int LMEM_ZEROINIT = 0x40;
        //private const int LPTR = 0x40;

        public bool initCompass()
        {
            if (this.devHandle != IntPtr.Zero)
                return true;

            this.devHandle = CreateFile("SEN0:", 0, 0, IntPtr.Zero, 3, 0x80, IntPtr.Zero);
            if ((this.devHandle.ToInt32() == INVALID_HANDLE_VALUE) | (this.devHandle.ToInt32() == 0))
            {
                this.devHandle = IntPtr.Zero;
                this.srvHandle = RegisterService("SEN", 0, "HTCSensorService.dll", 1);
                if (this.srvHandle == 0)
                    return false;

                this.devHandle = CreateFile("SEN0:", 0, 0, IntPtr.Zero, 3, 0x80, IntPtr.Zero);
                if ((this.devHandle.ToInt32() == INVALID_HANDLE_VALUE) | (this.devHandle.ToInt32() == 0))
                    return false;
            }
            int read = 0;
            if (DeviceIoControl(this.devHandle, DIO_INIT, IntPtr.Zero, 0, IntPtr.Zero, 0, out read, IntPtr.Zero) == 0)
                return false;
            return true;
        }

        public bool shutdownCompass()
        {
            if (this.devHandle != IntPtr.Zero)
            {
                int read = 0;
                if (DeviceIoControl(this.devHandle, DIO_QUIT, IntPtr.Zero, 0, IntPtr.Zero, 0, out read, IntPtr.Zero) == 0)
                    return false;
                this.devHandle = IntPtr.Zero;
            }
            return true;
        }

        public override string ToString()
        {
            return ("X: " + buffer[0].ToString() +
            "\r\nY: " + buffer[1].ToString() +
            "\r\nZ: " + buffer[2].ToString() +
            "\r\nA: " + buffer[3].ToString() +
            "\r\nS: " + buffer[4].ToString() +
            "\r\n");
        }

        public bool updateData()
        {
            byte[] buf = new byte[10];
            IntPtr getMemory = new IntPtr();
            if ((this.devHandle == IntPtr.Zero) | (this.devHandle == IntPtr.Zero))
            {
                return false;
            }
            getMemory = LocalAlloc(0x40, buf.Length);
            Marshal.Copy(buf, 0, getMemory, buf.Length);
            int read = 0;
            int result = DeviceIoControl(this.devHandle, DIO_READ, IntPtr.Zero, 0, getMemory, buf.Length, out read, IntPtr.Zero);
            Marshal.Copy(getMemory, buf, 0, buf.Length);
            if (getMemory != IntPtr.Zero)
            {
                LocalFree(getMemory);
            }
            if (result == 0)
            {
                return false;
            }
            Buffer.BlockCopy(buf, 0, buffer, 0, buf.Length);
            return true;
        }

        # region PInvokes

        [DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern IntPtr LocalAlloc(int uFlags, int uBytes);
        [DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern IntPtr LocalFree(IntPtr hMem);
        [DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern int RegisterService(string lpszType, int dwIndex, string lpszLib, int dwInfo);
        [DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
        [DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern int DeviceIoControl([In] IntPtr hDevice, [In] int dwIoControlCode, [In] IntPtr lpInputBuffer, [In] int nInBufferSize, [Out] IntPtr lpOutBuffer, [In] int nOutBufferSize, out int lpBytesReturned, [In] IntPtr lpOverlapped);

        #endregion
    }

}


Если очень кратко то работа с библиотекой происходит так:
CompassHTC.HTCCompass compass = new CompassHTC.HTCCompass();
compass.Start();
//Здесь получаем угол от направления на северный полюс.
double compas_angle = compass.Angle; 
compass.Stop();
//Вот и всё.


Теперь часть вторая, программа которая работает с этой библиотекой и отображает компас на экране:
namespace CompasTest
{
    public partial class FormCompas : Form
    {
        public FormCompas()
        {
            InitializeComponent();
        }

        //Создаём компас.
        CompassHTC.HTCCompass compass = new CompassHTC.HTCCompass();
        //Переменные для  привязки координат изображения к Panel1.
        int center_x = 100;
        int center_y = 100;
        int radius = 80;
        double compas_angle = 0;
        double compas_angle_old = 0;//Это чтобы лишний раз не обновлять изображение.

        private void button1_Click(object sender, EventArgs e)
        {
            //Останавливаем таймер, сомпас и выходим.
            timer1.Enabled = false; 
            compass.Stop();
            Application.Exit();
        }

        //При загрузке заполняем основные параметры, запускаем компас и таймер.
        private void FormCompas_Load(object sender, EventArgs e)
        {
            //Запускаем компас, и включаем таймер.
            //Параметры в рассчёте на экран 480 на 800. - если другой надо менять либо писать обработчик размеров экрана.
            panel1.Width = this.Width - 80;
            panel1.Height = panel1.Width;
            center_x = (int)(panel1.Width / 2);
            center_y = center_x;
            radius = center_x - 50;
            compass.Start();
            timer1.Enabled = true;
        }

        //Рисуем на панели "красивенький" компас.
        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            
            Graphics g = panel1.CreateGraphics();
            g.Clear(Color.Black);
            Pen penN = new Pen(Color.Blue);
            penN.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
            penN.Width = 10;

            Pen penS = new Pen(Color.Red);
            penS.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
            penS.Width = 10;

            Pen penZW = new Pen(Color.Green);
            penZW.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
            penZW.Width = 10;

            Pen penKrug = new Pen(Color.Gray);
            penKrug.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
            penKrug.Width = 10;

            Rectangle rect = new Rectangle(center_x-radius,center_y-radius,radius+radius,radius+radius);
            g.DrawEllipse(penKrug, rect);

            //Задаём шрифт и его текстуру... (для обозначения направлений).
            //В рассчёте на экран 480 на 800. - если другой надо менять либо писать обработчик размеров экрана.
            Font fn = new Font(FontFamily.GenericMonospace, 20.0F, FontStyle.Bold);
            Brush br1 = new SolidBrush(Color.White);



            //Рисуем зеленую полоску на восток и на запад.
            double angle = Math.PI * (270 - compas_angle) / 180.0;
            double x = Math.Sin(angle) * radius;
            double y = Math.Cos(angle) * radius;
            g.DrawLine(penZW, center_x, center_y, center_x + (int)x, center_y - (int)y);
            //И кружок для буковки...
            Brush br = new SolidBrush(Color.Green);
            g.FillEllipse(br, center_x + (int)x - 30, center_y - (int)y - 30, 60, 60);
            //буковка.
            Rectangle rc = new Rectangle(center_x + (int)x - 15, center_y - (int)y - 25, 50, 50);
            g.DrawString("W", fn, br1, rc);

            angle = Math.PI * (90 - compas_angle) / 180.0;
            x = Math.Sin(angle) * radius;
            y = Math.Cos(angle) * radius;
            g.DrawLine(penZW, center_x, center_y, center_x + (int)x, center_y - (int)y);
            //И кружок для буковки...
            g.FillEllipse(br, center_x + (int)x - 30, center_y - (int)y - 30, 60, 60);
            //буковка.
            rc = new Rectangle(center_x + (int)x - 15, center_y - (int)y - 25, 50, 50);
            g.DrawString("E", fn, br1, rc);
            
            

            //Рисуем синию полоску на север.
            angle = Math.PI * (360-compas_angle) / 180.0;
            x = Math.Sin(angle) * radius;
            y = Math.Cos(angle) * radius;
            g.DrawLine(penN, center_x, center_y, center_x+(int)x, center_y-(int)y);
            //И кружок для буковки...
            br = new SolidBrush(Color.Blue);
            g.FillEllipse(br, center_x + (int)x - 30, center_y - (int)y - 30, 60, 60);
            //буковка.
            rc = new Rectangle(center_x + (int)x - 15, center_y - (int)y - 25, 50, 50);
            g.DrawString("N", fn, br1,rc);

            //Рисуем красную полоску на юг.
            angle = Math.PI * (180 - compas_angle) / 180.0;
            x = Math.Sin(angle) * radius;
            y = Math.Cos(angle) * radius;
            g.DrawLine(penS, center_x, center_y, center_x + (int)x, center_y - (int)y);
            //И кружок для буковки...
            br = new SolidBrush(Color.Red);
            g.FillEllipse(br, center_x + (int)x - 30, center_y - (int)y - 30, 60, 60);
            //буковка.
            rc = new Rectangle(center_x + (int)x - 15, center_y - (int)y - 25, 50, 50);
            g.DrawString("S", fn, br1, rc);


        }

        //То что выполняем по таймеру.
        private void timer1_Tick(object sender, EventArgs e)
        {
            //Проверяем активен ли компас, и если всё впорядке выводим угол - относительно севера.
            //И запускаем рисование стрелок.
            if (compass.IsValid == true)
            {
                compas_angle = compass.Angle;
                label1.Text = "Угол: " + compas_angle.ToString();
                //Проверяем изменился ли угол (ато, зачем почём зря рисовать).
                if (compas_angle != compas_angle_old)
                {
                    panel1_Paint(sender, null);
                    compas_angle_old = compas_angle;
                }

            }

        }
    }
}


Вот что должно получиться:


А вот исходники библиотеки и программы: CompasExample.rar
  • 1 июля 2010, 11:22
  •      
  • Roman


Комментарии (0)

RSS свернуть / развернуть

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.