I want to be able to change the Device Motion Manager Reference frame (for the gyro) so that I have my gravity vector on the Y axis.
Usually when you start Device Motion Manager Updates you will only have the z axis of the phone aligned with gravity.
You can change this to use the magnetometer to make the x axis be aligned with either the magnetic or true north pole. With this I have my X axis Pointing north and my Z axis pointing down.
What I want to do is have my Y axis (negative) pointing down (so that its aligned with the gravity) and also have my X axis pointing the true magnetic pole.
The result I want is such that when have my phone standing still in a vertical (portrait) orientation the right of the phone will be aligned to the north pole and all my readings (roll, pitch, yaw) will read as 0. Then with this if i rotate my phone on the X axis the pitch will change, and if I rotate around the Y axis the yaw will change.
So far I know i can set my own reference frame if I multiply by the inverse of the attitude a previously stored attitude, (like i could set my phone in this orientation MANUALLY, save that attitude and simply keep multiplying the new attitude by the inverse of this stored one and all my readings will be exactly like the ones i want).
But setting it manually is not an option, so how do i make this programatically?
I don't think there is a function to create my own attitude reference frame, or if at least there was a function to multiply the attitude by a rotation matrix then i could probably solve this. (because i would just multiply all the attitude by a 90 degrees change in the pitch).
I hope I explained myself clearly,
I will appreciate any suggestions. thanks
PD: These are the iPhone Orientation coordinates:
Pseudo code:
Here is the code:
- (void) initMotionCapture
{
firstGravityReading = NO;
referenceAttitude = nil;
if (motionManager == nil)
{
self.motionManager = [CMMotionManager new];
}
motionManager.deviceMotionUpdateInterval = 0.01;
self.gravityTimer = [NSTimer scheduledTimerWithTimeInterval:1/60.0
target:self
selector:@selector(getFirstGravityReading)
userInfo:nil repeats:YES];
}
- (void) getFirstGravityReading
{
CMAcceleration currentGravity;
CMDeviceMotion *dm = motionManager.deviceMotion;
referenceAttitude = dm.attitude;
currentGravity = dm.gravity;
[motionManager startDeviceMotionUpdates];
if (currentGravity.x !=0 &&
currentGravity.y !=0 && currentGravity.z !=0)
{
NSLog(@"Gravity = (%f,%f,%f)",
currentGravity.x, currentGravity.y, currentGravity.z);
firstGravityReading = YES;
[gravityTimer invalidate];
self.gravityTimer = nil;
[self setupCompass];
}
}
- (void) setupCompass
{
//Draw your cube... I am using a quartz 3D perspective hack!
CATransform3D initialTransform = perspectiveTransformedLayer.sublayerTransform;
initialTransform.m34 = 1.0/-10000;
//HERE IS WHAT YOU GUYS NEED... the vector equations!
NSLog(@"Gravity = (%f,%f,%f)",
currentGravity.x, currentGravity.y, currentGravity.z);
//we have current gravity vector and our device gravity vector of (0, 0, -1)
// get the dot product
float dotProduct = currentGravity.x*0 +
currentGravity.y*0 +
currentGravity.z*-1;
float innerMagnitudeProduct = currentGravity.x*currentGravity.x +
currentGravity.y + currentGravity.y +
currentGravity.z*currentGravity.z;
float magnitudeCurrentGravity = sqrt(innerMagnitudeProduct);
float magnitudeDeviceVector = 1; //since (0,0,-1) computes to: 0*0 + 0*0 + -1*-1 = 1
thetaOffset = acos(dotProduct/(magnitudeCurrentGravity*magnitudeDeviceVector));
NSLog(@"theta(degrees) = %f", thetaOffset*180.0/M_PI);
//Now we have the device angle to the gravity vector (0,0,-1)
//We must transform these coordinates to match our
//device's attitude by transforming to theta prime
float theta_deg = thetaOffset*180.0/M_PI;
float thetaPrime_deg = -theta_deg + 90; // ThetaPrime = -Theta + 90 <==> y=mx+b
NSLog(@"thetaPrime(degrees) = %f", thetaOffset*180.0/M_PI);
deviceOffsetRotation =
CATransform3DMakeRotation((thetaPrime_deg) * M_PI / 180.0, 1, 0, 0);
initialTransform = CATransform3DConcat(deviceOffsetRotation, initialTransform);
perspectiveTransformedLayer.sublayerTransform = initialTransform;
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:1/60.0
target:self
selector:@selector(tick)
userInfo:nil
repeats:YES];
}
- (void) tick
{
CMRotationMatrix rotation;
CMDeviceMotion *deviceMotion = motionManager.deviceMotion;
CMAttitude *attitude = deviceMotion.attitude;
if (referenceAttitude != nil)
{
[attitude multiplyByInverseOfAttitude:referenceAttitude];
}
rotation = attitude.rotationMatrix;
CATransform3D rotationalTransform = perspectiveTransformedLayer.sublayerTransform;
//inverse (or called the transpose) of the attitude.rotationalMatrix
rotationalTransform.m11 = rotation.m11;
rotationalTransform.m12 = rotation.m21;
rotationalTransform.m13 = rotation.m31;
rotationalTransform.m21 = rotation.m12;
rotationalTransform.m22 = rotation.m22;
rotationalTransform.m23 = rotation.m32;
rotationalTransform.m31 = rotation.m13;
rotationalTransform.m32 = rotation.m23;
rotationalTransform.m33 = rotation.m33;
rotationalTransform =
CATransform3DConcat(deviceOffsetRotation, rotationalTransform);
rotationalTransform =
CATransform3DConcat(rotationalTransform,
CATransform3DMakeScale(1.0, -1.0, 1.0));
perspectiveTransformedLayer.sublayerTransform = rotationalTransform;
}