Я начал проект, пытаясь использовать магнитометры для определения поведения пользователя, поэтому, чтобы начать понимать, как их использовать, я построил простой компас.

Шаг 1 - Исходные данные магнитометра:

С помощью приложения PowerSense для iPhone я записал показания 3-осевого магнитометра при различных направлениях компаса и ориентации устройства. Если устройство ограничено горизонтальной плоскостью, получены следующие (очень многообещающие) показания:

Помимо того, что ось X перевернута, эти точки очень удобны для определения направления по компасу! Преобразование в сферические координаты используется, чтобы сделать вывод более удобным для использования:

Поскольку величина вектора магнитного поля в трехмерном пространстве постоянна при изменении ориентации устройства, значение r в этом приложении можно игнорировать. В горизонтальной плоскости значение rho также является постоянным из-за постоянных показаний по оси Z, поэтому на данный момент оно также игнорируется. Тета рассчитывается с использованием:

def direction(vec): 
    #vec = [x,y,z] from imported data
    x = -vec[0] #negate x
    y = vec[1]
    z = vec[2]
    if y > 0 and x > 0:        #if in Q1
        theta = math.atan(x/y)
    elif y < 0:                #if in Q2 or Q3
        theta = math.atan(x/y) + math.pi
    else:                      #if in Q4
        theta = math.atan(x/y) + math.pi*2
    return theta

Это дает нам заголовок, соответствующий углам, под которыми были записаны данные! Теперь все, что нужно, - это получить данные магнитометра с мобильного устройства.

Шаг 2. Осознайте, что iPhone не передает необработанные данные магнитометра веб-приложениям…

Наверное, следовало сначала проверить эту часть. Ну что ж.

Шаг 3 - Откройте для себя чудо события ориентации устройств

deviceorientation позволяет веб-сайту собирать данные о том, как удерживается устройство, которые отображаются по 3 различным осям:

альфа - вращение вокруг оси z от 0 до 360 градусов.

beta - поворот вокруг оси x от -180 до 180 градусов.

гамма - поворот вокруг оси y, от -90 до 90 градусов.

Предостережение заключается в том, что эта информация указывается несколько иначе для устройств iOS и Android. До версии Chrome 50 устройства Android сообщали альфа как «абсолютное» значение, постоянное в системе отсчета земной поверхности. Это означает, что альфа = 0 соответствует устройству, направленному на север, что действительно удобно. Как видно из данных магнитометра, направление вращения обратное. Это можно решить, взяв heading = 360 - alpha

function deviceOrientationListener(event) {
  var alpha    = event.alpha; //z axis rotation [0,360)
  var beta     = event.beta; //x axis rotation [-180, 180]
  var gamma    = event.gamma; //y axis rotation [-90, 90]
  var heading  = 360 - alpha; //heading [0, 360)
}
if(window.DeviceOrientationEvent){ //Check if device is compatible
      window.addEventListener("deviceorientation", deviceOrientationListener);
    }

Однако теперь Android пошел по пути iOS, сообщая альфа как «относительное» значение, где 0 определяется как направление устройства при загрузке страницы. Это делает его менее полезным в качестве компаса…

К счастью, в глубинах страниц разработчиков Google кроется решение! Некоторые устройства по-прежнему сообщают об абсолютном альфа-значении в виде заголовка компаса через event.webkitCompassHeading. Приложение проверяет, сообщает ли устройство об этом значении, и если да, то использует его. В противном случае для отображения страницы используются относительные альфа-значения, но не на север.

...
if (typeof event.webkitCompassHeading !== "undefined") {
        alpha = event.webkitCompassHeading; //iOS non-standard
        var heading = alpha
        document.getElementById("heading").innerHTML = heading.toFixed([0]);
      }
      else {
        alert("Your device is reporting relative alpha values, so this compass won't point north! ");
        var heading = 360 - alpha; //heading [0, 360)
        document.getElementById("heading").innerHTML = heading.toFixed([0]);
      }
...
if (window.DeviceOrientationAbsoluteEvent) {
      window.addEventListener("DeviceOrientationAbsoluteEvent", deviceOrientationListener);
    } // If not, check if the device sends any orientation data
    else if(window.DeviceOrientationEvent){
      window.addEventListener("deviceorientation", deviceOrientationListener);
    } // Send an alert if the device isn't compatible
    else {
      alert("Sorry, try again on a compatible mobile device!");
    }

Весь код приведен внизу страницы.

Компас можно проверить здесь, но если ваше устройство не работает, посмотрите ниже:

<!DOCTYPE html>
<html>
  <head>
    <style>
    p {
      font-family: verdana;
      font-size: 400px;
      color: #FFFFFF;
    }
    </style>
    <title>Compass</title>
    <script>
    // Get event data
    function deviceOrientationListener(event) {
      var alpha    = event.alpha; //z axis rotation [0,360)
      var beta     = event.beta; //x axis rotation [-180, 180]
      var gamma    = event.gamma; //y axis rotation [-90, 90]
      //Check if absolute values have been sent
      if (typeof event.webkitCompassHeading !== "undefined") {
        alpha = event.webkitCompassHeading; //iOS non-standard
        var heading = alpha
        document.getElementById("heading").innerHTML = heading.toFixed([0]);
      }
      else {
        alert("Your device is reporting relative alpha values, so this compass won't point north :(");
        var heading = 360 - alpha; //heading [0, 360)
        document.getElementById("heading").innerHTML = heading.toFixed([0]);
      }
      
      // Change backgroud colour based on heading
      // Green for North and South, black otherwise
      if (heading > 359 || heading < 1) { //Allow +- 1 degree
        document.body.style.backgroundColor = "green";
        document.getElementById("heading").innerHTML = "N"; // North
      }
      else if (heading > 179 && heading < 181){ //Allow +- 1 degree
        document.body.style.backgroundColor = "green";
        document.getElementById("heading").innerHTML = "S"; // South
      } 
      else { // Otherwise, use near black
        document.body.style.backgroundColor = "#161616";
      }
    }
    
    // Check if device can provide absolute orientation data
    if (window.DeviceOrientationAbsoluteEvent) {
      window.addEventListener("DeviceOrientationAbsoluteEvent", deviceOrientationListener);
    } // If not, check if the device sends any orientation data
    else if(window.DeviceOrientationEvent){
      window.addEventListener("deviceorientation", deviceOrientationListener);
    } // Send an alert if the device isn't compatible
    else {
      alert("Sorry, try again on a compatible mobile device!");
    }
    </script>
  </head>
  <body>
    <br><br>
    <p id="heading" style="text-align:center"></p>
  </body>
</html>