Статьи

31 Дней Манго | День № 4: Компас

Эта статья является четвертым днем ​​в серии под названием « 31 день манго» .

Day4-компас

Сегодня я собираюсь обсудить датчик, который находится внутри каждого устройства Windows Phone сегодня: Компас. В оригинальных телефонах у каждого устройства был компас, но у разработчиков не было API для доступа к нему. С выпуском Windows Phone 7.5 он стал дополнительным оборудованием, но с богатым API, похожим на акселерометр. 

Кроме того, если вы хотите прямо сейчас попробовать это приложение на своем устройстве, оно доступно на Windows Phone Marketplace!

DownloadIcon

Прежде чем углубиться в «как» в Compass API, давайте немного поговорим о том, что такое Compass на самом деле.

Что такое компас?

В традиционных терминах компас используется для определения направления северного магнитного полюса Земли. Когда вы слышите слово «компас», вы, вероятно, представляете одно из них:

образ

Там, безусловно, не один из них занимает место в вашем устройстве. Вместо этого мобильные телефоны предпочитают использовать нечто более точное, называемое магнитометром . В нашем случае магнитометр все еще может определять направление магнитного севера, но он также может определять вращение устройства относительно магнитного севера. Кроме того, он также способен обнаруживать магнитные поля вокруг устройства (которые часто мешают другим расчетам). Чтобы лучше продемонстрировать это, я создал короткое видео, где я использую магнит с моим устройством Windows Phone.

Значения X, Y и Z, которые вы видите на экране, фактически измеряются в микротеслах , которые являются единицей напряженности магнитного поля. Значения белого, показанные выше X, Y и Z, представляют собой магнитные и истинные значения, измеренные в градусах. MagneticHeading использует магнитный северный полюс в качестве эталона, в то время как TrueHeading использует географический северный полюс. Итак, теперь, когда мы углубились в науку о датчике Compass, давайте посмотрим, как мы создаем приложение, показанное в видео.

Использование Windows Phone Compass

Во-первых, давайте создадим пользовательский интерфейс. Если вы не смотрели видео выше, мы создадим это:

образ

После того, как вы создадите этот пример приложения, я уверен, что вы также возьмете магнит и попробуете мой маленький видео-трюк. Вот XAML, чтобы начать все в вашем интерфейсе:

<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
        <TextBlock x:Name="ApplicationTitle" Text="31 DAYS OF MANGO" Style="{StaticResource PhoneTextNormalStyle}"/>
        <TextBlock x:Name="PageTitle" Text="compass" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <TextBlock Height="30" HorizontalAlignment="Left" Margin="20,73,0,0" Text="MAGNETIC" VerticalAlignment="Top" Foreground="White" FontSize="28" FontWeight="Bold"/>
        <TextBlock Height="30" HorizontalAlignment="Right" Margin="0,74,47,0" Text="TRUE" VerticalAlignment="Top" Foreground="Gray" FontSize="28" FontWeight="Bold"/>
        <TextBlock Height="30" HorizontalAlignment="Left" Margin="20,100,0,0" Name="magneticValue" Text="1.0" VerticalAlignment="Top" Foreground="White" FontSize="28" FontWeight="Bold" Width="147" TextAlignment="Center" />
        <TextBlock Height="30" HorizontalAlignment="Right" Margin="0,100,20,0" Name="trueValue" Text="1.0" VerticalAlignment="Top" Foreground="Gray" FontSize="28" FontWeight="Bold" Width="123" TextAlignment="Center" />
        <TextBlock Height="30" HorizontalAlignment="Left" Margin="20,140,0,0" Name="xBlock" Text="X: 1.0" VerticalAlignment="Top" Foreground="Red" FontSize="28" FontWeight="Bold"/>
        <TextBlock Height="30" HorizontalAlignment="Center" Margin="0,140,0,0" Name="yBlock" Text="Y: 1.0" VerticalAlignment="Top" Foreground="Green" FontSize="28" FontWeight="Bold"/>
        <TextBlock Height="30" HorizontalAlignment="Right" Margin="0,140,20,0" Name="zBlock" Text="Z: 1.0" VerticalAlignment="Top" Foreground="Blue" FontSize="28" FontWeight="Bold"/>
        <Line x:Name="magneticLine" X1="240" Y1="350" X2="240" Y2="270" Stroke="White" StrokeThickness="4"></Line>
    </Grid>
</Grid> 

 

Вы можете видеть, что у нас действительно есть просто несколько значений TextBlock и одна строка. Эта линия будет использоваться как традиционный компас, указывающий на север. Тем не менее, он на самом деле указывает на самое сильное магнитное поле, поэтому он движется так сильно, когда мы ввели магнит.

Большая часть наших усилий будет сосредоточена на нашем файле с выделенным кодом, в том числе на некоторой немного продвинутой математике, чтобы наша линия двигалась соответствующим образом. Давайте начнем с инициализации и обнаружения нашего компаса на устройстве.

using System;
using Microsoft.Phone.Controls;
using Microsoft.Devices.Sensors;
using Microsoft.Xna.Framework;

namespace Day4_Compass
{
    public partial class MainPage : PhoneApplicationPage
    {
        Compass compass;
        
        public MainPage()
        {
            InitializeComponent();

            if (Compass.IsSupported)
            {
                //DO SOMETHING
            }
        }
    }
}
 

Вы можете видеть, что нам пришлось добавить ссылку на сборку Microsoft.Devices.Sensors (мы также добавили ссылку на Microsoft.Xna.Framework, но это будет позже). Это дает нам возможность связываться с датчиком компаса, а также проверять, поддерживает ли устройство пользователя компас. Я оставил комментарий «Делать что-то», где мы инициализируем наш компас и создадим обработчик событий для обработки данных, которые он генерирует.

using System;
using Microsoft.Phone.Controls;
using Microsoft.Devices.Sensors;
using Microsoft.Xna.Framework;

namespace Day4_Compass
{
    public partial class MainPage : PhoneApplicationPage
    {
        Compass compass;
        
        public MainPage()
        {
            InitializeComponent();

            if (Compass.IsSupported)
            {
                compass = new Compass();
                compass.TimeBetweenUpdates = TimeSpan.FromMilliseconds(1);
                compass.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<CompassReading>>(compass_CurrentValueChanged);
                compass.Start();
            }
        }

        void compass_CurrentValueChanged(object sender, SensorReadingEventArgs<CompassReading> e)
        {
            //MAKE THIS THREAD-SAFE
        }
    }
} 

В приведенном выше примере кода мы добавили свойство TimeBetweenUpdates, которое ограничивает частоту получения новых данных от датчика. Я установил его так, чтобы он обновлял меня как можно чаще, но это тоже вопрос батареи. Я не рекомендую читать с любого из датчиков чаще, чем нужно. Более медленные показания также дадут вам возможность плавного движения иглы. Вы должны заметить, что в моем видео ранее стрелка компаса прыгает очень хаотично. Это результат моего частого TimeBetweenUpdates.

Я также создал обработчик события CurrentValueChanged, который будет давать нам новые значения компаса при каждом обнаружении изменения. Для чего-то столь же чувствительного, как Compass (или предмет завтрашнего дня, Gyroscope), вы, вероятно, получите постоянную подачу данных, так что обрабатывайте это так, чтобы это имело смысл для вашего приложения.

Наконец, мы вызываем метод Start (), который говорит Compass начать обращать внимание на данные. Затем эти данные передаются в наш метод-обработчик compass_CurrentValueChanged как объект CompassReading. Для того, чтобы прочитать эти данные, нам на самом деле нужно выйти из основного потока пользовательского интерфейса процессора. Если мы попытаемся прочитать эти данные непосредственно в этом методе обработчика событий, мы получим исключение UnauthorizedAccessException, которое предотвращает недопустимый межпоточный доступ. Таким образом, там, где живет наш комментарий «MAKE THREAD SAFE», мы собираемся передать наш объект CompassReading методу в отдельном потоке, который дает нам наш окончательный код ниже:

using System;
using Microsoft.Phone.Controls;
using Microsoft.Devices.Sensors;
using Microsoft.Xna.Framework;

namespace Day4_Compass
{
    public partial class MainPage : PhoneApplicationPage
    {
        Compass compass;
        
        public MainPage()
        {
            InitializeComponent();

            if (Compass.IsSupported)
            {
                compass = new Compass();
                compass.TimeBetweenUpdates = TimeSpan.FromMilliseconds(1);
                compass.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<CompassReading>>(compass_CurrentValueChanged);
                compass.Start();
            }
        }

        void compass_CurrentValueChanged(object sender, SensorReadingEventArgs<CompassReading> e)
        {
            Dispatcher.BeginInvoke(() => UpdateUI(e.SensorReading));
        }

        private void UpdateUI(CompassReading compassReading)
        {
            magneticValue.Text = compassReading.MagneticHeading.ToString("0.00");
            trueValue.Text = compassReading.TrueHeading.ToString("0.00");

            magneticLine.X2 = magneticLine.X1 - (200 * Math.Sin(MathHelper.ToRadians((float)compassReading.MagneticHeading)));
            magneticLine.Y2 = magneticLine.Y1 - (200 * Math.Cos(MathHelper.ToRadians((float)compassReading.MagneticHeading)));

            xBlock.Text = "X: " + compassReading.MagnetometerReading.X.ToString("0.00");
            yBlock.Text = "Y: " + compassReading.MagnetometerReading.Y.ToString("0.00");
            zBlock.Text = "Z: " + compassReading.MagnetometerReading.Z.ToString("0.00");
        }
    }
} 

Использование метода Dispatcher.BeginInvoke позволяет нам передавать наши данные в отдельный поток, где мы можем работать с данными. В нашем методе UpdateUI мы можем начать работу по обновлению нашего пользовательского интерфейса с информацией из нашего компаса.

Сначала мы собираем наши MagneticHeading и TrueHeading, и назначаем их нашим TextBlocks. В нижней части метода UpdateUI мы также присваиваем необработанные значения X, Y и Z нашим элементам пользовательского интерфейса. Однако для определения направления нашей линии нам понадобится некоторая сложная математика, и мы фактически извлекаем класс MathHelper из сборки Microsoft.Xna.Framework.

То, что я делаю в этих двух значениях для magnetLine, определяет координаты X и Y конца элемента управления Line, который мы используем. Поскольку он всегда привязан к центру экрана, мы рассчитываем нашу математику на основе этой позиции. Вот почему я начинаю со значений MagneticLine.X1 и magnetLine.Y1. Чтобы объяснить остальную часть математики, значение «200» на самом деле является длиной строки. Линия всегда 200 пикселей в длину. В остальной части расчета используется преобразование радиан из значения MagneticHeading, чтобы определить, где на кривой (с использованием функций Sin и Cos) должен находиться конец линии.

Используя приведенный выше XAML и C #, через который мы только что прошли, вы можете легко создать убедительное устройство чтения данных Compass.

Резюме

Компас является мощным датчиком в наших телефонах Windows, который дает нам возможность определять, в каком направлении направляет устройство, в дополнение к измерению магнитных полей вокруг устройства. В течение следующих нескольких дней мы рассмотрим другой датчик, гироскоп, а затем рассмотрим новый класс Motion, который фактически объединяет все наши датчики в один API, который дает нам гораздо более богатый взгляд на ориентацию устройство пользователя.

Если вы хотите загрузить рабочее решение, которое обращается к датчику компаса Windows Phone, нажмите кнопку «Загрузить код» ниже.

скачать

Завтра я напишу о другом классном датчике: гироскопе. Это удивительный инструмент для понимания ориентации устройства вашего пользователя и одна из тех тем, на которые вам обязательно стоит потратить некоторое время. Увидимся позже!

toolsbutton

Источник: 
http://www.jeffblankenburg.com/2011/11/04/31-days-of-mango-day-4-compass