#ifndef INDOOR_IMU_COMPASSDETECTION_H #define INDOOR_IMU_COMPASSDETECTION_H #include "MagnetometerData.h" #include "PoseDetection.h" #include "../../data/Timestamp.h" #include "../../math/MovingAverageTS.h" #include "../../math/MovingStdDevTS.h" #include "../../geo/Point3.h" #include "../../Assertions.h" #include "CompassDetectionPlot.h" #include #include /** * custom compass implementation, similar to turn-detection */ class CompassDetection { private: #ifdef WITH_DEBUG_PLOT CompassDetectionPlot plot; #endif // struct { // Eigen::Matrix3f rotationMatrix = Eigen::Matrix3f::Identity(); // bool isKnown = false; // Timestamp lastEstimation; // } orientation; MovingAverageTS avgIn = MovingAverageTS(Timestamp::fromMS(150), MagnetometerData(0,0,0)); //MovingStdDevTS stdDev = MovingStdDevTS(Timestamp::fromMS(2000), MagnetometerData(0,0,0)); MovingStdDevTS stdDev = MovingStdDevTS(Timestamp::fromMS(1500), 0); PoseDetection* pose = nullptr; int numMagReadings = 0; public: /** ctor */ CompassDetection(PoseDetection* pose) : pose(pose) { ; } /** add magnetometer readings, returns the current heading, or NAN (if unstable/unknown) */ float addMagnetometer(const Timestamp& ts, const MagnetometerData& mag) { ++numMagReadings; // get the current magnetometer-reading as vector //Eigen::Vector3f vec; vec << mag.x, mag.y, mag.z; const Vector3 vec(mag.x, mag.y, mag.z); // rotate it into our desired coordinate system, where the smartphone lies flat on the ground //Eigen::Vector3f _curMag = pose->getMatrix() * vec; const Vector3 _curMag = pose->getMatrix() * vec; //avgIn.add(ts, MagnetometerData(_curMag(0), _curMag(1), _curMag(2))); avgIn.add(ts, MagnetometerData(_curMag.x, _curMag.y, _curMag.z)); const MagnetometerData curMag = avgIn.get(); //const MagnetometerData curMag =MagnetometerData(_curMag.x, _curMag.y, _curMag.z); // calculate standard-deviation //stdDev.add(ts, curMag); //const float dev = std::max(stdDev.get().x, stdDev.get().y); // calculate angle // https://aerocontent.honeywell.com/aero/common/documents/myaerospacecatalog-documents/Defense_Brochures-documents/Magnetic__Literature_Application_notes-documents/AN203_Compass_Heading_Using_Magnetometers.pdf const float mx = curMag.x; const float my = curMag.y; const float tmp = std::atan2(my, mx); //const float tmp = (my > 0) ? (M_PI*0.5 - std::atan(mx/my)) : (M_PI*1.5 - atan(mx/my)); // http://www.magnetic-declination.com/ // http://davidegironi.blogspot.de/2013/01/avr-atmega-hmc5883l-magnetometer-lib-01.html const float declination = 3.0f / 180.0f * M_PI; // GERMANY! const float curHeading = - tmp + declination; float resHeading; bool stable = true; // calculate standard-deviation within a certain timeframe stdDev.add(ts, curHeading); // if the standard-deviation is too high, // the compass is considered unstable if (numMagReadings < 250 || stdDev.get() > 0.30) { resHeading = NAN; stable = false; } else { resHeading = curHeading; stable = true; } #ifdef WITH_DEBUG_PLOT plot.add(ts, curHeading, stable, mag, curMag); #endif // done return resHeading; } }; #endif // INDOOR_IMU_COMPASSDETECTION_H