Ответ 1
Я знаю, что я играю в никеры, но я много работал над этим материалом в последнее время, поэтому я подумал, что брошу свои 2 ¢.
Устройство не содержит компасов или инклинометров, поэтому оно не измеряет азимут, шаг или рулон напрямую. (Мы называем эти углы Эйлера, BTW). Вместо этого он использует акселерометры и магнитометры, оба из которых производят 3-пространственные векторы XYZ. Они используются для вычисления значений азимута и т.д.
Векторы находятся в координатном пространстве устройства:
Мировые координаты Y обращены на север, X - на восток, а Z - вверх:
Таким образом, нейтральная ориентация устройства лежит на спине на столе, а верхняя часть устройства обращена на север.
Акселерометр создает вектор в направлении "UP". Магнитометр создает вектор в "северном" направлении. (Обратите внимание, что в северном полушарии это имеет тенденцию указывать вниз из-за магнитного падения.)
Вектор акселерометра и вектор магнитометра можно комбинировать математически через SensorManager.getRotationMatrix(), который возвращает матрицу 3x3, которая будет отображать векторы в координатах устройства в мировые координаты или наоборот. Для устройства в нейтральном положении эта функция вернет идентификационную матрицу.
Эта матрица не меняется с ориентацией экрана. Это означает, что ваше приложение должно знать ориентацию и соответственно компенсировать.
SensorManager.getOrientation() принимает матрицу преобразования и вычисляет значения азимута, тангажа и рулона. Они берутся относительно устройства в нейтральном положении.
Я не знаю, какая разница между вызовом этой функции и просто использованием датчика TYPE_ORIENTATION, за исключением того, что функция позволяет вам сначала манипулировать матрицей.
Если устройство наклонено под углом 90 ° или около него, то использование углов Эйлера разваливается. Математически это вырожденный случай. В этой области, как устройство должно знать, изменяете ли вы азимут или рулон?
Функция SensorManager.remapCoordinateSystem() может использоваться для управления матрицей преобразования, чтобы компенсировать то, что вы знаете о ориентации устройства. Однако мои эксперименты показали, что это не охватывает все случаи, даже некоторые из обычных. Например, если вы хотите переназначить устройство, удерживаемое в вертикальном положении (например, чтобы сделать снимок), вы хотели бы умножить матрицу преобразования на эту матрицу:
1 0 0
0 0 1
0 1 0
перед вызовом getOrientation(), и это не одно из переназначений ориентации, которое remapCoordinateSystem() поддерживает [кто-то, пожалуйста, поправьте меня, если я что-то пропустил здесь].
ОК, так что все это был длинный способ сказать, что если вы используете ориентацию, либо из датчика TYPE_ORIENTATION, либо из getOrientation(), вы, вероятно, ошибаетесь. Единственный раз, когда вы на самом деле хотите, чтобы углы Эйлера отображали информацию о ориентации в удобной для пользователя форме, комментировать фотографию, управлять дисплеем полетного инструмента или что-то подобное.
Если вы хотите выполнять вычисления, связанные с ориентацией устройства, вам почти наверняка будет лучше использовать матрицу преобразования и работать с векторами XYZ.
Работая консультантом, всякий раз, когда кто-то приходит ко мне с проблемой, связанной с углами Эйлера, я подкрепляюсь и спрашиваю их, что они на самом деле пытаются сделать, а затем найдет способ сделать это с помощью векторов.
Возвращаясь к исходному вопросу, getOrientation() должен вернуть три значения в [-180 180] [-90 90] и [-180 180] (после преобразования из радианов). На практике мы рассматриваем азимут как цифры в [0 360], поэтому вам нужно просто добавить 360 к любым отрицательным числам, которые вы получаете. Ваш код выглядит правильно, как написано. Это помогло бы, если бы я точно знал, какие результаты вы ожидали, и что вы получали вместо этого.
Отредактировано для добавления: Еще несколько мыслей. В современных версиях Android используется нечто, называемое "слияние датчиков", что в основном означает, что все доступные входы - акселерометр, магнитометр, гироскоп - объединены вместе в математическом черном ящике (обычно это фильтр Калмана, но зависит от поставщика). В качестве выходов из этого черного ящика берутся все различные датчики - ускорение, магнитное поле, гироскопы, гравитация, линейное ускорение и ориентация.
По возможности вы должны использовать TYPE_GRAVITY, а не TYPE_ACCELEROMETER, как вход для getRotationMatrix().