title: OrientationSensor / Inclinometer weird, undocumented sensor reading jump when facing horizon
body: <p>I'm trying to upgrade an app of mine to use Windows 10 Mobile device sensors as a VR device for the pc (like Google Cardboard).
  I'm experiencing a problem with the sensor readouts when the device changes from pointing below the horizon to above the horizon (happens for both landscape and portrait, however in this case only landscape is important). A small sketch:
  <a href="https://i.stack.imgur.com/V2MUg.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/V2MUg.png" alt="Switch over point causing major sensor reading change"></a></p>
  
  <p>Raw sensor readouts (pointing downward):</p>
  
  <p>Inclinometer Pitch: -000.677 , Roll: -055.380 , Yaw: +013.978</p>
  
  <p>Now after changing to pointing upward:</p>
  
  <p>Inclinometer Pitch: -178.550 , Roll: +083.841 , Yaw: +206.219</p>
  
  <p>As you can see, all 3 values changed, by a significant amount. In reality only one axis should have changed, roll or pitch (depending on sensor orientation)</p>
  
  <p>I'm 95% sure, this problem didn't exist in Windows Phone 8. I'm unable to find any documentation about this weird behaviour of the sensors and it's stopping me from creating Augmented Reality and Virtual Reality apps.</p>
  
  <p>Here are 2 pictures of the problem:
  <a href="https://i.stack.imgur.com/Sv9ah.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/Sv9ah.jpg" alt="Pointing downward"></a>
  <a href="https://i.stack.imgur.com/45HGG.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/45HGG.jpg" alt="Pointing upward"></a></p>
  
  <p>Here is the code for this demonstration:</p>
  
  <pre class="lang-xml prettyprint-override"><code>&lt;Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"&gt;
      &lt;TextBlock Style="{StaticResource BodyTextBlockStyle}"
                  x:Name="output"
                  FontFamily="Consolas"
                  Foreground="Black"
                  Text="test"/&gt;
  
  &lt;/Grid&gt;
  </code></pre>
  
  <p>Code behind:</p>
  
  <pre class="lang-cs prettyprint-override"><code>public MainPage()
  {
      this.InitializeComponent();
      timer = new DispatcherTimer();
      timer.Interval = TimeSpan.FromMilliseconds(250);
      timer.Tick += Timer_Tick;
  }
  
  private void Timer_Tick(object sender, object e)
  {
      output.Text = "";
      output.Text = DateTime.Now.ToString("HH:mm:ss.fff") + Environment.NewLine;
      Print();
  }
  
  DispatcherTimer timer;
  public void WriteValue(String desc, String val)
  {
      StringBuilder b = new StringBuilder();
      int length = desc.Length + val.Length;
      int topad = 40 - length;
      if (topad &lt; 0)
          topad = length - 40;
      output.Text += desc + val.PadLeft(topad + val.Length) + Environment.NewLine;
  }
  public String ValueToString(double value)
  {
      String ret = value.ToString("000.00000");
      if (value &gt; 0)
          ret = " +" + ret;
      else if (value == 0)
          ret = "  " + ret;
      else
          ret = " " + ret;
  
      return ret;
  }
  public static double RadianToDegree(double radians)
  {
      return radians * (180 / Math.PI);
  }
  public void Print()
  {
      WriteValue("DisplayOrientation", LastDisplayOrient.ToString());
  
      WriteValue("Inclinometer", "");
      WriteValue("Pitch", ValueToString(LastIncline.PitchDegrees));
      WriteValue("Roll", ValueToString(LastIncline.RollDegrees));
      WriteValue("Yaw", ValueToString(LastIncline.YawDegrees));
      WriteValue("YawAccuracy", LastIncline.YawAccuracy.ToString());
  
  
      WriteValue("OrientationSensor", "");
      var q = LastOrient.Quaternion;
  
  
      double ysqr = q.Y * q.Y;
      // roll (x-axis rotation)
      double t0 = +2.0f * (q.W * q.X + q.Y * q.Z);
      double t1 = +1.0f - 2.0f * (q.X * q.X + ysqr);
      double Roll = RadianToDegree(Math.Atan2(t0, t1));
      // pitch (y-axis rotation)
      double t2 = +2.0f * (q.W * q.Y - q.Z * q.X);
      t2 = t2 &gt; 1.0f ? 1.0f : t2;
      t2 = t2 &lt; -1.0f ? -1.0f : t2;
      double Pitch = RadianToDegree(Math.Asin(t2));
      // yaw (z-axis rotation)
      double t3 = +2.0f * (q.W * q.Z + q.X * q.Y);
      double t4 = +1.0f - 2.0f * (ysqr + q.Z * q.Z);
      double Yaw = RadianToDegree(Math.Atan2(t3, t4));
      WriteValue("Roll", ValueToString(Roll));
      WriteValue("Pitch", ValueToString(Pitch));
      WriteValue("Yaw", ValueToString(Yaw));
  }
  Inclinometer sIncline;
  DisplayInformation sDisplay;
  OrientationSensor sOrient;
  protected override void OnNavigatedTo(NavigationEventArgs e)
  {
      base.OnNavigatedTo(e);
      sIncline = Inclinometer.GetDefault(SensorReadingType.Absolute);
      sDisplay = DisplayInformation.GetForCurrentView();
      sOrient = OrientationSensor.GetDefault(SensorReadingType.Absolute);
      sOrient.ReadingChanged += SOrient_ReadingChanged;
      sDisplay.OrientationChanged += SDisplay_OrientationChanged;
      sIncline.ReadingChanged += SIncline_ReadingChanged;
  
      LastDisplayOrient = sDisplay.CurrentOrientation;
      LastIncline = sIncline.GetCurrentReading();
      LastOrient = sOrient.GetCurrentReading();
      timer.Start();
  }
  
  private void SOrient_ReadingChanged(OrientationSensor sender, OrientationSensorReadingChangedEventArgs args)
  {
      LastOrient = args.Reading;
  }
  
  private void SDisplay_OrientationChanged(DisplayInformation sender, object args)
  {
      LastDisplayOrient = sDisplay.CurrentOrientation;
  }
  OrientationSensorReading LastOrient;
  InclinometerReading LastIncline;
  DisplayOrientations LastDisplayOrient;
  private void SIncline_ReadingChanged(Inclinometer sender, InclinometerReadingChangedEventArgs args)
  {
      LastIncline = args.Reading;
  }
  
  protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
  {
      base.OnNavigatingFrom(e);
  
      sIncline.ReadingChanged -= SIncline_ReadingChanged;
      sDisplay.OrientationChanged -= SDisplay_OrientationChanged;
      sOrient.ReadingChanged -= SOrient_ReadingChanged;
      timer.Stop();
  }
  </code></pre>
  
  <p><strong>edit: Added the following sketch and more description</strong></p>
  
  <p>Take a look at this following, more in depth sketch:
  <a href="https://i.stack.imgur.com/mJvTy.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/mJvTy.png" alt="More indepth sketch"></a></p>
  
  <p>Position the phone as seen in step (1). Phone in landscape mode, camera facing slightly downward, screen facing slightly upward.</p>
  
  <p>Change to step (2). You slightly tilt it forward, only one axis should change (in this case Inclinometer will show you only "Roll" changing. <strong>THIS IS CORRECT</strong>)</p>
  
  <p>Change to step (3). You now tilt your phone back. As soon as the switch over point comes, where the camera is no longer facing the ground, but now the sky and the screen is now facing slightly downward, all 3 values change by a significant amount. Pitch will jump by about -180°, Roll will jump by about 90° additionally to the amount you actually changed, and Yaw will jump by about +180°. </p>
  
  <p><strong>As long as the camera is ONLY pointing to EITHER the earth or the sky, the sensors behave fine! ONLY when switch from one to the other does this problem occur! (This scenario happens all the time with VR and AR, so this is a big problem)</strong></p>