Я начал проект, пытаясь использовать магнитометры для определения поведения пользователя, поэтому, чтобы начать понимать, как их использовать, я построил простой компас.
Шаг 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>