nuclear@1: /************************************************************************************ nuclear@1: nuclear@1: Filename : Util_MagCalibration.cpp nuclear@1: Content : Procedures for calibrating the magnetometer nuclear@1: Created : April 16, 2013 nuclear@1: Authors : Steve LaValle, Andrew Reisse nuclear@1: nuclear@1: Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. nuclear@1: nuclear@1: Use of this software is subject to the terms of the Oculus license nuclear@1: agreement provided at the time of installation or download, or which nuclear@1: otherwise accompanies this software in either electronic or hard copy form. nuclear@1: nuclear@1: *************************************************************************************/ nuclear@1: nuclear@1: #include "Util_MagCalibration.h" nuclear@1: nuclear@1: namespace OVR { namespace Util { nuclear@1: nuclear@1: void MagCalibration::BeginAutoCalibration(SensorFusion& sf) nuclear@1: { nuclear@1: Stat = Mag_AutoCalibrating; nuclear@1: // This is a "hard" reset of the mag, so need to clear stored values nuclear@1: sf.ClearMagCalibration(); nuclear@1: SampleCount = 0; nuclear@1: nuclear@1: // reset the statistics nuclear@1: MinMagValues = Vector3f(10000.0f,10000.0f,10000.0f); nuclear@1: MaxMagValues = Vector3f(-10000.0f,-10000.0f,-10000.0f); nuclear@1: MinQuatValues = Quatf(1.0f,1.0f,1.0f,1.0f); nuclear@1: MaxQuatValues = Quatf(0.0f,0.0f,0.0f,0.0f); nuclear@1: } nuclear@1: nuclear@1: unsigned MagCalibration::UpdateAutoCalibration(SensorFusion& sf) nuclear@1: { nuclear@1: if (Stat != Mag_AutoCalibrating) nuclear@1: return Stat; nuclear@1: nuclear@1: Quatf q = sf.GetOrientation(); nuclear@1: Vector3f m = sf.GetMagnetometer(); nuclear@1: nuclear@1: InsertIfAcceptable(q, m); nuclear@1: nuclear@1: if ((SampleCount == 4) && (Stat == Mag_AutoCalibrating)) nuclear@1: { nuclear@1: //LogText("Magnetometer Output Spread: %f %f %f\n",MagSpread.x,MagSpread.y,MagSpread.z); nuclear@1: //LogText("Quaternion Spread: %f %f %f %f\n",QuatSpread.x,QuatSpread.y,QuatSpread.z,QuatSpread.w); nuclear@1: SetCalibration(sf); nuclear@1: } nuclear@1: nuclear@1: return Stat; nuclear@1: nuclear@1: } nuclear@1: nuclear@1: void MagCalibration::BeginManualCalibration(SensorFusion& sf) nuclear@1: { nuclear@1: Stat = Mag_ManuallyCalibrating; nuclear@1: sf.ClearMagCalibration(); nuclear@1: SampleCount = 0; nuclear@1: } nuclear@1: nuclear@1: bool MagCalibration::IsAcceptableSample(const Quatf& q, const Vector3f& m) nuclear@1: { nuclear@1: switch (SampleCount) nuclear@1: { nuclear@1: // Initial sample is always acceptable nuclear@1: case 0: nuclear@1: return true; nuclear@1: break; nuclear@1: case 1: nuclear@1: return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& nuclear@1: ((m - MagSamples[0]).LengthSq() > MinMagDistanceSq); nuclear@1: break; nuclear@1: case 2: nuclear@1: return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& nuclear@1: (q.DistanceSq(QuatSamples[1]) > MinQuatDistanceSq)&& nuclear@1: ((m - MagSamples[0]).LengthSq() > MinMagDistanceSq)&& nuclear@1: ((m - MagSamples[1]).LengthSq() > MinMagDistanceSq); nuclear@1: break; nuclear@1: case 3: nuclear@1: return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& nuclear@1: (q.DistanceSq(QuatSamples[1]) > MinQuatDistanceSq)&& nuclear@1: (q.DistanceSq(QuatSamples[2]) > MinQuatDistanceSq)&& nuclear@1: ((PointToPlaneDistance(MagSamples[0],MagSamples[1],MagSamples[2],m) > MinMagDistance)|| nuclear@1: (PointToPlaneDistance(MagSamples[1],MagSamples[2],m,MagSamples[0]) > MinMagDistance)|| nuclear@1: (PointToPlaneDistance(MagSamples[2],m,MagSamples[0],MagSamples[1]) > MinMagDistance)|| nuclear@1: (PointToPlaneDistance(m,MagSamples[0],MagSamples[1],MagSamples[2]) > MinMagDistance)); nuclear@1: } nuclear@1: nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: bool MagCalibration::InsertIfAcceptable(const Quatf& q, const Vector3f& m) nuclear@1: { nuclear@1: // Update some statistics nuclear@1: if (m.x < MinMagValues.x) nuclear@1: MinMagValues.x = m.x; nuclear@1: if (m.y < MinMagValues.y) nuclear@1: MinMagValues.y = m.y; nuclear@1: if (m.z < MinMagValues.z) nuclear@1: MinMagValues.z = m.z; nuclear@1: if (m.x > MaxMagValues.x) nuclear@1: MaxMagValues.x = m.x; nuclear@1: if (m.y > MaxMagValues.y) nuclear@1: MaxMagValues.y = m.y; nuclear@1: if (m.z > MaxMagValues.z) nuclear@1: MaxMagValues.z = m.z; nuclear@1: if (q.x < MinQuatValues.x) nuclear@1: MinQuatValues.x = q.x; nuclear@1: if (q.y < MinQuatValues.y) nuclear@1: MinQuatValues.y = q.y; nuclear@1: if (q.z < MinQuatValues.z) nuclear@1: MinQuatValues.z = q.z; nuclear@1: if (q.w < MinQuatValues.w) nuclear@1: MinQuatValues.w = q.w; nuclear@1: if (q.x > MaxQuatValues.x) nuclear@1: MaxQuatValues.x = q.x; nuclear@1: if (q.y > MaxQuatValues.y) nuclear@1: MaxQuatValues.y = q.y; nuclear@1: if (q.z > MaxQuatValues.z) nuclear@1: MaxQuatValues.z = q.z; nuclear@1: if (q.w > MaxQuatValues.w) nuclear@1: MaxQuatValues.w = q.w; nuclear@1: MagSpread = MaxMagValues - MinMagValues; nuclear@1: QuatSpread = MaxQuatValues - MinQuatValues; nuclear@1: nuclear@1: if (IsAcceptableSample(q, m)) nuclear@1: { nuclear@1: MagSamples[SampleCount] = m; nuclear@1: QuatSamples[SampleCount] = q; nuclear@1: SampleCount++; nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: Matrix4f MagCalibration::GetMagCalibration() const nuclear@1: { nuclear@1: Matrix4f calMat = Matrix4f(); nuclear@1: calMat.M[0][3] = -MagCenter.x; nuclear@1: calMat.M[1][3] = -MagCenter.y; nuclear@1: calMat.M[2][3] = -MagCenter.z; nuclear@1: return calMat; nuclear@1: } nuclear@1: nuclear@1: bool MagCalibration::SetCalibration(SensorFusion& sf) nuclear@1: { nuclear@1: if (SampleCount < 4) nuclear@1: return false; nuclear@1: nuclear@1: MagCenter = CalculateSphereCenter(MagSamples[0],MagSamples[1],MagSamples[2],MagSamples[3]); nuclear@1: Matrix4f calMat = GetMagCalibration(); nuclear@1: sf.SetMagCalibration(calMat); nuclear@1: Stat = Mag_Calibrated; nuclear@1: //LogText("MagCenter: %f %f %f\n",MagCenter.x,MagCenter.y,MagCenter.z); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // Calculate the center of a sphere that passes through p1, p2, p3, p4 nuclear@1: Vector3f MagCalibration::CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2, nuclear@1: const Vector3f& p3, const Vector3f& p4) nuclear@1: { nuclear@1: Matrix4f A; nuclear@1: int i; nuclear@1: Vector3f p[4]; nuclear@1: p[0] = p1; nuclear@1: p[1] = p2; nuclear@1: p[2] = p3; nuclear@1: p[3] = p4; nuclear@1: nuclear@1: for (i = 0; i < 4; i++) nuclear@1: { nuclear@1: A.M[i][0] = p[i].x; nuclear@1: A.M[i][1] = p[i].y; nuclear@1: A.M[i][2] = p[i].z; nuclear@1: A.M[i][3] = 1.0f; nuclear@1: } nuclear@1: float m11 = A.Determinant(); nuclear@1: OVR_ASSERT(m11 != 0.0f); nuclear@1: nuclear@1: for (i = 0; i < 4; i++) nuclear@1: { nuclear@1: A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; nuclear@1: A.M[i][1] = p[i].y; nuclear@1: A.M[i][2] = p[i].z; nuclear@1: A.M[i][3] = 1.0f; nuclear@1: } nuclear@1: float m12 = A.Determinant(); nuclear@1: nuclear@1: for (i = 0; i < 4; i++) nuclear@1: { nuclear@1: A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; nuclear@1: A.M[i][1] = p[i].x; nuclear@1: A.M[i][2] = p[i].z; nuclear@1: A.M[i][3] = 1.0f; nuclear@1: } nuclear@1: float m13 = A.Determinant(); nuclear@1: nuclear@1: for (i = 0; i < 4; i++) nuclear@1: { nuclear@1: A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; nuclear@1: A.M[i][1] = p[i].x; nuclear@1: A.M[i][2] = p[i].y; nuclear@1: A.M[i][3] = 1.0f; nuclear@1: } nuclear@1: float m14 = A.Determinant(); nuclear@1: nuclear@1: float c = 0.5f / m11; nuclear@1: return Vector3f(c*m12, -c*m13, c*m14); nuclear@1: } nuclear@1: nuclear@1: // Distance from p4 to the nearest point on a plane through p1, p2, p3 nuclear@1: float MagCalibration::PointToPlaneDistance(const Vector3f& p1, const Vector3f& p2, nuclear@1: const Vector3f& p3, const Vector3f& p4) nuclear@1: { nuclear@1: Vector3f v1 = p1 - p2; nuclear@1: Vector3f v2 = p1 - p3; nuclear@1: Vector3f planeNormal = v1.Cross(v2); nuclear@1: planeNormal.Normalize(); nuclear@1: return (fabs((planeNormal * p4) - planeNormal * p1)); nuclear@1: } nuclear@1: nuclear@1: }}