このページは役に立ちましたか。
このページのコンテンツについての ご意見をお待ちしております
その他にご意見はありますか。
残り 1500 文字
エクスポート (0) 印刷
すべて展開
情報
要求されたトピックは次のとおりです。しかし、このトピックはこのライブラリには含まれていません。

Windows Phone 8 のコンパス センサーからデータを取得する方法

2014/06/18

対象: Windows Phone 8 および Windows Phone Silverlight 8.1 | Windows Phone OS 7.1

 

このトピックでは、コンパス データを数値およびグラフィックで表示するコンパス アプリの作成について説明します。また、このチュートリアルでは、コンパスのキャリブレーション ダイアログ ボックスを実装する方法も示します。

メモメモ:

このトピックで説明した Windows.Devices.Sensors 名前空間の API に加えて、Microsoft.Devices.Sensors 名前空間の同様のクラスを使用して電話のセンサーをプログラミングすることもできます。

このトピックは、次のセクションで構成されています。

 

Compassセンサー (磁力計) は、地球の磁北極に対するデバイスの回転角度を判断するために使用できます。アプリは生の磁力計の測定値を使用して、デバイス周囲の磁力も検出できます。コンパス センサーは、一部の Windows Phone デバイスでは必須ではありません。アプリを設計および実装する際には、この点を考慮する必要があります。アプリでは、センサーが使用できるかどうかを常に確認し、使用できない場合は代替の入力メカニズムを提供するか、適切な手順でエラーとする必要があります。

コンパス API では、デバイスの方向に基づいて、単一の軸を使用して方位を計算します。すべての軸におけるデバイスの方向を使用するアプリを作成する場合は、RotationMatrix クラスの Motion プロパティを使用する必要があります。

デバイスのコンパス センサーは、特に磁場にさらされている場合、徐々に精度が落ちることがあります。コンパスを再調整する単純なユーザー操作が存在します。方位精度が +/- 20°を超えていることがシステムによって検出されるたびに、Calibrate イベントが発生します。この例では、ユーザーがコンパスを校正するために使用できるキャリブレーション ダイアログ ボックスを実装する方法を示します。

コンパス アプリの作成方法を次の手順に示します。

重要:重要:

このアプリはエミュレーターでテストできません。エミュレーターはコンパスをサポートしません。このアプリは登録済みの電話でのみテストできます。

