diff --git a/OculusSDK/LibOVR/Src/Util/Util_MagCalibration.cpp b/OculusSDK/LibOVR/Src/Util/Util_MagCalibration.cpp index f3e72f5..cca0fba 100644 --- a/OculusSDK/LibOVR/Src/Util/Util_MagCalibration.cpp +++ b/OculusSDK/LibOVR/Src/Util/Util_MagCalibration.cpp @@ -22,7 +22,7 @@ void MagCalibration::BeginAutoCalibration(SensorFusion& sf) Status = Mag_AutoCalibrating; // This is a "hard" reset of the mag, so need to clear stored values sf.ClearMagCalibration(); - SampleCount = 0; + SampleCount = AutoSampleCount = 0; } unsigned MagCalibration::UpdateAutoCalibration(SensorFusion& sf) @@ -32,14 +32,145 @@ unsigned MagCalibration::UpdateAutoCalibration(SensorFusion& sf) Quatf q = sf.GetOrientation(); Vector3f m = sf.GetMagnetometer(); + + // new method + if( NewAutoMethod ) + { + if (!InsertAutoSample(q, m)) + return Status; + } + // old method + else + { + InsertIfAcceptable(q, m); + } + + if (SampleCount == REQUIRED_SAMPLE_COUNT && Status == Mag_AutoCalibrating) + SetCalibration(sf); - InsertIfAcceptable(q, m); + return Status; - if ((SampleCount == 4) && (Status == Mag_AutoCalibrating)) - SetCalibration(sf); +} - return Status; +bool MagCalibration::InsertAutoSample(const Quatf& q, const Vector3f& m) +{ + int lowestRatingIndex = 0; + float myRating = 1.0f; + + double autoSamplesRating[ MAX_AUTO_SAMPLES ]; + + for( unsigned int i = 0; i < AutoSampleCount; i++ ) + autoSamplesRating[i] = 1.0f; + // Simply rate by scaling all samples with the difference to all other samples. + // This leads to duplicates getting dropped early. + for( unsigned int i = 0; i < AutoSampleCount; i++ ) + { + for( unsigned int j = i+1; j < AutoSampleCount; j++ ) + { + double deltaRating = AutoSamplesQuat[i].DistanceSq(AutoSamplesQuat[j]); + autoSamplesRating[i] *= deltaRating; + autoSamplesRating[j] *= deltaRating; + } + + // include new sample in rating + double deltaRating = AutoSamplesQuat[i].DistanceSq( q ); + myRating *= deltaRating; + autoSamplesRating[i] *= deltaRating; + + // keep track of worst sample + if( autoSamplesRating[i] < autoSamplesRating[lowestRatingIndex] ) + { + lowestRatingIndex = i; + } + } + + // append new sample or replace previous one if new one is unique enough + int newIndex = -1; + if( AutoSampleCount < MAX_AUTO_SAMPLES ) + { + newIndex = AutoSampleCount; + AutoSampleCount++; + } + else if( myRating >= autoSamplesRating[lowestRatingIndex] ) + { + newIndex = lowestRatingIndex; + } + + if( newIndex < 0 ) + return false; + + AutoSamplesMag[newIndex] = m; + AutoSamplesQuat[newIndex] = q; + AutoNewestSample = newIndex; + + return EvalAutoCalibrationSamples( newIndex ); +} + +bool MagCalibration::EvalAutoCalibrationSamples(unsigned int newestSample) +{ + if( AutoSampleCount < REQUIRED_SAMPLE_COUNT ) + return false; + + // brute force; optimization can wait + // possible optimizations: + // 1. be smarter + + // if there is a solution it has to include the newest sample + unsigned int indices[REQUIRED_SAMPLE_COUNT]; + indices[0] = newestSample; + if( EvalAutoCalibrationSamples( indices, 1 ) ) + { + for( int i = 0; i < REQUIRED_SAMPLE_COUNT; i++ ) + { + MagSamples[i] = AutoSamplesMag[ indices[i] ]; + QuatSamples[i] = AutoSamplesQuat[ indices[i] ]; + } + SampleCount = REQUIRED_SAMPLE_COUNT; + return true; + } + + return false; +} + +bool MagCalibration::EvalAutoCalibrationSamples( unsigned int (&indices)[REQUIRED_SAMPLE_COUNT], unsigned int pos ) +{ + int start = 0; + + if( pos > 1 ) + start = indices[pos-1]+1; + + for( unsigned int i = start; i < AutoSampleCount; i++ ) + { + // first sample is fixed to the newest sample as a small optimization + if( i == indices[0] ) + continue; + + indices[pos] = i; + if( pos == REQUIRED_SAMPLE_COUNT-1 ) + { + if( CheckCalibrationSamples( indices ) ) + return true; + } + else if( EvalAutoCalibrationSamples( indices, pos+1 ) ) + { + return true; + } + } + + return false; +} + +// unsigned int s0, unsigned int s1, unsigned int s2, unsigned int s3 ) +bool MagCalibration::CheckCalibrationSamples( unsigned int (&indices)[REQUIRED_SAMPLE_COUNT] ) +{ + SampleCount = 0; + for( unsigned int i = 0; i < REQUIRED_SAMPLE_COUNT; i++ ) + { + unsigned int index = indices[i]; + InsertIfAcceptable( AutoSamplesQuat[index], AutoSamplesMag[index] ); + } + return SampleCount == REQUIRED_SAMPLE_COUNT; } void MagCalibration::BeginManualCalibration(SensorFusion& sf) @@ -97,7 +228,7 @@ bool MagCalibration::InsertIfAcceptable(const Quatf& q, const Vector3f& m) bool MagCalibration::SetCalibration(SensorFusion& sf) { - if (SampleCount < 4) + if (SampleCount < REQUIRED_SAMPLE_COUNT) return false; MagCenter = CalculateSphereCenter(MagSamples[0],MagSamples[1],MagSamples[2],MagSamples[3]); diff --git a/OculusSDK/LibOVR/Src/Util/Util_MagCalibration.h b/OculusSDK/LibOVR/Src/Util/Util_MagCalibration.h index 9371125..bd8d1a7 100644 --- a/OculusSDK/LibOVR/Src/Util/Util_MagCalibration.h +++ b/OculusSDK/LibOVR/Src/Util/Util_MagCalibration.h @@ -37,7 +37,7 @@ public: MagCalibration() : Status(Mag_Uninitialized), MinMagDistance(0.3f), MinQuatDistance(0.5f), - SampleCount(0) + SampleCount(0), AutoSampleCount(0), NewAutoMethod(true) { MinMagDistanceSq = MinMagDistance * MinMagDistance; MinQuatDistanceSq = MinQuatDistance * MinQuatDistance; @@ -47,11 +47,11 @@ public: bool IsUnitialized() const { return Status == Mag_Uninitialized; } bool IsCalibrated() const { return Status == Mag_Calibrated; } int NumberOfSamples() const { return SampleCount; } - int RequiredSampleCount() const { return 4; } + int RequiredSampleCount() const { return REQUIRED_SAMPLE_COUNT; } void ClearCalibration(SensorFusion& sf) { Status = Mag_Uninitialized; - SampleCount = 0; + SampleCount = AutoSampleCount = 0; sf.ClearMagCalibration(); }; @@ -64,6 +64,7 @@ public: void BeginManualCalibration(SensorFusion& sf); bool IsAcceptableSample(const Quatf& q, const Vector3f& m); bool InsertIfAcceptable(const Quatf& q, const Vector3f& m); + // Returns true if successful, requiring that SampleCount = 4 bool SetCalibration(SensorFusion& sf); bool IsManuallyCalibrating() const { return Status == Mag_ManuallyCalibrating; } @@ -89,6 +90,15 @@ public: Vector3f GetMagCenter() const { return MagCenter; } private: + static const int REQUIRED_SAMPLE_COUNT = 4; + static const int MAX_AUTO_SAMPLES = 16; + static_assert( MAX_AUTO_SAMPLES > REQUIRED_SAMPLE_COUNT, "Auto calibration will never work out for you." ); + + bool InsertAutoSample(const Quatf& q, const Vector3f& m); + bool EvalAutoCalibrationSamples(unsigned int newestSample ); + bool EvalAutoCalibrationSamples( unsigned int (&indices)[REQUIRED_SAMPLE_COUNT], unsigned int pos ); + bool CheckCalibrationSamples(unsigned int (&indices)[REQUIRED_SAMPLE_COUNT]); + // Determine the unique sphere through 4 non-coplanar points Vector3f CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2, const Vector3f& p3, const Vector3f& p4); @@ -103,6 +113,12 @@ private: float MinQuatDistance; float MinMagDistanceSq; float MinQuatDistanceSq; + + bool NewAutoMethod; + unsigned AutoSampleCount; + unsigned AutoNewestSample; + Vector3f AutoSamplesMag[ MAX_AUTO_SAMPLES ]; + Quatf AutoSamplesQuat[ MAX_AUTO_SAMPLES ]; unsigned SampleCount; Vector3f MagSamples[4];