void InertiaSensorFilter::update(OrientationData& orientationData,
        const InertiaSensorData& theInertiaSensorData,
        const SensorData& theSensorData,
        const RobotModel& theRobotModel,
        const FrameInfo& theFrameInfo,
        const MotionInfo& theMotionInfo,
        const WalkingEngineOutput& theWalkingEngineOutput)
{
//  MODIFY("module:InertiaSensorFilter:parameters", p);
//
//  DECLARE_PLOT("module:InertiaSensorFilter:expectedGyroX");
//  DECLARE_PLOT("module:InertiaSensorFilter:gyroX");
//  DECLARE_PLOT("module:InertiaSensorFilter:expectedGyroY");
//  DECLARE_PLOT("module:InertiaSensorFilter:gyroY");
//  DECLARE_PLOT("module:InertiaSensorFilter:expectedAccX");
//  DECLARE_PLOT("module:InertiaSensorFilter:accX");
//  DECLARE_PLOT("module:InertiaSensorFilter:expectedAccY");
//  DECLARE_PLOT("module:InertiaSensorFilter:accY");
//  DECLARE_PLOT("module:InertiaSensorFilter:expectedAccZ");
//  DECLARE_PLOT("module:InertiaSensorFilter:accZ");

  // check whether the filter shall be reset
  if(!lastTime || theFrameInfo.time <= lastTime)
  {
    if(theFrameInfo.time == lastTime)
      return; // weird log file replaying?
        x = State<>();
        cov = p.processCov;

        lastLeftFoot = lastRightFoot = Pose3D();
        lastTime = theFrameInfo.time - (unsigned int)(theFrameInfo.cycleTime * 1000.f);
  }

  // get foot positions
  const Pose3D& leftFoot(theRobotModel.limbs[MassCalibration::footLeft]);
  const Pose3D& rightFoot(theRobotModel.limbs[MassCalibration::footRight]);
  const Pose3D leftFootInvert(leftFoot.invert());
  const Pose3D rightFootInvert(rightFoot.invert());

  // calculate rotation and position offset using the robot model (joint data)
  const Pose3D leftOffset(lastLeftFoot.translation.z != 0.f ? Pose3D(lastLeftFoot).conc(leftFootInvert) : Pose3D());
  const Pose3D rightOffset(lastRightFoot.translation.z != 0.f ? Pose3D(lastRightFoot).conc(rightFootInvert) : Pose3D());

  // detect the foot that is on ground
  bool useLeft = true;
  if(theMotionInfo.motion == MotionRequest::walk && theWalkingEngineOutput.speed.translation.x != 0)
    useLeft = theWalkingEngineOutput.speed.translation.x > 0 ?
              (leftOffset.translation.x > rightOffset.translation.x) :
              (leftOffset.translation.x < rightOffset.translation.x);
  else
  {
    Pose3D left(x.rotation);
    Pose3D right(x.rotation);
    left.conc(leftFoot);
    right.conc(rightFoot);
    useLeft = left.translation.z < right.translation.z;
  }

  // calculate velocity
  Vector3<> calcVelocity, lastCalcVelocity;
  float timeScale = 1.f / (float(theFrameInfo.time - lastTime) * 0.001f);
  calcVelocity = useLeft ? leftOffset.translation : rightOffset.translation;
  calcVelocity *= timeScale * 0.001f; // => m/s

  // update the filter
  timeScale = float(theFrameInfo.time - lastTime) * 0.001f;
  predict(theInertiaSensorData.gyro.x != InertiaSensorData::off ?
          RotationMatrix(Vector3<>(theInertiaSensorData.gyro.x * timeScale, theInertiaSensorData.gyro.y * timeScale, 0)) :
          (useLeft ? leftOffset.rotation :  rightOffset.rotation));

  // insert calculated rotation
  if(theInertiaSensorData.acc.x != InertiaSensorData::off)
    safeRawAngle = Vector2<>(theSensorData.data[SensorData::angleX], theSensorData.data[SensorData::angleY]);
  if((theMotionInfo.motion == MotionRequest::walk || theMotionInfo.motion == MotionRequest::stand ||
      (theMotionInfo.motion == MotionRequest::specialAction && theMotionInfo.specialActionRequest.specialAction == SpecialActionRequest::sitDownKeeper)) &&
     fabs(safeRawAngle.x) < p.calculatedAccLimit.x && fabs(safeRawAngle.y) < p.calculatedAccLimit.y)
  {
    const RotationMatrix& usedRotation(useLeft ? leftFootInvert.rotation : rightFootInvert.rotation);
    RotationMatrix calculatedRotation(Vector3<>(
                                        atan2f(usedRotation.c1.z, usedRotation.c2.z),
                                        atan2f(-usedRotation.c0.z, usedRotation.c2.z), 0.f));
    Vector3<> accGravOnly(calculatedRotation.c0.z, calculatedRotation.c1.z, calculatedRotation.c2.z);
    accGravOnly *= -9.80665f;
    readingUpdate(accGravOnly);
  }
  else // insert acceleration sensor values
  {
    if(theInertiaSensorData.acc.x != InertiaSensorData::off)
      readingUpdate(theInertiaSensorData.acc);
  }

  // fill the representation
  orientationData.orientation = Vector2<>(
                                  atan2f(x.rotation.c1.z, x.rotation.c2.z),
                                  atan2f(-x.rotation.c0.z, x.rotation.c2.z));
  // this removes any kind of z-rotation from internal rotation
  if(orientationData.orientation.squareAbs() < 0.04f * 0.04f)
    x.rotation = RotationMatrix(Vector3<>(orientationData.orientation.x, orientationData.orientation.y, 0.f));
  orientationData.velocity = calcVelocity;

  // store some data for the next iteration
  lastLeftFoot = leftFoot;
  lastRightFoot = rightFoot;
  lastTime = theFrameInfo.time;

  // plots
//  PLOT("module:InertiaSensorFilter:orientationX", orientationData.orientation.x);
//  PLOT("module:InertiaSensorFilter:orientationY", orientationData.orientation.y);
//  PLOT("module:InertiaSensorFilter:velocityX", orientationData.velocity.x);
//  PLOT("module:InertiaSensorFilter:velocityY", orientationData.velocity.y);
//  PLOT("module:InertiaSensorFilter:velocityZ", orientationData.velocity.z);
}
void InertialDataFilter::update(InertialData& inertialData)
{
  DECLARE_PLOT("module:InertialDataFilter:expectedAccX");
  DECLARE_PLOT("module:InertialDataFilter:accX");
  DECLARE_PLOT("module:InertialDataFilter:expectedAccY");
  DECLARE_PLOT("module:InertialDataFilter:accY");
  DECLARE_PLOT("module:InertialDataFilter:expectedAccZ");
  DECLARE_PLOT("module:InertialDataFilter:accZ");

  // check whether the filter shall be reset
  if(!lastTime || theFrameInfo.time <= lastTime)
  {
    if(theFrameInfo.time == lastTime)
      return; // weird log file replaying?
    reset();
  }

  if(theMotionInfo.motion == MotionRequest::specialAction && theMotionInfo.specialActionRequest.specialAction == SpecialActionRequest::playDead)
  {
    reset();
  }

  // get foot positions
  Pose3f leftFoot = theRobotModel.limbs[Limbs::footLeft];
  Pose3f rightFoot = theRobotModel.limbs[Limbs::footRight];
  leftFoot.translate(0.f, 0.f, -theRobotDimensions.footHeight);
  rightFoot.translate(0.f, 0.f, -theRobotDimensions.footHeight);
  const Pose3f leftFootInvert(leftFoot.inverse());
  const Pose3f rightFootInvert(rightFoot.inverse());

  // calculate rotation and position offset using the robot model (joint data)
  const Pose3f leftOffset(lastLeftFoot.translation.z() != 0.f ? Pose3f(lastLeftFoot).conc(leftFootInvert) : Pose3f());
  const Pose3f rightOffset(lastRightFoot.translation.z() != 0.f ? Pose3f(lastRightFoot).conc(rightFootInvert) : Pose3f());

  // detect the foot that is on ground
  bool useLeft = true;
  if(theMotionInfo.motion == MotionRequest::walk && theWalkingEngineOutput.speed.translation.x() != 0)
  {
    useLeft = theWalkingEngineOutput.speed.translation.x() > 0 ?
              (leftOffset.translation.x() > rightOffset.translation.x()) :
              (leftOffset.translation.x() < rightOffset.translation.x());
  }
  else
  {
    Pose3f left(mean.rotation);
    Pose3f right(mean.rotation);
    left.conc(leftFoot);
    right.conc(rightFoot);
    useLeft = left.translation.z() < right.translation.z();
  }

  // update the filter
  const float timeScale = theFrameInfo.cycleTime;
  predict(RotationMatrix::fromEulerAngles(theInertialSensorData.gyro.x() * timeScale,
                                          theInertialSensorData.gyro.y() * timeScale, 0));

  // insert calculated rotation
  safeRawAngle = theInertialSensorData.angle.head<2>().cast<float>();
  bool useFeet = true;
  MODIFY("module:InertialDataFilter:useFeet", useFeet);
  if(useFeet &&
     (theMotionInfo.motion == MotionRequest::walk || theMotionInfo.motion == MotionRequest::stand ||
      (theMotionInfo.motion == MotionRequest::specialAction && theMotionInfo.specialActionRequest.specialAction == SpecialActionRequest::standHigh)) &&
     std::abs(safeRawAngle.x()) < calculatedAccLimit.x() && std::abs(safeRawAngle.y()) < calculatedAccLimit.y())
  {
    const RotationMatrix& usedRotation(useLeft ? leftFootInvert.rotation : rightFootInvert.rotation);
    Vector3f accGravOnly(usedRotation.col(0).z(), usedRotation.col(1).z(), usedRotation.col(2).z());
    accGravOnly *= Constants::g_1000;
    readingUpdate(accGravOnly);
  }
  else // insert acceleration sensor values
    readingUpdate(theInertialSensorData.acc);

  // fill the representation
  inertialData.angle = Vector2a(std::atan2(mean.rotation.col(1).z(), mean.rotation.col(2).z()), std::atan2(-mean.rotation.col(0).z(), mean.rotation.col(2).z()));

  inertialData.acc = theInertialSensorData.acc;
  inertialData.gyro = theInertialSensorData.gyro;

  inertialData.orientation = Rotation::removeZRotation(Quaternionf(mean.rotation));

  // this  keeps the rotation matrix orthogonal
  mean.rotation.normalize();

  // store some data for the next iteration
  lastLeftFoot = leftFoot;
  lastRightFoot = rightFoot;
  lastTime = theFrameInfo.time;

  // plots
  Vector3f angleAxisVec = Rotation::AngleAxis::pack(AngleAxisf(inertialData.orientation));
  PLOT("module:InertialDataFilter:angleX", toDegrees(angleAxisVec.x()));
  PLOT("module:InertialDataFilter:angleY", toDegrees(angleAxisVec.y()));
  PLOT("module:InertialDataFilter:angleZ", toDegrees(angleAxisVec.z()));
  PLOT("module:InertialDataFilter:unfilteredAngleX", theInertialSensorData.angle.x().toDegrees());
  PLOT("module:InertialDataFilter:unfilteredAngleY", theInertialSensorData.angle.y().toDegrees());

  angleAxisVec = Rotation::AngleAxis::pack(AngleAxisf(mean.rotation));
  PLOT("module:InertialDataFilter:interlanAngleX", toDegrees(angleAxisVec.x()));
  PLOT("module:InertialDataFilter:interlanAngleY", toDegrees(angleAxisVec.y()));
  PLOT("module:InertialDataFilter:interlanAngleZ", toDegrees(angleAxisVec.z()));
}