Chrome 从DeviceOrientation事件API计算罗盘航向

yws3nbqq  于 2023-03-21  发布在  Go
关注(0)|答案(4)|浏览(106)

对于一个智能手机的增强现实网络应用程序,当用户将设备握在手中,屏幕处于垂直平面,屏幕顶部向上时,我试图计算指南针的方向。
我已经从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;
    };
}

然而,问题是,即使设备(谷歌Galaxy Nexus)被安装为保持静止位置,计算的罗盘航向值也会从-75跳到80。这似乎发生在谷歌Chrome BETA和FF BETA 23中。
有人看到我的方法有错误吗?或者有更可靠的方法来计算罗盘的航向?

j2datikz

j2datikz1#

根据规范**中提供的示例,确定罗经航向 * 所需的步骤如下:

  • 将返回的设备方向alphabetagamma值从度转换为弧度,即alphaRadbetaRadgammaRad
  • 使用alphaRadbetaRadgammaRad(如以下示例代码所示),按照规范中的工作示例计算旋转A(rA)和旋转B(rB)分量。
  • 计算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);

您也可以使用view a demo of the code provided above
截至编写本文件时(2014年2月17日),该文件目前适用于:

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

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

    • 确定垂直于设备屏幕并指向屏幕背面的矢量的水平分量的罗盘航向。*
nzk0hqpo

nzk0hqpo2#

我也玩过一些DeviceOrientationEvent(可能会采用你的公式...),不时会看到类似的问题。你可能想试试像这个video这样的校准。你可以在YouTube上搜索更多的例子。

7hiiyaii

7hiiyaii3#

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

wlzqhblo

wlzqhblo4#

2023年答案
由绝对α、β和γ计算罗经航向的公式如下:

compass = -(alpha + beta * gamma / 90);
compass -= Math.floor(compass / 360) * 360; // Wrap to range [0,360]

上述公式对于零beta和gamma(例如,电话躺在table上)表现良好,并且对于beta=90(例如,保持直立)也有效。
现在是2023年,iOS仍然不支持绝对DeviceOrientationEvent,Android仍然不直接支持指南针,下面的TypeScript代码展示了如何获取两种类型设备的指南针值,你可以通过移除冒号后面的类型说明符将其转换为JavaScript。

function startCompassListener(callback: (compass: number) => void) {
    if (!window["DeviceOrientationEvent"]) {
        console.warn("DeviceOrientation API not available");
        return;
    }
    let absoluteListener = (e: DeviceOrientationEvent) => {
        if (!e.absolute || e.alpha == null || e.beta == null || e.gamma == null)
            return;
        let compass = -(e.alpha + e.beta * e.gamma / 90);
        compass -= Math.floor(compass / 360) * 360; // Wrap into range [0,360].
        window.removeEventListener("deviceorientation", webkitListener);
        callback(compass);
    };
    let webkitListener = (e) => {
        let compass = e.webkitCompassHeading;
        if (compass!=null && !isNaN(compass)) {
            callback(compass);
            window.removeEventListener("deviceorientationabsolute", absoluteListener);
        }
    }

    function addListeners() {
        // Add both listeners, and if either succeeds then remove the other one.
        window.addEventListener("deviceorientationabsolute", absoluteListener);
        window.addEventListener("deviceorientation", webkitListener);
    }

    if (typeof (DeviceOrientationEvent["requestPermission"]) === "function") {
        DeviceOrientationEvent["requestPermission"]()
            .then(response => {
                if (response == "granted") {
                    addListeners();
                } else
                    console.warn("Permission for DeviceMotionEvent not granted");
            });
    } else
        addListeners();
}

测试时,我发现我的Android的绝对(α,β,γ)是非常不准确的(约60度误差α).然而,通过滚动设备周围校准自己,并得到了约5度误差.

相关问题