コンパス アプリを作成するには

  1. Visual Studio で、新しい Windows Phone アプリ  プロジェクトを作成します。このテンプレートは、Windows Phone カテゴリにあります。

  2. このアプリでは、センサー API を含む Microsoft.Devices.Sensors アセンブリへの参照が必要です。また、コンパス データの一部が XNA Framework Vector3 オブジェクトの形式で渡されるため、XNA Framework の参照も必要です。これらのアセンブリは、Windows Phone 8 を対象とするプロジェクトでは既に参照されています。

  3. MainPage.xaml ファイルで、"ContentPanel" というの名前の Grid 要素に次の XAML コードを配置します。この XAML コードでは、コンパス データを表示する UI を作成します。 TextBlock 要素は、コンパス データを数値で表示するために使用されます。Line 要素は、方位および生のコンパス データをグラフィカルに表示するために使用されます。

    <StackPanel Orientation="Vertical">
      <StackPanel Orientation="Horizontal">
        <TextBlock>status: </TextBlock>
        <TextBlock Name="statusTextBlock"></TextBlock>
      </StackPanel>
      <StackPanel Orientation="Horizontal">
        <TextBlock>time between updates:</TextBlock>
        <TextBlock Name="timeBetweenUpdatesTextBlock"></TextBlock>
      </StackPanel>
      <StackPanel Orientation="Horizontal">
        <TextBlock>magnetic heading: </TextBlock>
        <TextBlock Name="magneticTextBlock"></TextBlock>
      </StackPanel>
      <StackPanel Orientation="Horizontal">
        <TextBlock>true heading: </TextBlock>
        <TextBlock Name="trueTextBlock"></TextBlock>
      </StackPanel>
      <StackPanel Orientation="Horizontal">
        <TextBlock>heading accuracy: </TextBlock>
        <TextBlock Name="accuracyTextBlock"></TextBlock>
      </StackPanel>
      <StackPanel Orientation="Horizontal">
        <TextBlock>compass orientation mode:</TextBlock>
        <TextBlock Name="orientationTextBlock"></TextBlock>
      </StackPanel>
      <Grid Height="200" Name="headingGrid">
        <TextBlock Foreground="Yellow" FontSize="16">magnetic heading</TextBlock>
        <TextBlock Foreground="Orange" FontSize="16" Margin="0,18">true heading</TextBlock>
        <Line x:Name="magneticLine" X1="240" Y1="100" X2="240" Y2="0" Stroke="Yellow" StrokeThickness="4"></Line>
        <Line x:Name="trueLine" X1="240" Y1="100" X2="240" Y2="0" Stroke="Orange" StrokeThickness="4"></Line>
      </Grid>
      <TextBlock Text="raw magnetometer data:"></TextBlock>
      <Grid>
        <TextBlock Height="30" HorizontalAlignment="Left" Name="xTextBlock" Text="X: 1.0" VerticalAlignment="Top" Foreground="Red" FontWeight="Bold"/>
        <TextBlock Height="30" HorizontalAlignment="Center" Name="yTextBlock" Text="Y: 1.0" VerticalAlignment="Top" Foreground="Green" FontWeight="Bold"/>
        <TextBlock Height="30" HorizontalAlignment="Right"  Name="zTextBlock" Text="Z: 1.0" VerticalAlignment="Top"  Foreground="Blue" FontWeight="Bold"/>
      </Grid>
      <Grid Height="140">
        <Line x:Name="xLine" X1="240" Y1="40" X2="240" Y2="40" Stroke="Red" StrokeThickness="14"></Line>
        <Line x:Name="yLine" X1="240" Y1="70" X2="240" Y2="70" Stroke="Green" StrokeThickness="14"></Line>
        <Line x:Name="zLine" X1="240" Y1="100" X2="240" Y2="100" Stroke="Blue" StrokeThickness="14"></Line>
      </Grid>
    </StackPanel>
    
    
    

    これは UI の表示方法を示しています。

    UI for the compass application
  4. 次に、前の手順で追加したコードの直後に、コンパス調整 UI を定義する XAML を追加します。この UI は、ユーザーにイメージを表示すると共に、コンパスを調整するためにデバイスを移動する方法についての説明を表示します。外側の StackPanelVisibility.Collapsed に設定され、ユーザーから非表示になっていることに注目してください。この UI を表示するコードは後ほど示します。

    <!--Calibration UI-->
    <StackPanel Name="calibrationStackPanel" Background="Black" Opacity="1" Visibility="Collapsed">
      <Image Source="/Images/calibrate_compass.png" Opacity=".95" HorizontalAlignment="Center"/>
      <TextBlock TextWrapping="Wrap" TextAlignment="Center">The compass on your device needs to be calibrated.
      Hold the device in front of you and sweep it through a figure 8 pattern as shown
      until the calibration is complete.</TextBlock>
      <StackPanel Orientation="Horizontal" Margin="0,10" HorizontalAlignment="Center">
        <TextBlock>heading accuracy:</TextBlock>
        <TextBlock Name="calibrationTextBlock">0.0°</TextBlock>  
      </StackPanel>
      <Button Name="calibrationButton" Content="Done" Click="calibrationButton_Click"></Button>
    </StackPanel>
    <!--End Calibration UI-->
    
    

    このようにして調整 UI を表示します。

    UI for the compass calibration screen
  5. 前の手順で定義したコンパス調整の UI では、コンパスを調整するためにユーザーが実行する必要があるデバイスのスイープのパターンを示すイメージを使用しています。イメージをソリューションに追加するには、まず新しいフォルダーを作成します。新しいフォルダーを追加するには、[ソリューション エクスプローラー] のアプリ プロジェクトを右クリックし、[追加]、[新しいフォルダー] を順にクリックします。新しいフォルダーに Images という名前を付けます。[ソリューション エクスプローラー] で新しいフォルダーを右クリックし、[追加]、[既存の項目] を順にクリックします。イメージを選択し、[追加] をクリックします。イメージを calibrate_compass.png に変更します。イメージを追加したら、[ソリューション エクスプローラー] でイメージ アイコンを右クリックし、[プロパティ] をクリックします。[ビルド アクション] プロパティが [コンテンツ] に設定されていることを確認します。

    このサンプルで使用されるイメージは生センサー データのサンプルに含まれています。

  6. MainPage.xaml に追加する最後の UI コードは、コンパスからのデータの取得を開始および停止する 1 つのボタンを含むアプリ バーの定義です。XAML コードの最終行 </phone:PhoneApplicationPage> の前に、次のコードを貼り付けます。

    <phone:PhoneApplicationPage.ApplicationBar>
      <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
        <shell:ApplicationBarIconButton IconUri="/Images/onoff.png" Text="on/off" Click="ApplicationBarIconButton_Click"/>
      </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>
    
    
  7. MainPage.xaml.cs 分離コード ページを開き、センサーと XNA Framework 名前空間の using ディレクティブを、ページの先頭にある他の using ディレクティブに追加します。この例では、タイマーを使用して UI を更新します。そのため、System.Windows.Threading 名前空間もインクルードします。

    using Microsoft.Devices.Sensors;
    using Microsoft.Xna.Framework;
    using System.Windows.Threading;
    
    
  8. MainPage クラス定義の先頭でいくつかのメンバー変数を宣言します。

    public partial class MainPage : PhoneApplicationPage
    {
      Compass compass;
      DispatcherTimer timer;
    
      double magneticHeading;
      double trueHeading;
      double headingAccuracy;
      Vector3 rawMagnetometerReading;
      bool isDataValid;
    
      bool calibrating = false;
    
    

    最初の変数は、コンパス センサーからデータを取得するために使用する Compass 型のオブジェクトです。次に、UI を定期的に更新するために使用する DispatcherTimer を宣言します。 さらに、コンパス データを保持する一連の変数を宣言します。これらはコンパス API を使用して設定され、DispatcherTimer.TickDispatcherTimer イベントに表示されます。最後に、ブール変数 calibrating を使用して、キャリブレーション ダイアログ ボックスが現在表示されているかどうかを追跡します。

  9. ページのコンストラクターで、アプリが実行されているデバイスがコンパス センサーをサポートしているかどうかを確認します。すべてのデバイスがすべてのセンサーをサポートしているわけではないため、センサーを使用する前に必ず確認する必要があります。コンパスがサポートされていない場合は、ユーザーに対してメッセージが表示され、アプリ バーは非表示になります。コンパスがサポートされている場合は、DispatcherTimer が初期化され、イベント ハンドラーが割り当てられますが、この時点ではタイマーは起動しません。既存のページのコンストラクターを次のコードに置き換えます。

    timer_Tick イベント ハンドラーは後で追加します。

    // Constructor
    public MainPage()
    {
      InitializeComponent();
    
      if (!Compass.IsSupported)
      {
        // The device on which the application is running does not support
        // the compass sensor. Alert the user and hide the
        // application bar.
        statusTextBlock.Text = "device does not support compass";
        ApplicationBar.IsVisible = false;
      }
      else
      {
        // Initialize the timer and add Tick event handler, but don't start it yet.
        timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromMilliseconds(30);
        timer.Tick += new EventHandler(timer_Tick);
      }
    }
    
    
  10. アプリ バー ボタンのクリック イベントのハンドラーを追加します。このトピックで示した XAML コードの追加方法によっては、Visual Studio によってこのハンドラーが自動的に追加されている場合があります。そのような場合は、ハンドラー内のすべてのコードを削除します。ハンドラーが自動的に追加されていない場合は、次の空の関数をコピーして MainPage クラス定義に貼り付けます。

    private void ApplicationBarIconButton_Click(object sender, EventArgs e)
    {
              
    }
    
    
  11. アプリ バー ボタンのクリック ハンドラーでは、まず Compass オブジェクトが null ではなく、データを受け取っているどうかを確認します。この条件に該当する場合、ユーザーはコンパスを停止するためにボタンをクリックしています。そこで、Stop()Compass の両方について DispatcherTimer を呼び出します。空のボタンのクリック ハンドラー内に次のコードを貼り付けます。

      if (compass != null && compass.IsDataValid)
      {
        // Stop data acquisition from the compass.
        compass.Stop();
        timer.Stop();
        statusTextBlock.Text = "compass stopped.";
      }
    
    
  12. 次に、ユーザーがコンパスを開始している場合の処理を行います。Compass オブジェクトが null の場合、新しいインスタンスを作成します。目的の更新の間隔の時間を設定します。デバイスのセンサーにより、サポートされる更新間隔が異なります。この例では、センサーの実際の間隔をユーザーに表示するために、TimeBetweenUpdates プロパティを設定後に問い合わせています。次に、CurrentValueChanged イベントのイベント ハンドラーを追加します。このイベントは、コンパスが新しいデータを取得するたびに発生します。また、コンパスが調整を必要とするときに発生する Calibrate イベントのイベント ハンドラーも追加します。ボタンのクリック ハンドラー内の前のコード セクションの後に、このコードを貼り付けます。

    else ブロックの閉じ中かっこは次の手順で追加されます。

      else
      {
        if (compass == null)
        {
          // Instantiate the compass.
          compass = new Compass();
    
          // Specify the desired time between updates. The sensor accepts
          // intervals in multiples of 20 ms.
          compass.TimeBetweenUpdates = TimeSpan.FromMilliseconds(20);
    
          // The sensor may not support the requested time between updates.
          // The TimeBetweenUpdates property reflects the actual rate.
          timeBetweenUpdatesTextBlock.Text = compass.TimeBetweenUpdates.TotalMilliseconds + " ms";
    
    
          compass.CurrentValueChanged +=
              new EventHandler<SensorReadingEventArgs<CompassReading>>(compass_CurrentValueChanged);
          compass.Calibrate +=
              new EventHandler<CalibrationEventArgs>(compass_Calibrate);
        }
    
    
    
  13. 次に、Start() メソッドを使用してコンパスを開始します。Start の呼び出しは失敗する可能性があるため、この呼出は try ブロックに配置してください。catch ブロックで、コンパスを開始できなかったことをユーザーに警告することができます。このコードは、DispatcherTimer も起動します。このコードを、[Start] ボタンのクリック ハンドラー内の、前のコード セクションの後に貼り付けます。

        try
        {
          statusTextBlock.Text = "starting compass.";
          compass.Start();
          timer.Start();
        }
        catch (InvalidOperationException)
        {
          statusTextBlock.Text = "unable to start compass.";
        }
    
      }
    
    
  14. ここで、CurrentValueChanged イベント ハンドラーを実装します。このメソッドは、TimeBetweenUpdates で指定した頻度で、新しいコンパス データを使用して呼び出されます。このハンドラーは、コンパス データを含む CompassReading オブジェクトを受け取ります。このハンドラーは、UI にアクセスしないバックグラウンド スレッドで呼び出されます。したがって、このメソッドから UI を変更する場合は、Dispatcher.BeginInvoke を使用して、このコードを UI スレッドで呼び出す必要があります。この例では、ディスパッチャー タイマーを使用して UI を更新するので、このメソッドはクラス メンバー変数の値を CompassReading オブジェクトの値に設定するだけです。このアプリでは、符号ではなく、精度の大きさのみを考慮するので、Abs 関数を使用して方位精度の絶対値を取得します。

    void compass_CurrentValueChanged(object sender, SensorReadingEventArgs<CompassReading> e)
    {
      // Note that this event handler is called from a background thread
      // and therefore does not have access to the UI thread. To update 
      // the UI from this handler, use Dispatcher.BeginInvoke() as shown.
      // Dispatcher.BeginInvoke(() => { statusTextBlock.Text = "in CurrentValueChanged"; });
    
    
      isDataValid = compass.IsDataValid;
    
      trueHeading = e.SensorReading.TrueHeading;
      magneticHeading = e.SensorReading.MagneticHeading;
      headingAccuracy = Math.Abs(e.SensorReading.HeadingAccuracy);
      rawMagnetometerReading = e.SensorReading.MagnetometerReading;
                
    }
    
    
  15. 現在のコンパスの値で UI を更新するには、DispatcherTimer.Tick イベント ハンドラーを実装します。このメソッドは、コンパスが現在調整中であるかどうかによって動作が異なります。調整中でない場合、ステータス TextBlock が更新され、データが受信中であることが示されます。次に、TextBlock オブジェクトが更新され、磁方位 (磁北を基準とした方位)、真方位 (地軸の北を基準とした方位)、およびコンパスの読み取り値の誤差を示す方位精度が表示されます。 次に、Line オブジェクトが更新され、コンパス測定値がグラフィカルに示されます。Microsoft.Xna.Framework ライブラリの MathHelper クラスを使用して、測定値を度数からラジアンに変換し、三角関数で使用できるようにします。 次に、生の磁力計測定値が数値およびグラフの両方で表示されます。 次のコードを MainPage.xaml.cs に貼り付けます。このメソッドの残りの部分は、次のステップで示します。

    timer_Tick メソッドの閉じ中かっこは次の手順で追加されます。

    void timer_Tick(object sender, EventArgs e)
    {
      if (!calibrating)
      {
        if (isDataValid)
        {
          statusTextBlock.Text = "receiving data from compass.";
        }
    
        // Update the textblocks with numeric heading values
        magneticTextBlock.Text = magneticHeading.ToString("0.0");
        trueTextBlock.Text = trueHeading.ToString("0.0");
        accuracyTextBlock.Text = headingAccuracy.ToString("0.0");
    
        // Update the line objects to graphically display the headings
        double centerX = headingGrid.ActualWidth / 2.0;
        double centerY = headingGrid.ActualHeight / 2.0;
        magneticLine.X2 = centerX - centerY * Math.Sin(MathHelper.ToRadians((float)magneticHeading));
        magneticLine.Y2 = centerY - centerY * Math.Cos(MathHelper.ToRadians((float)magneticHeading));
        trueLine.X2 = centerX - centerY * Math.Sin(MathHelper.ToRadians((float)trueHeading));
        trueLine.Y2 = centerY - centerY * Math.Cos(MathHelper.ToRadians((float)trueHeading));
    
        // Update the textblocks with numeric raw magnetometer readings
        xTextBlock.Text = rawMagnetometerReading.X.ToString("0.00");
        yTextBlock.Text = rawMagnetometerReading.Y.ToString("0.00");
        zTextBlock.Text = rawMagnetometerReading.Z.ToString("0.00");
    
        // Update the line objects to graphically display raw data
        xLine.X2 = xLine.X1 + rawMagnetometerReading.X * 4;
        yLine.X2 = yLine.X1 + rawMagnetometerReading.Y * 4;
        zLine.X2 = zLine.X1 + rawMagnetometerReading.Z * 4;
      }
    
    
  16. DispatcherTimer.Tick イベント ハンドラーの 2 番目の部分では、コンパス調整用の UI を更新します。既に説明したように、この UI は既定ではユーザーに表示されません。この UI を表示するコードは、次のステップで示します。このコードは、HeadingAccuracy 値を単純に評価します。この値が 10°以下の場合は、コンパスは調整済みであると判断して、テキストの色を緑に設定し、調整が完了していることをユーザーに伝えます。それ以外の場合、テキストは赤になり、方位精度の数値が表示されます。まず、ブラシと色の使用をサポートする使用ステートメントを追加します。

    using System.Windows.Media;
    

    次に、前の手順のコードの後に次のコードを貼り付けて、DispatcherTimer.Tick イベント ハンドラーを完成させます。

      else
      {
        if (headingAccuracy <= 10)
        {
          calibrationTextBlock.Foreground = new SolidColorBrush(Colors.Green);
          calibrationTextBlock.Text = "Complete!";
        }
        else
        {
          calibrationTextBlock.Foreground = new SolidColorBrush(Colors.Red);
          calibrationTextBlock.Text = headingAccuracy.ToString("0.0");
        }
      }
    }
    
    
  17. 次に、Calibrate イベント ハンドラーを実装します。このイベントは、コンパスの方位精度が +/- 20°を超えていることがシステムによって検出された場合に発生します。このイベント ハンドラーでは、調整 UI を単純に表示し、calibrating メンバー変数を true に設定します。この UI を更新するコードが Dispatcher.Invoke を使用して呼び出されていることに注目してください。これは、このイベント ハンドラーが UI スレッドで呼び出されないからです。

    void compass_Calibrate(object sender, CalibrationEventArgs e)
    {
      Dispatcher.BeginInvoke(() => { calibrationStackPanel.Visibility = Visibility.Visible; });
      calibrating = true;
    }
    
    
  18. 最後に、調整 UI のボタン用の Click イベント ハンドラーを実装します。ユーザーは、調整が完了したときに、このボタンをタップします。このメソッドでは、調整 UI を単純に非表示にし、calibrating メンバー変数を false に設定します。

    private void calibrationButton_Click(object sender, RoutedEventArgs e)
    {
      calibrationStackPanel.Visibility = Visibility.Collapsed;
      calibrating = false;
    }
    
    

コンパス API では、電話の方向に基づいて、異なる軸を使用して方位を計算します。次のコードは、サンプル アプリを変更して、実行時にコンパスで使用されている向きを特定します。

コンパスの方向モードを特定するには

  1. まず、クラスの先頭に、Accelerometer 型のメンバー変数を他のメンバー変数と共に追加します。

    Accelerometer accelerometer;
    
    
  2. 次に、アプリケーション バー ボタンのクリック ハンドラーで、Stop() および Compass に対する Stop メソッドの呼び出しの直後に、加速度計の DispatcherTimer メソッドを呼び出します。

    
    …
    compass.Stop();
    timer.Stop();
    // Add the following line
    accelerometer.Stop();
    
    
  3. 次に、同じアプリケーション バー ボタンのクリック ハンドラーで、Accelerometer オブジェクトを初期化し、CurrentValueChanged にイベント ハンドラーをアタッチして、Start() を呼び出します。Compass および DispatcherTimer に対する Start の呼び出しの直後に、このコードを追加します。

    …
    statusTextBlock.Text = "starting compass.";
    compass.Start();
    timer.Start();
    
    // add the following lines
    accelerometer = new Accelerometer();
    accelerometer.CurrentValueChanged +=
        new EventHandler<SensorReadingEventArgs<AccelerometerReading>>(accelerometer_CurrentValueChanged);
    accelerometer.Start();
    
    
  4. 最後に、Accelerometer オブジェクトの CurrentValueChanged イベントのイベント ハンドラーを実装します。このメソッドは、加速度測定値を表す Vector3 オブジェクトを取得し、三角関数を使用してデバイスの方向を判断します。最後に、現在のコンパス モードで UI を更新します。このイベントは UI スレッドで呼び出されないので、Dispatcher.BeginInvoke を使用して UI が更新されることに注意してください。

    void accelerometer_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e)
    {
      Vector3 v = e.SensorReading.Acceleration;
    
      bool isCompassUsingNegativeZAxis = false;
    
      if (Math.Abs(v.Z) < Math.Cos(Math.PI / 4) &&
                    (v.Y < Math.Sin(7 * Math.PI / 4)))
      {
        isCompassUsingNegativeZAxis = true;
      }
    
      Dispatcher.BeginInvoke(() =>
          { orientationTextBlock.Text = (isCompassUsingNegativeZAxis) ? "portrait mode" : "flat mode"; });
    }
    
    

表示:
© 2015 Microsoft