Существует несколько способов вычисления курса по матрице вращения, возвращаемой CMDeviceMotion.Это предполагает, что вы используете то же определение компаса Apple, где направление + y (верхняя часть iPhone), указывающее на север, возвращает курс 0, а вращение iPhone вправо увеличивает курс, поэтому восток равен 90, юг - 180и т. д.
Сначала при запуске обновлений обязательно убедитесь, что заголовки доступны:
if (([CMMotionManager availableAttitudeReferenceFrames] & CMAttitudeReferenceFrameXTrueNorthZVertical) != 0) {
...
}
Далее, когда вы запустите диспетчер движений, спросите об отношениикак вращение от X, указывающего истинный север (или магнитный север, если вам это нужно по какой-то причине):
[motionManager startDeviceMotionUpdatesUsingReferenceFrame: CMAttitudeReferenceFrameXTrueNorthZVertical
toQueue: self.motionQueue
withHandler: dmHandler];
Когда диспетчер движения сообщает об обновлении движения, вы хотите узнать, сколько устройство повернуло.в плоскости XY.Поскольку нас интересует верхняя часть iPhone, мы выберем точку в этом направлении и повернем ее, используя возвращенную матрицу вращения, чтобы получить точку после поворота:
[m11 m12 m13] [0] [m12]
[m21 m22 m23] [1] = [m22]
[m31 m32 m33] [0] [m32]
Причудливые скобки - это матрицы;это лучшее, что я могу сделать, используя ASCII.:)
Курс - это угол между повернутой точкой и истинным севером.Мы можем использовать координаты X и Y повернутой точки, чтобы извлечь арктангенс, который дает угол между точкой и осью X.Это на самом деле 180 градусов от того, что мы хотим, поэтому мы должны соответственно отрегулировать.Результирующий код выглядит следующим образом:
CMDeviceMotionHandler dmHandler = ^(CMDeviceMotion *aMotion, NSError *error) {
// Check for an error.
if (error) {
// Add error handling here.
} else {
// Get the rotation matrix.
CMAttitude *attitude = self.motionManager.deviceMotion.attitude;
CMRotationMatrix rm = attitude.rotationMatrix;
// Get the heading.
double heading = PI + atan2(rm.m22, rm.m12);
heading = heading*180/PI;
printf("Heading: %5.0f\n", heading);
}
};
Есть одна ошибка: если верх iPhone направлен прямо вверх или вниз, направление не определено.В результате m21 и m22 равны нулю или очень близки к нему.Вам нужно решить, что это значит для вашего приложения, и соответственно обработать условие.Например, вы можете переключиться на заголовок на основе оси -Z (за iPhone), когда m12 * m12 + m22 * m22 близка к нулю.
Все это предполагает, что вы хотите повернутьо плоскости XY, как Apple обычно делает для своего компаса.Это работает, потому что вы используете матрицу вращения, возвращаемую менеджером движения, чтобы повернуть вектор, направленный вдоль оси Y, то есть эту матрицу:
[0]
[1]
[0]
Чтобы повернуть другой вектор, скажем, один направленный-Z - использовать другую матрицу, например
[0]
[0]
[-1]
. Конечно, вам также нужно взять арктангенс в другой плоскости, поэтому вместо
double heading = PI + atan2(rm.m22, rm.m12);
вы бы использовали
double heading = PI + atan2(-rm.m33, -rm.m13);
, чтобы получить вращение в плоскости XZ.