Однажды я обзавёлся чудесным девайсом 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
Комментарии (0)
RSS свернуть / развернутьТолько зарегистрированные и авторизованные пользователи могут оставлять комментарии.