Georg fährt extra nach Berlin um Steve Ballmer zu treffen

31 Tage Mango | Tag #4: Der Kompass

Die­ser Arti­kel ist Tag #4 der Serie 31 Tage Man­go von Jeff Blan­ken­burg.

Der Ori­gi­nal­ar­ti­kel befin­det sich hier: Day #4: Com­pass.

Heu­te möch­te ich einen Sen­sor dis­ku­tie­ren, der in jedem der­zei­ti­gen Win­dows Pho­ne Gerät vor­han­den ist: den Kom­pass. Der Kom­pass war auch schon in den Gerä­ten der ers­ten Genera­ti­on ein­ge­baut — als App Ent­wick­ler hat­te man nur kei­nen API Zugriff dar­auf. Mit dem Win­dows Pho­ne 7.5 Update ist der Kom­pass ein optio­na­ler Bestand­teil der Hard­ware gewor­den. Dafür hat er eine reich­hal­ti­ge API bekom­men, die der API für den Acce­le­ro­me­ter ähnelt.

Wenn Sie die Anwen­dung die­ses Arti­kels auf Ihrem Gerät aus­pro­bie­ren möch­ten, kön­nen Sie die­se vom Win­dows Pho­ne Mar­ket­place her­un­ter­la­den!

Bevor ich dazu kom­me, wie die Kom­pass API ver­wen­det wer­den kann, las­sen Sie uns kurz dar­über reden, was ein Kom­pass eigent­lich ist.

Was ist ein Kompass?

Ein Kom­pass dient tra­di­tio­nell dazu, die Rich­tung des magne­ti­schen Nord­pols der Erde zu bestim­men. Wenn man das Wort Kom­pass hört, denkt man übli­cher­wei­se an so ein Gerät:

Ein Han­dy ent­hält natür­lich nicht so ein Gerät. Mobil­te­le­fo­ne ver­wen­den etwas, das als Magne­to­me­ter bezeich­net wird. Ein Magne­to­me­ter kann eben­falls die Rich­tung des magne­ti­schen Nord­pols bestim­men. Zudem kann er aber die Rota­ti­on des Geräts rela­tiv zum magne­ti­schen Nord­pol ermit­teln. Wei­ter­hin ist er in der Lage, magne­ti­sche Fel­der rund um das Gerät zu erken­nen (die oft die ande­ren Berech­nun­gen stö­ren kön­nen). Um die­se Effek­te vor­zu­füh­ren, habe ich ein klei­nes Video erstellt, in wel­chem ich einen Magne­ten zusam­men mit mei­nem Win­dows Pho­ne ver­wen­de.

Die X, Y, und Z Wer­te, die Sie auf dem Bild­schirm sehen, wer­den gemes­sen in Mikro­tes­la. Die­se Ein­heit gibt die Stär­ke eines Magnet­fel­des an. Die bei­den wei­ßen Wer­te ober­halb der X, Y und Z Wer­te sind die magne­ti­sche und die tat­säch­li­che Pei­lung — jeweils gemes­sen in Grad. Magne­ti­cHea­ding benutzt den magne­ti­schen Nord­pol als Refe­renz­punkt wäh­rend TrueHea­ding den geo­gra­phi­schen Nord­pol ver­wen­det. Jetzt, wo wir die Hin­ter­grün­de zum Kom­pass Sen­sor ken­nen, las­sen Sie uns die Anwen­dung aus dem Video rea­li­sie­ren.

Den Windows Phone Kompass benutzen

Wir begin­nen mit der Benut­zer­ober­flä­che. Falls Sie das Video nicht ange­schaut haben, so soll die Benut­zer­ober­flä­che aus­se­hen:

Wenn Sie die Anwen­dung fer­tig gestellt haben, kön­nen Sie den Trick mit dem Magne­ten aus dem Video selbst aus­pro­bie­ren. Hier ist das XAML für die Benut­zer­schnitt­stel­le:

<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> 

