Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.hardware.cts;
     18 
     19 import junit.framework.Assert;
     20 
     21 import android.content.Context;
     22 import android.hardware.SensorManager;
     23 import android.os.PowerManager;
     24 
     25 import java.util.Random;
     26 
     27 public class SensorManagerStaticTest extends SensorTestCase {
     28     private static final String TAG = "SensorManagerTest";
     29 
     30     // local float version of PI
     31     private static final float FLOAT_PI = (float) Math.PI;
     32 
     33 
     34     private PowerManager.WakeLock mWakeLock;
     35 
     36     @Override
     37     protected void setUp() throws Exception {
     38         Context context = getContext();
     39         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
     40         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
     41 
     42         mWakeLock.acquire();
     43     }
     44 
     45     @Override
     46     protected void tearDown(){
     47         if (mWakeLock != null && mWakeLock.isHeld()) {
     48             mWakeLock.release();
     49         }
     50     }
     51 
     52     // SensorManager Tests
     53     public void testGetAltitude() throws Exception {
     54         float r, q;
     55         float altitude;
     56 
     57         // identity property
     58         for (r = 0.5f; r < 1.3f; r += 0.1f) {
     59 
     60             altitude = SensorManager.getAltitude(r * SensorManager.PRESSURE_STANDARD_ATMOSPHERE,
     61                                                  r * SensorManager.PRESSURE_STANDARD_ATMOSPHERE);
     62             assertRoughlyEqual("getAltitude identity property violated.", altitude, 0.0f, 0.1f);
     63         }
     64 
     65         // uniform increasing as pressure decreases property
     66         float prevAltitude = 1e5f; // 100km ceiling
     67         for (r = 0.5f; r < 1.3f; r += 0.01f) {
     68             altitude = SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE,
     69                                                  r * SensorManager.PRESSURE_STANDARD_ATMOSPHERE);
     70 
     71             assertTrue("getAltitude result has to decrease as p increase.", prevAltitude > altitude);
     72             prevAltitude = altitude;
     73         }
     74 
     75         // compare to a reference algorithm
     76         final float coef = 1.0f / 5.255f;
     77         for (r = 0.8f; r < 1.3f; r += 0.1f) {
     78             for (q = 1.1f * r; q > 0.5f * r; q -= 0.1f * r) {
     79                 float p0 = r * SensorManager.PRESSURE_STANDARD_ATMOSPHERE;
     80                 float p  = q * SensorManager.PRESSURE_STANDARD_ATMOSPHERE;
     81 
     82                 float t1 = SensorManager.getAltitude(p0, p);
     83                 float t2 = 44330.f*(1.0f- (float) Math.pow(p/p0, coef));
     84 
     85                 assertRoughlyEqual(
     86                       String.format("getAltitude comparing to reference algorithm failed. " +
     87                           "Detail: getAltitude(%f, %f) => %f, reference => %f",
     88                           p0, p, t1, t2),
     89                       t1, t2, 100.f);
     90             }
     91         }
     92 
     93     }
     94 
     95     public void testGetAngleChange() throws Exception {
     96         TestDataGenerator data = new TestDataGenerator();
     97 
     98         int i;
     99         float [] rotv = new float[3];
    100         float [] rotv2 = new float[3];
    101 
    102         // test many instances
    103         for (i=0; i<100; ++i) {
    104             float [] R1, R12, R2;
    105             // azimuth(yaw) pitch roll
    106             data.nextRotationAngles(rotv);
    107             R1 = mat9VRot(rotv); // random base
    108 
    109             // azimuth(yaw) pitch roll
    110             data.nextRotationAngles(rotv);
    111             R12 = mat9VRot(rotv);
    112             R2 = mat9Mul(R1, R12); // apply another random rotation
    113 
    114             // test different variations of input matrix format
    115             switch(i & 3) {
    116                 case 0:
    117                     SensorManager.getAngleChange(rotv2, R2, R1);
    118                     break;
    119                 case 1:
    120                     SensorManager.getAngleChange(rotv2, mat9to16(R2), R1);
    121                     break;
    122                 case 2:
    123                     SensorManager.getAngleChange(rotv2, R2, mat9to16(R1));
    124                     break;
    125                 case 3:
    126                     SensorManager.getAngleChange(rotv2, mat9to16(R2), mat9to16(R1));
    127                     break;
    128             }
    129 
    130             // check range
    131             assertRotationAnglesValid("getAngleChange result out of range.", rotv2);
    132 
    133             // avoid directly checking the rotation angles to avoid corner cases
    134             float [] R12rt = mat9T(mat9VRot(rotv2));
    135             float [] RI = mat9Mul(R12rt, R12);
    136 
    137             assertRoughlyEqual(
    138                 String.format("getAngleChange result is incorrect. Details: case %d, " +
    139                     "truth = [%f, %f, %f], result = [%f, %f, %f]", i, rotv[0], rotv[1], rotv[2],
    140                     rotv2[0], rotv2[1], rotv2[2]),
    141                 RI[0] + RI[4] + RI[8], 3.f, 1e-4f);
    142         }
    143     }
    144 
    145     public void testGetInclination() throws Exception {
    146         TestDataGenerator data = new TestDataGenerator();
    147 
    148         int i;
    149         float [] rotv = new float[3];
    150         float [] rotv2 = new float[3];
    151         float [] rotv3;
    152 
    153         // test many instances
    154         for (i = 0; i < 100; ++i) {
    155             float [] R;
    156             float angle;
    157             angle = (data.nextFloat()-0.5f) * FLOAT_PI;
    158             R = mat9Rot(SensorManager.AXIS_X, -angle);
    159 
    160             float angler = ((i&1) != 0) ?
    161                     SensorManager.getInclination(mat9to16(R)) : SensorManager.getInclination(R);
    162             assertRoughlyEqual(
    163                 String.format(
    164                     "getInclination return incorrect result. Detail: case %d, truth %f, result %f.",
    165                     i, angle, angler),
    166                 angle, angler, 1e-4f);
    167         }
    168     }
    169 
    170     public void testGetOrientation() throws Exception {
    171         TestDataGenerator data = new TestDataGenerator();
    172 
    173         int i;
    174         float [] rotv = new float[3];
    175         float [] rotv2 = new float[3];
    176         float [] rotv3;
    177 
    178         // test many instances
    179         for (i=0; i<100; ++i) {
    180             float [] R;
    181             // yaw pitch roll
    182             data.nextRotationAngles(rotv);
    183             R = mat9VRot(rotv);
    184 
    185             rotv3 = SensorManager.getOrientation( ((i&1) != 0) ? R : mat9to16(R), rotv2);
    186             assertTrue("getOrientaion has to return the array passed in argument", rotv3 == rotv2);
    187 
    188             // check range
    189             assertRotationAnglesValid("getOrientation result out of range.", rotv2);
    190 
    191             // Avoid directly comparing rotation angles. Instead, compare the rotation matrix.
    192             float [] Rr = mat9T(mat9VRot(rotv2));
    193             float [] RI = mat9Mul(Rr, R);
    194 
    195             assertRoughlyEqual(
    196                 String.format("getOrientation result is incorrect. Details: case %d, " +
    197                     "truth = [%f, %f, %f], result = [%f, %f, %f]", i, rotv[0], rotv[1], rotv[2],
    198                     rotv2[0], rotv2[1], rotv2[2]),
    199                 RI[0] + RI[4] + RI[8], 3.f, 1e-4f);
    200         }
    201     }
    202 
    203     public void testGetQuaternionFromVector() throws Exception {
    204         TestDataGenerator data = new TestDataGenerator();
    205 
    206         int i;
    207         float [] v;
    208         float [] q = new float[4];
    209         float [] q2 = new float[4];
    210         float [] v3 = new float[3];
    211         float [] v4 = new float[4];
    212         float [] v5 = new float[5];
    213         float [][] vs = new float[][] {v3, v4, v5};
    214 
    215         float [] xyzth = new float[4];
    216         for (i = 0; i < 100; ++i) {
    217             float c, s;
    218 
    219             data.nextRotationAxisAngle(xyzth);
    220 
    221             c = (float) Math.cos(xyzth[3]);
    222             s = (float) Math.sin(xyzth[3]);
    223             if (c < 0.f) {
    224                 c = -c;
    225                 s = -s;
    226             }
    227 
    228             v = vs[i%3];
    229             switch(i%3) {
    230                 case 2:
    231                     v[4] = data.nextBoolean() ? data.nextFloat() : -1.f;
    232                 case 1:
    233                     v[3] = c;
    234                 case 0:
    235                     v[0] = s * xyzth[0];
    236                     v[1] = s * xyzth[1];
    237                     v[2] = s * xyzth[2];
    238             }
    239 
    240             q2[0] = c;
    241             q2[1] = v[0];
    242             q2[2] = v[1];
    243             q2[3] = v[2];
    244 
    245             SensorManager.getQuaternionFromVector(q, v);
    246             assertVectorRoughlyEqual(
    247                 String.format("getQuaternionFromVector returns wrong results, Details: case %d, " +
    248                     "truth = (%f, %f, %f, %f), result = (%f, %f, %f, %f).",
    249                     i, q2[0], q2[1], q2[2], q2[3], q[0], q[1], q[2], q[3]),
    250                 q, q2, 1e-4f);
    251         }
    252     }
    253 
    254     public void testGetRotationMatrix() throws Exception {
    255         TestDataGenerator data = new TestDataGenerator();
    256         final float gravity = 9.81f;
    257         final float magStrength = 50.f;
    258 
    259         int i;
    260         float [] gm = new float[9];
    261         float [] rotv = new float[3];
    262         float [] gI = null;
    263         float [] mI = null;
    264         float [] Rr = new float[9];
    265         float [] Ir = new float[9];
    266 
    267         gm[6] = gravity; // m/s^2, first column gravity
    268 
    269         // test many instances
    270         for (i=0; i<100; ++i) {
    271             float [] Rt;
    272             float incline;
    273             // yaw pitch roll
    274             data.nextRotationAngles(rotv);
    275             Rt = mat9T(mat9VRot(rotv)); // from world frame to phone frame
    276             //Rt = mat9I();
    277 
    278             incline = -0.9f * (data.nextFloat() - 0.5f) * FLOAT_PI; // ~ +-80 degrees
    279             //incline = 0.f;
    280             gm[4] = magStrength * (float) Math.cos(-incline); // positive means rotate downwards
    281             gm[7] = magStrength * (float) Math.sin(-incline);
    282 
    283             float [] gmb = mat9Mul(Rt, gm); // do not care about right most column
    284             gI = mat9Axis(gmb, SensorManager.AXIS_X);
    285             mI = mat9Axis(gmb, SensorManager.AXIS_Y);
    286 
    287             assertTrue("getRotationMatrix returns false on valid inputs",
    288                 SensorManager.getRotationMatrix(Rr, Ir, gI, mI));
    289 
    290             float [] n = mat9Mul(Rr, Rt);
    291             assertRoughlyEqual(
    292                 String.format("getRotationMatrix returns incorrect R matrix. " +
    293                     "Details: case %d, truth R = %s, result R = %s.",
    294                     i, mat9ToStr(mat9T(Rt)), mat9ToStr(Rr)),
    295                 n[0] + n[4] + n[8], 3.f, 1e-4f);
    296 
    297 
    298             // Magnetic incline is defined so that it means the magnetic field lines is formed
    299             // by rotate local y axis around -x axis by incline angle. However, I matrix is
    300             // defined as (according to document):
    301             //     [0 m 0] = I * R * geomagnetic,
    302             // which means,
    303             //     I' * [0 m 0] = R * geomagnetic.
    304             // Thus, I' = Rot(-x, incline) and I = Rot(-x, incline)' = Rot(x, incline)
    305             float [] Ix = mat9Rot(SensorManager.AXIS_X, incline);
    306             assertVectorRoughlyEqual(
    307                 String.format("getRotationMatrix returns incorrect I matrix. " +
    308                     "Details: case %d, truth I = %s, result I = %s.",
    309                     i, mat9ToStr(Ix), mat9ToStr(Ir)),
    310                 Ix, Ir, 1e-4f);
    311         }
    312 
    313         // test 16 element inputs
    314         float [] Rr2 = new float[16];
    315         float [] Ir2 = new float[16];
    316 
    317         assertTrue("getRotationMatrix returns false on valid inputs",
    318             SensorManager.getRotationMatrix(Rr2, Ir2, gI, mI));
    319 
    320         assertVectorRoughlyEqual(
    321             "getRotationMatrix acts inconsistent with 9- and 16- elements matrix buffer",
    322             mat16to9(Rr2), Rr, 1e-4f);
    323 
    324         assertVectorRoughlyEqual(
    325             "getRotationMatrix acts inconsistent with 9- and 16- elements matrix buffer",
    326             mat16to9(Ir2), Ir, 1e-4f);
    327 
    328         // test null inputs
    329         assertTrue("getRotationMatrix does not handle null inputs",
    330             SensorManager.getRotationMatrix(Rr, null, gI, mI));
    331 
    332         assertTrue("getRotationMatrix does not handle null inputs",
    333             SensorManager.getRotationMatrix(null, Ir, gI, mI));
    334 
    335         assertTrue("getRotationMatrix does not handle null inputs",
    336             SensorManager.getRotationMatrix(null, null, gI, mI));
    337 
    338         // test fail cases
    339         // free fall, if the acc reading is less than 10% of gravity
    340         gI[0] = gI[1] = gI[2] = data.nextFloat() * gravity * 0.05f; // sqrt(3) * 0.05 < 0.1
    341          assertFalse("getRotationMatrix does not fail when it supposed to fail (gravity too small)",
    342             SensorManager.getRotationMatrix(Rr, Ir, gI, mI));
    343 
    344         // wrong input
    345         assertFalse("getRotationMatrix does not fail when it supposed to fail (singular axis)",
    346             SensorManager.getRotationMatrix(Rr, Ir, gI, gI));
    347     }
    348 
    349     public void testGetRotationMatrixFromVector() throws Exception {
    350         TestDataGenerator data = new TestDataGenerator();
    351 
    352         int i;
    353         float [] v;
    354         float [] q = new float[4];
    355 
    356         float [] v3 = new float[3];
    357         float [] v4 = new float[4];
    358         float [] v5 = new float[5];
    359         float [][] vs = new float[][]{v3, v4, v5};
    360 
    361         float [] m9 = new float[9];
    362         float [] m16 = new float[16];
    363 
    364         // format: x y z theta/2
    365         float [] xyzth = new float[4];
    366         // test the orthogonal property of returned matrix
    367         for (i=0; i<20; ++i) {
    368             float c, s;
    369             data.nextRotationAxisAngle(xyzth);
    370 
    371             c = (float) Math.cos(xyzth[3]);
    372             s = (float) Math.sin(xyzth[3]);
    373             if (c < 0.f) {
    374                 c = -c;
    375                 s = -s;
    376             }
    377 
    378             v = vs[i%3];
    379             switch(i%3) {
    380                 case 2:
    381                     v[4] = data.nextBoolean() ? data.nextFloat() : -1.f;
    382                 case 1:
    383                     v[3] = c;
    384                 case 0:
    385                     v[0] = s * xyzth[0];
    386                     v[1] = s * xyzth[1];
    387                     v[2] = s * xyzth[2];
    388             }
    389 
    390             if ((i % 1) != 0) {
    391                 SensorManager.getRotationMatrixFromVector(m16, v);
    392                 m9 = mat16to9(m16);
    393             }else {
    394                 SensorManager.getRotationMatrixFromVector(m9, v);
    395             }
    396 
    397             float [] n = mat9Mul(m9, mat9T(m9));
    398             assertRoughlyEqual("getRotationMatrixFromVector do not return proper matrix",
    399                     n[0]+ n[4] + n[8], 3.f, 1e-4f);
    400         }
    401 
    402         // test if multiple rotation (total 2pi) about an axis result in identity
    403         v = v3;
    404         float [] Rr = new float[9];
    405 
    406         for (i=0; i<20; ++i) {
    407             float j, halfTheta, residualHalfTheta = FLOAT_PI;
    408             float [] R = mat9I();
    409             float c, s;
    410 
    411             data.nextRotationAxisAngle(xyzth);  // half theta is ignored
    412 
    413             j = data.nextInt(5) + 2;  // 2 ~ 6 rotations
    414 
    415             while(j-- > 0) {
    416                 if (j == 0) {
    417                     halfTheta = residualHalfTheta;
    418                 } else {
    419                     halfTheta = data.nextFloat() * FLOAT_PI;
    420                 }
    421 
    422                 c = (float) Math.cos(halfTheta);
    423                 s = (float) Math.sin(halfTheta);
    424                 if (c < 0.f) {
    425                     c = -c;
    426                     s = -s;
    427                 }
    428 
    429                 v[0] = s * xyzth[0];
    430                 v[1] = s * xyzth[1];
    431                 v[2] = s * xyzth[2];
    432 
    433                 SensorManager.getRotationMatrixFromVector(Rr, v);
    434                 R = mat9Mul(Rr, R);
    435 
    436                 residualHalfTheta -= halfTheta;
    437             }
    438 
    439             assertRoughlyEqual("getRotationMatrixFromVector returns incorrect matrix",
    440                     R[0] + R[4] + R[8], 3.f, 1e-4f);
    441         }
    442 
    443         // test if rotation about trival axis works
    444         v = v3;
    445         for (i=0; i<20; ++i) {
    446             int axis = (i % 3) + 1;
    447             float theta = data.nextFloat() * 2.f * FLOAT_PI;
    448             float [] R;
    449 
    450             v[0] = v[1] = v[2] = 0.f;
    451             v[axis - 1] = (float) Math.sin(theta / 2.f);
    452             if ( (float) Math.cos(theta / 2.f) < 0.f) {
    453                 v[axis-1] = -v[axis-1];
    454             }
    455 
    456             SensorManager.getRotationMatrixFromVector(m9, v);
    457             R = mat9Rot(axis, theta);
    458 
    459             assertVectorRoughlyEqual(
    460                 String.format("getRotationMatrixFromVector returns incorrect matrix with "+
    461                     "simple rotation. Details: case %d, truth R = %s, result R = %s.",
    462                     i, mat9ToStr(R), mat9ToStr(m9)),
    463                 R, m9, 1e-4f);
    464         }
    465     }
    466 
    467     public void testRemapCoordinateSystem() throws Exception {
    468         TestDataGenerator data = new TestDataGenerator();
    469 
    470         int i, j, k;
    471         float [] rotv = new float[3];
    472         float [] Rout = new float[9];
    473         float [] Rout2 = new float[16];
    474         int a1, a2; // AXIS_X/Y/Z
    475         int b1, b2, b3; // AXIS_X/Y/Z w/ or w/o MINUS
    476 
    477         // test a few instances
    478         for (i=0; i<10; ++i) {
    479             float [] R;
    480             // yaw pitch roll
    481             data.nextRotationAngles(rotv);
    482             R = mat9VRot(rotv);
    483 
    484             // total of 6*4 = 24 variations
    485             // 6 = A(3,2)
    486             for (j=0; j<9; ++j) {
    487                 // axis without minus
    488                 a1 = j/3 + 1;
    489                 a2 = j%3 + 1;
    490 
    491                 // skip cases when two axis are the same
    492                 if (a1 == a2) continue;
    493 
    494                 for (k=0; k<3; ++k) {
    495                     // test all minus axis combination: ++, +-, -+, --
    496                     b1 = a1 | (((k & 2) != 0) ? 0x80 : 0);
    497                     b2 = a2 | (((k & 1) != 0) ? 0x80 : 0);
    498                     // the third axis
    499                     b3 = (6 - a1 -a2) |
    500                          ( (((a2 + 3 - a1) % 3 == 2) ? 0x80 : 0) ^ (b1 & 0x80) ^ (b2 & 0x80));
    501 
    502                     // test both input formats
    503                     if ( (i & 1) != 0 ) {
    504                       assertTrue(SensorManager.remapCoordinateSystem(R, b1, b2, Rout));
    505                     } else {
    506                       assertTrue(SensorManager.remapCoordinateSystem(mat9to16(R), b1, b2, Rout2));
    507                       Rout = mat16to9(Rout2);
    508                     }
    509 
    510                     float [] v1, v2;
    511 
    512                     String detail = String.format(
    513                             "Details: case %d (%x %x %x), original R = %s, result R = %s.",
    514                             i, b1, b2, b3, mat9ToStr(R), mat9ToStr(Rout));
    515 
    516                     v1 = mat9Axis(R, SensorManager.AXIS_X);
    517                     v2 = mat9Axis(Rout, b1);
    518                     assertVectorRoughlyEqual(
    519                         "remapCoordinateSystem gives incorrect result (x)." + detail,
    520                         v1, v2, 1e-4f);
    521 
    522                     v1 = mat9Axis(R, SensorManager.AXIS_Y);
    523                     v2 = mat9Axis(Rout, b2);
    524                     assertVectorRoughlyEqual(
    525                         "remapCoordinateSystem gives incorrect result (y)." + detail,
    526                         v1, v2, 1e-4f);
    527 
    528                     v1 = mat9Axis(R, SensorManager.AXIS_Z);
    529                     v2 = mat9Axis(Rout, b3);
    530                     assertVectorRoughlyEqual(
    531                         "remapCoordinateSystem gives incorrect result (z)." + detail,
    532                         v1, v2, 1e-4f);
    533                 }
    534             }
    535 
    536         }
    537 
    538         // test cases when false should be returned
    539         assertTrue("remapCoordinateSystem should return false with mismatch size input and output",
    540                    !SensorManager.remapCoordinateSystem(Rout,
    541                      SensorManager.AXIS_Y, SensorManager.AXIS_Z, Rout2));
    542         assertTrue("remapCoordinateSystem should return false with invalid axis setting",
    543                    !SensorManager.remapCoordinateSystem(Rout,
    544                      SensorManager.AXIS_X, SensorManager.AXIS_X, Rout));
    545         assertTrue("remapCoordinateSystem should return false with invalid axis setting",
    546                    !SensorManager.remapCoordinateSystem(Rout,
    547                      SensorManager.AXIS_X, SensorManager.AXIS_MINUS_X, Rout));
    548 
    549     }
    550 
    551     // Utilities class & functions
    552 
    553     private class TestDataGenerator {
    554         // carry out test deterministically without manually picking numbers
    555         private final long DEFAULT_SEED = 0xFEDCBA9876543210l;
    556 
    557         private Random mRandom;
    558 
    559         TestDataGenerator(long seed) {
    560             mRandom = new Random(seed);
    561         }
    562 
    563         TestDataGenerator() {
    564             mRandom = new Random(DEFAULT_SEED);
    565         }
    566 
    567         void nextRotationAngles(float [] rotv) {
    568             assertTrue(rotv.length == 3);
    569 
    570             rotv[0] = (mRandom.nextFloat()-0.5f) * 2.0f * FLOAT_PI; // azimuth(yaw) -pi ~ pi
    571             rotv[1] = (mRandom.nextFloat()-0.5f) * FLOAT_PI; // pitch -pi/2 ~ +pi/2
    572             rotv[2] = (mRandom.nextFloat()-0.5f) * 2.f * FLOAT_PI; // roll -pi ~ +pi
    573         }
    574 
    575         void nextRotationAxisAngle(float [] aa) {
    576             assertTrue(aa.length == 4);
    577 
    578             aa[0] = (mRandom.nextFloat() - 0.5f) * 2.f;
    579             aa[1] = (mRandom.nextFloat() - 0.5f ) * 2.f * (float) Math.sqrt(1.f - aa[0] * aa[0]);
    580             aa[2] = (mRandom.nextBoolean() ? 1.f : -1.f) *
    581                         (float) Math.sqrt(1.f - aa[0] * aa[0] - aa[1] * aa[1]);
    582             aa[3] = mRandom.nextFloat() * FLOAT_PI;
    583         }
    584 
    585         int nextInt(int i) {
    586             return mRandom.nextInt(i);
    587         }
    588 
    589         float nextFloat() {
    590             return mRandom.nextFloat();
    591         }
    592 
    593         boolean nextBoolean() {
    594             return mRandom.nextBoolean();
    595         }
    596     }
    597 
    598     private static void assertRotationAnglesValid(String message, float[] ra) {
    599 
    600         assertTrue(message, ra.length == 3 &&
    601             ra[0] >= -FLOAT_PI && ra[0] <= FLOAT_PI &&         // azimuth
    602             ra[1] >= -FLOAT_PI / 2.f && ra[1] <= FLOAT_PI / 2.f && // pitch
    603             ra[2] >= -FLOAT_PI && ra[2] <= FLOAT_PI);          // roll
    604     }
    605 
    606     private static void assertRoughlyEqual(String message, float a, float b, float bound) {
    607         assertTrue(message, Math.abs(a-b) < bound);
    608     }
    609 
    610     private static void assertVectorRoughlyEqual(String message, float [] v1, float [] v2,
    611                                                  float bound) {
    612         assertTrue(message, v1.length == v2.length);
    613         int i;
    614         float sum = 0.f;
    615         for (i=0; i<v1.length; ++i) {
    616             sum += (v1[i] - v2[i]) * (v1[i] - v2[i]);
    617         }
    618         assertRoughlyEqual(message, (float)Math.sqrt(sum), 0.f, bound);
    619     }
    620 
    621     private static float [] mat9to16(float [] m) {
    622         assertTrue(m.length == 9);
    623 
    624         float [] n  = new float[16];
    625         int i;
    626         for (i=0; i<9; ++i) {
    627             n[i+i/3] = m[i];
    628         }
    629         n[15] = 1.f;
    630         return n;
    631     }
    632 
    633     private static float [] mat16to9(float [] m) {
    634         assertTrue(m.length == 16);
    635 
    636         float [] n = new float[9];
    637         int i;
    638         for (i=0; i<9; ++i) {
    639             n[i] = m[i + i/3];
    640         }
    641         return n;
    642     }
    643 
    644     private static float [] mat9Mul(float [] m, float [] n) {
    645         assertTrue(m.length == 9 && n.length == 9);
    646 
    647         float [] r = new float[9];
    648         int i, j, k;
    649 
    650         for (i = 0; i < 3; ++i)
    651             for (j = 0; j < 3; ++j)
    652                 for (k = 0; k < 3; ++k)
    653                     r[i * 3 + j] += m[i * 3 + k] * n[k * 3 + j];
    654 
    655         return r;
    656     }
    657 
    658     private static float [] mat9T(float [] m) {
    659         assertTrue(m.length == 9);
    660 
    661         int i, j;
    662         float [] n = new float[9];
    663 
    664         for (i = 0; i < 3; ++i)
    665             for (j = 0; j < 3; ++j)
    666                 n[i * 3 + j] = m[j * 3 + i];
    667 
    668         return n;
    669     }
    670 
    671     private static float [] mat9I() {
    672         float [] m = new float[9];
    673         m[0] = m[4] = m[8] = 1.f;
    674         return m;
    675     }
    676 
    677     private static float [] mat9Rot(int axis, float angle) {
    678         float [] m = new float[9];
    679         switch (axis) {
    680             case SensorManager.AXIS_X:
    681                 m[0] = 1.f;
    682                 m[4] = m[8] = (float) Math.cos(angle);
    683                 m[5] = - (m[7] = (float) Math.sin(angle));
    684                 break;
    685             case SensorManager.AXIS_Y:
    686                 m[4] = 1.f;
    687                 m[0] = m[8] = (float) Math.cos(angle);
    688                 m[6] = - (m[2] = (float) Math.sin(angle));
    689                 break;
    690             case SensorManager.AXIS_Z:
    691                 m[8] = 1.f;
    692                 m[0] = m[4] = (float) Math.cos(angle);
    693                 m[1] = - (m[3] = (float) Math.sin(angle));
    694                 break;
    695             default:
    696                 // should never be here
    697                 assertTrue(false);
    698         }
    699         return m;
    700     }
    701 
    702     private static float [] mat9VRot(float [] angles) {
    703         assertTrue(angles.length == 3);
    704         // yaw, android yaw rotate to -z
    705         float [] R = mat9Rot(SensorManager.AXIS_Z, -angles[0]);
    706         // pitch, android pitch rotate to -x
    707         R = mat9Mul(R, mat9Rot(SensorManager.AXIS_X, -angles[1]));
    708         // roll
    709         R = mat9Mul(R, mat9Rot(SensorManager.AXIS_Y, angles[2]));
    710 
    711         return R;
    712     }
    713 
    714     private static float [] mat9Axis(float m[], int axis) {
    715         assertTrue(m.length == 9);
    716 
    717         boolean negative = (axis & 0x80) != 0;
    718         float [] v = new float[3];
    719         int offset;
    720 
    721         offset = (axis & ~0x80) - 1;
    722         v[0] = negative ? -m[offset]   : m[offset];
    723         v[1] = negative ? -m[offset+3] : m[offset+3];
    724         v[2] = negative ? -m[offset+6] : m[offset+6];
    725         return v;
    726     }
    727 
    728     private static float vecInner(float u[], float v[]) {
    729         assertTrue(u.length == v.length);
    730 
    731         int i;
    732         float sum = 0.f;
    733 
    734         for (i=0; i < v.length; ++i) {
    735             sum += u[i]*v[i];
    736         }
    737         return (float)Math.sqrt(sum);
    738     }
    739 
    740     private static String vecToStr(float u[]) {
    741         int i;
    742         String s;
    743         switch (u.length) {
    744             case 3:
    745                 return String.format("[%f, %f, %f]", u[0], u[1], u[2]);
    746             case 4:
    747                 return String.format("(%f, %f, %f, %f)", u[0], u[1], u[2], u[3]);
    748             default:
    749                 s = "[";
    750                 for (i = 0; i < u.length-1; ++i) {
    751                     s += String.format("%f, ", u[i]);
    752                 }
    753                 s += String.format("%f]", u[i]);
    754                 return s;
    755         }
    756     }
    757 
    758     private static String mat9ToStr(float m[]) {
    759         assertTrue(m.length == 9);
    760         return String.format("[%f, %f, %f; %f, %f, %f; %f, %f, %f]",
    761             m[0], m[1], m[2],
    762             m[3], m[4], m[5],
    763             m[6], m[7], m[8]);
    764     }
    765 
    766     private static String mat16ToStr(float m[]) {
    767         assertTrue(m.length == 16);
    768         return String.format("[%f, %f, %f, %f; %f, %f, %f, %f; %f, %f, %f, %f; %f, %f, %f, %f]",
    769             m[0], m[1], m[2], m[3],
    770             m[4], m[5], m[6], m[7],
    771             m[8], m[9], m[10], m[11],
    772             m[12], m[13], m[14], m[15]);
    773     }
    774 
    775 }
    776 
    777