Использование компаса в WinRT

82

Хотя акселерометр отличает верх от низа, он не дает полной информации об ориентации устройства в трехмерном пространстве. Чтобы понять, о чем я говорю, запустите программу AccelerometerAndSimpleOrientation из предыдущей статьи на мобильном устройстве. Встаньте и придайте устройству какое-нибудь необычное положение. Акселерометр сообщит направление к центру Земли. Теперь повернитесь всем телом по кругу. Планшет повернулся на 360° в пространстве, но показания акселерометра практически не изменились, потому что вертикальное направление осталось прежним относительно устройства.

Что изменяется при круговом повороте планшета вокруг вектора ускорения? Один из возможных ответов — направление на север относительно планшета. Именно поэтому так важен встроенный компас: он предоставляет недостающие возможности по определению ориентации планшета. Объединяя показания компаса и акселерометра, вы получаете полную информацию об ориентации планшета в трехмерном пространстве.

Класс Compass имеет практически такую же структуру, как Accelerometer, а класс CompassReading содержит два свойства: HeadingMagneticNorth и HeadingTrueNorth. Оба свойства обозначают угол компьютера относительно направления на север (значения в градусах). Если расположить экран параллельно поверхности Земли и направить верх экрана на север, эти углы должны быть близки к нулю. (Под «верхом» имеется в виду положительное направление оси Y на схемах, приведенных ранее.) При повороте экрана к восточному направлению углы увеличиваются.

Конечно, эти углы почти никогда точно не совпадают. Планшет оснащен магнитометром, который реагирует на магнитный север (положение которого определяется магнитным полем Земли); истинный север определяется положением оси вращения Земли. Интересно, что свойство HeadingMagneticNorth относится к типу double, a HeadingTrueNorth — к типу double с поддержкой null, из чего можно сделать вывод, что значение может быть недоступным.

Посмотрим, как работает компас. Файл XAML проекта SimpleCompass определяет две стрелки, направленные от центра экрана прямо вверх:

<Page ...>

    <Grid Background="#FF1D1D1D">
        <Canvas HorizontalAlignment="Center" VerticalAlignment="Center">
            <Path Data="M -10 0 L 10 0, 10 -300, 0 -350, -10 -300 Z" Fill="Magenta" >
                <Path.RenderTransform>
                    <RotateTransform x:Name="magNorthRotate" />
                </Path.RenderTransform>
            </Path>

            <Path Name="trueNorthPath" Fill="Blue"
                  Data="M -10 0 L 10 0, 10 -300, 0 -350, -10 -300 Z">
                <Path.RenderTransform>
                    <RotateTransform x:Name="trueNorthRotate" />
                </Path.RenderTransform>
            </Path>
        </Canvas>
    </Grid>
</Page>

Магнитному северу соответствует фиолетовый цвет, а истинному — синий. Файл фонового кода скрывает второй объект Path, если значение HeadingTrueNorth равно null:

using System;
using Windows.Devices.Sensors;
using Windows.Graphics.Display;
using Windows.UI.Core;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace WinRTTestApp
{
    public sealed partial class MainPage : Page
    {
        Compass compass = Compass.GetDefault();

        public MainPage()
        {
            this.InitializeComponent();
            DisplayProperties.AutoRotationPreferences = 
                DisplayProperties.NativeOrientation;
            Loaded += OnMainPageLoaded;
        }

        private async void OnMainPageLoaded(object sender, RoutedEventArgs e)
        {
            if (compass != null)
            {
                ShowCompassValues(compass.GetCurrentReading());
                compass.ReportInterval = compass.MinimumReportInterval;
                compass.ReadingChanged += OnCompassReadingChanged;
            }
            else
            {
                await new MessageDialog("Компас не поддерживается").ShowAsync();
            }
        }

        private async void OnCompassReadingChanged(Compass sender, 
            CompassReadingChangedEventArgs e)
        {
            await this.Dispatcher.RunAsync(
                CoreDispatcherPriority.Normal, () =>
                {
                    ShowCompassValues(e.Reading);
                });
        }

        private void ShowCompassValues(CompassReading compassReading)
        {
            if (compassReading == null)
                return;

            magNorthRotate.Angle = -compassReading.HeadingMagneticNorth;

            if (compassReading.HeadingTrueNorth.HasValue)
            {
                trueNorthPath.Visibility = Visibility.Visible;
                trueNorthRotate.Angle = -compassReading.HeadingTrueNorth.Value;
            }
            else
            {
                trueNorthPath.Visibility = Visibility.Collapsed;
            }
        }
    }
}

Углам поворота двух стрелок задаются свойства HeadingMagneticNorth и HeadingTrueNorth с противоположным знаком. Эти значения обозначают угол поворота компьютера относительно северного направления, поэтому стрелки должны быть повернуты в противоположном направлении, обозначая направление на север относительно компьютера.

На обоих планшетах, использованных мной во время работы (включая Microsoft Surface), результаты не впечатляли. Значение HeadingTrueNorth в обоих случаях было равно null. На Microsoft Surface направление на магнитный север было неточным, а на планшете Samsung значения просто изменялись в диапазоне от 0 до 180°! На планшете моего друга значение HeadingMagneticNorth всегда равно 0.

Теоретически истинный север можно вычислить по магнитному, зная местонахождение компьютера, но включение функциональности Location в файле Package.appxmanifest не помогает.

Пройди тесты
Лучший чат для C# программистов