Wie Sie sehen, besteht die Benut­zer­ober­flä­che im Wesent­li­chen aus einer Rei­he von Text­Block Ele­men­ten und einer Line. Die Line wer­den wir ver­wen­den, um das Ver­hal­ten eines tra­di­tio­nel­len Kom­pas­ses zu imi­tie­ren. Sie soll immer Rich­tung Nor­den zei­gen. In Wirk­lich­keit zeigt sie immer in Rich­tung des stärks­ten Magnet­fel­des. Des­halb hat sie sich so stark ablen­ken las­sen als wir den Magne­ten in die Nähe des Tele­fons gehal­ten haben.

Die meis­te Arbeit fin­det in der Code-behind Datei statt — inklu­si­ve leicht fort­ge­schrit­te­ner Mathe­ma­tik, um die Linie rich­tig zu bewe­gen. Begin­nen wir mit der Initia­li­sie­rung und Erken­nung des Kom­pas­ses auf unse­rem Gerät.

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
         }
      }
   }
} 

Wie Sie sehen, muss­ten wir eine Refe­renz auf das Microsoft.Devices.Sensors Assem­bly hin­zu­fü­gen (wir haben zudem eine Refe­renz zum Microsoft.Xna.Framework Assem­bly hin­zu­ge­fügt, aber dazu spä­ter). Damit sind wir in der Lage, mit dem Kom­pass Sen­sor zu inter­agie­ren und zu über­prü­fen, ob das Gerät des Anwen­ders über­haupt einen Kom­pass unter­stützt. Dort wo ich den „DO SOMETHING“ Kom­men­tar hin­ter­las­sen habe, wer­den wir den Kom­pass initia­li­sie­ren und einen Event Hand­ler erstel­len, der die Daten des Kom­pas­ses ver­ar­bei­ten wird.

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
      }
   }
} 

Im obi­gen Bei­spiel haben wir die Time­Bet­ween­Up­dates Eigen­schaft mit einem Wert belegt um zu begren­zen, wie oft wir neue Daten vom Sen­sor bekom­men. Hier habe ich die­sen Wert so gesetzt, dass die Anwen­dung so vie­le Aktua­li­sie­run­gen bekommt wie mög­lich. Da die Aktua­li­se­rungs­häu­fig­keit Ein­fluss auf die Akku­lauf­zeit hat, soll­ten Sie nicht öfter aktua­li­sie­ren als es für Ihre Anwen­dung erfor­der­lich ist. Sel­te­ne­re Aktua­li­sie­run­gen erlau­ben zudem eine flüs­si­ge­re Bewe­gung der „Kom­pass­na­del“. Im Video oben wer­den Sie fest­ge­stellt haben, dass die Kom­pass­na­del ziem­lich wild her­um­springt. Das ist eine direk­te Fol­ge der gerin­gen Time­Bet­ween­Up­dates.

Wei­ter­hin habe ich einen Cur­rentVa­lue­Ch­an­ged Event Hand­ler erstellt, wel­cher uns die neu­en Wer­te des Kom­pas­ses lie­fern wird wenn die­se sich geän­dert haben. Bei einem emp­find­li­chen Sen­sor wie dem Kom­pass (oder dem Gyro­skop, wel­ches wir mor­gen behan­deln wer­den) ist es sehr wahr­schein­lich, dass Sie einen kon­stan­ten Strom an Daten erhal­ten wer­den. Die Ver­ar­bei­tung die­ses kon­stan­ten Stroms soll­te natür­lich für Ihre Anwen­dung sinn­voll sein (Anm. leit­ning: Bei­spiels­wei­se soll­ten Sie nicht bei jeder Ände­rung der Daten kom­pli­zier­te und lang dau­ern­de Berech­nun­gen durch­füh­ren).

