5

对于智能手机的增强现实网络应用程序,我试图在用户手持设备时计算指南针航向,屏幕处于垂直平面且屏幕顶部朝上。

我从http://dev.w3.org/geo/api/spec-source-orientation中获取了建议的公式(参见工作示例)并实现了以下功能:

function compassHeading(alpha, beta, gamma) {
    var a1, a2, b1, b2;
    if ( beta !== 0 || gamma !== 0 ) {
        a1 = -Math.cos(alpha) * Math.sin(gamma);
        a2 = Math.sin(alpha) * Math.sin(beta) * Math.cos(gamma);
        b1 = -Math.sin(alpha) * Math.sin(gamma);
        b2 = Math.cos(alpha) * Math.sin(beta) * Math.cos(gamma);
        return Math.atan((a1 - a2) / (b1 + b2)).toDeg();
    }
    else {
        return 0;
    }
}

而 .toDeg() 是一个数字对象扩展礼貌http://www.movable-type.co.uk/scripts/latlong.html

/** Converts radians to numeric (signed) degrees */
if (typeof Number.prototype.toDeg == 'undefined') {
    Number.prototype.toDeg = function() {
        return this * 180 / Math.PI;
    };
}  

然而,问题在于,即使设备(Google Galaxy Nexus)安装在静态位置,计算出的罗盘航向值也会从大约 -75 跳到 80。这似乎发生在 Google Chrome BETA 和 FF BETA 23 中。

有人在我的方法中看到错误或知道计算罗盘航向的更可靠方法吗?

4

3 回答 3

15

根据规范*中提供的工作示例确定指南针航向所需的步骤如下:

  • 将返回的DeviceOrientationalpha和值从度数转换为弧度为, beta, 。gammaalphaRadbetaRadgammaRad
  • 使用,和(如下面的示例代码所示)根据规范中的工作示例计算 rotationA ( rA) 和 rotationB ( rB) 分量。alphaRadbetaRadgammaRad
  • 计算compassHeading = Math.atan(rA / rB)
  • 将返回的半单位圆标题转换为 [0-360) 度范围内的整个单位圆标题。
  • 从弧度转换compassHeading回度数(可选)。

以下是用 JavaScript 实现的规范中的工作示例:

function compassHeading(alpha, beta, gamma) {

  // Convert degrees to radians
  var alphaRad = alpha * (Math.PI / 180);
  var betaRad = beta * (Math.PI / 180);
  var gammaRad = gamma * (Math.PI / 180);

  // Calculate equation components
  var cA = Math.cos(alphaRad);
  var sA = Math.sin(alphaRad);
  var cB = Math.cos(betaRad);
  var sB = Math.sin(betaRad);
  var cG = Math.cos(gammaRad);
  var sG = Math.sin(gammaRad);

  // Calculate A, B, C rotation components
  var rA = - cA * sG - sA * sB * cG;
  var rB = - sA * sG + cA * sB * cG;
  var rC = - cB * cG;

  // Calculate compass heading
  var compassHeading = Math.atan(rA / rB);

  // Convert from half unit circle to whole unit circle
  if(rB < 0) {
    compassHeading += Math.PI;
  }else if(rA < 0) {
    compassHeading += 2 * Math.PI;
  }

  // Convert radians to degrees
  compassHeading *= 180 / Math.PI;

  return compassHeading;

}

window.addEventListener('deviceorientation', function(evt) {

  var heading = null;

  if(evt.absolute === true && evt.alpha !== null) {
    heading = compassHeading(evt.alpha, evt.beta, evt.gamma);
  }

  // Do something with 'heading'...

}, false);

您还可以查看上面提供的代码的演示

截至撰写本文时(2014 年 2 月 17 日),目前适用于:

  • 适用于 Android 的谷歌浏览器
  • Android 版 Opera 移动版
  • 适用于 Android 的Firefox测试版

其他浏览器尚不符合 DeviceOrientation 事件规范中描述的 DeviceOrientation 校准和/或不提供absoluteDeviceOrientation 数据值,因此无法compassHeading使用不完整的数据进行确定。

*确定与设备屏幕正交并指向屏幕背面的向量的水平分量的罗盘航向。

于 2014-02-17T13:01:29.153 回答
0

我也玩过一些DeviceOrientationEvent(可能会采用你的公式......)并且不时看到类似的问题。您可能想尝试在此视频中进行校准。您可以在 youtube 上搜索更多示例。

于 2013-10-07T10:50:48.470 回答
0

可能出于隐私原因,从设备方向事件中获取指南针方向似乎已被弃用。您应该查看需要权限的Geolocation API 。这些事件有Coordinates.heading

于 2018-08-27T13:26:19.203 回答