Zu guter Letzt rufen wir die Metho­de Start(), wel­che dem Kom­pass mit­teilt, dass er jetzt Daten schi­cken soll. Die­se Daten wer­den von unse­rer Event Hand­ler Metho­de compass_CurrentValueChanged in Form eines Com­pass­Rea­ding Objekts emp­fan­gen. Der Event Hand­ler wird auf einem sepa­ra­ten Thread auf­ge­ru­fen. Wenn wir also ver­su­chen wür­den, die Benut­zer­ober­flä­che direkt in die­sem Event Hand­ler zu aktua­li­sie­ren, wür­den wir eine Unaut­ho­ri­ze­dAc­ces­sEx­cep­ti­on wegen „inva­lid cross-thread access“ bekom­men. An der Stel­le, wo oben der Kom­men­tar „MAKE THIS THREAD SAFE“ steht, wer­den wir das Com­pass­Rea­ding Objekt an den UI Thread über­ge­ben. Damit ergibt sich der unten ste­hen­de Code:

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");
      }
   }
} 

Die Metho­de Dispatcher.BeginInvoke erlaubt uns, Daten an den UI Thread zu über­ge­ben. Inner­halb der Metho­de UpdateUI kön­nen wir also gefahr­los die Benut­zer­ober­flä­che mit den neu­en Wer­ten des Kom­pas­ses aktua­li­sie­ren.

Zunächst wei­sen wir den ent­spre­chen­den Text­Blocks die Wer­te Magne­ti­cHea­ding und TrueHea­ding zu. Ganz unten in der Metho­de wei­sen wir zudem die X, Y und Z Wer­te den ent­spre­chen­den Ele­men­ten der Benut­zer­ober­flä­che zu. Um die Rich­tung der Linie zu ermit­teln, benö­ti­gen wir leicht fort­ge­schrit­te­ne Mathe­ma­tik. Hier­zu bedie­nen wir uns der Math­Hel­per Klas­se aus dem Microsoft.Xna.Framework Assem­bly.

Bei den bei­den Wer­ten von magne­ti­cLi­ne bestim­me ich zunächst die X und die Y Koor­di­na­te des End­punk­tes der Linie. Da der Start­punkt der Linie immer die Mit­te des Bild­chirms ist, kön­nen wir unse­re Berech­nung aus­ge­hend von die­sem Punkt machen. Ich begin­ne des­halb mit den Wer­ten magneticLine.X1 und magneticLine.Y1. Der Wert „200“ ist die Län­ge der Linie. Die Linie ist in jeder Posi­ti­on genau 200 Pixel lang. Der Rest der Berech­nung nutzt die Radi­ans des Magne­ti­cHea­ding Wer­tes um zu bestim­men, wo auf der Kur­ve der End­punkt der Linie liegt. Dafür nut­ze ich die Sin und Cos Funk­tio­nen.

Mit dem XAML von oben und dem C# Code, den wir gera­de behan­delt haben, kön­nen Sie leicht eine ein­drucks­vol­le Kom­pass App ent­wi­ckeln.

Zusammenfassung

Der Kom­pass ist ein mäch­ti­ger Sen­sor in unse­ren Win­dows Pho­nes, mit des­sen Hil­fe wir die aktu­el­le Rich­tung des Geräts ermit­teln kön­nen und mit dem wir Magnet­fel­der in der Umge­bung mes­sen kön­nen. In den nächs­ten Tagen wer­den wir uns mit dem Gyro­skop einen wei­te­ren Sen­sor anschau­en und zum Schluss die Moti­on Klas­se betrach­ten, die alle Sen­so­ren in einer API ver­eint. Dadurch erhal­ten wir noch prä­zi­se­re Infor­ma­tio­nen über die Ori­en­tie­rung des Geräts.

Wenn Sie eine lauf­fä­hi­ge Solu­ti­on zur Arbeit mit dem Win­dows Pho­ne Kom­pass her­un­ter­la­den möch­ten, kli­cken Sie auf das Down­load Code Sym­bol.

Mor­gen wer­de ich über einen wei­te­ren coo­len Sen­sor schrei­ben: das Gyro­skop. Damit kön­nen Sie die Ori­en­tie­rung des Geräts fest­stel­len. Das Gyro­skop ist eines der The­men, mit denen Sie auf jeden Fall her­um­ex­pe­ri­men­tie­ren soll­ten. Bis dahin!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert