diff --git a/src/capture_base.h b/src/capture_base.h
index cbc052bb8765f002ac6e6fe49ba14bc986e3bfef..dca78846c8f83d0cfa13e155d7c7bc1c3f9f7360 100644
--- a/src/capture_base.h
+++ b/src/capture_base.h
@@ -22,6 +22,7 @@ class CaptureBase : public NodeBase, public std::enable_shared_from_this<Capture
     private:
         FrameBaseWPtr   frame_ptr_;
         FeatureBaseList feature_list_;
+        ConstraintBaseList constrained_by_list_;
         SensorBaseWPtr  sensor_ptr_; ///< Pointer to sensor
         // Deal with sensors with dynamic extrinsics (check dynamic_extrinsic_ in SensorBase)
         std::vector<StateBlockPtr> state_block_vec_; ///< vector of state blocks, in the order P, O, intrinsic.
@@ -47,6 +48,9 @@ class CaptureBase : public NodeBase, public std::enable_shared_from_this<Capture
         virtual ~CaptureBase();
         void remove();
 
+        // Type
+        virtual bool isMotion() const { return false; }
+
         unsigned int id();
         TimeStamp getTimeStamp() const;
         void setTimeStamp(const TimeStamp& _ts);
@@ -67,6 +71,11 @@ class CaptureBase : public NodeBase, public std::enable_shared_from_this<Capture
         SensorBasePtr getSensorPtr() const;
         virtual void setSensorPtr(const SensorBasePtr sensor_ptr);
 
+        // constrained by
+        virtual ConstraintBasePtr addConstrainedBy(ConstraintBasePtr _ctr_ptr);
+        unsigned int getHits() const;
+        ConstraintBaseList& getConstrainedByList();
+
         // State blocks
         const std::vector<StateBlockPtr>& getStateBlockVec() const;
         std::vector<StateBlockPtr>& getStateBlockVec();
@@ -217,6 +226,24 @@ inline void CaptureBase::getConstraintList(ConstraintBaseList& _ctr_list)
         f_ptr->getConstraintList(_ctr_list);
 }
 
+inline ConstraintBasePtr CaptureBase::addConstrainedBy(ConstraintBasePtr _ctr_ptr)
+{
+    constrained_by_list_.push_back(_ctr_ptr);
+    _ctr_ptr->setCaptureOtherPtr( shared_from_this() );
+    return _ctr_ptr;
+}
+
+inline unsigned int CaptureBase::getHits() const
+{
+    return constrained_by_list_.size();
+}
+
+inline ConstraintBaseList& CaptureBase::getConstrainedByList()
+{
+    return constrained_by_list_;
+}
+
+
 inline TimeStamp CaptureBase::getTimeStamp() const
 {
     return time_stamp_;
diff --git a/src/capture_motion.h b/src/capture_motion.h
index 68c920792c5fcdb7718a3c0161087bbaac284834..65b7fd4f33553ab89492ba1d8429fc585f1c62a4 100644
--- a/src/capture_motion.h
+++ b/src/capture_motion.h
@@ -63,6 +63,9 @@ class CaptureMotion : public CaptureBase
 
         virtual ~CaptureMotion();
 
+        // Type
+        virtual bool isMotion() const override { return true; }
+
         // Data
         const Eigen::VectorXs& getData() const;
         const Eigen::MatrixXs& getDataCovariance() const;
diff --git a/src/capture_odom_3D.cpp b/src/capture_odom_3D.cpp
index 666f3e58f39f6d82b857203e6c9a207513da668e..232f088ffa9f639e089a5c154342217a5eb80d52 100644
--- a/src/capture_odom_3D.cpp
+++ b/src/capture_odom_3D.cpp
@@ -38,7 +38,7 @@ Eigen::VectorXs CaptureOdom3D::correctDelta(const VectorXs& _delta, const Vector
 {
     VectorXs delta(7);
     delta.head(3) = _delta.head(3) + _delta_error.head(3);
-    delta.tail(4) = (Quaternions(_delta.head(4).data()) * exp_q(_delta_error.tail(3))).coeffs();
+    delta.tail(4) = (Quaternions(_delta.data()+3) * exp_q(_delta_error.tail(3))).coeffs();
     return delta;
 }
 
diff --git a/src/constraint_imu.h b/src/constraint_imu.h
index 197ba15d36fd288d2a39c9fd3f69d5e9595b9e01..7948045a05c805c54c4130f4f2d43edd00c953d1 100644
--- a/src/constraint_imu.h
+++ b/src/constraint_imu.h
@@ -139,8 +139,39 @@ class ConstraintIMU : public ConstraintAutodiff<ConstraintIMU, 15, 3, 4, 3, 6, 3
          */ 
         const Eigen::Matrix3s sqrt_A_r_dt_inv;
         const Eigen::Matrix3s sqrt_W_r_dt_inv;
+
+
+    private:
+        template<typename D>
+        void print(const std::string& name, const Eigen::MatrixBase<D>& mat) const;
+//        template<typename T, int R, int C>
+//        void print(const std::string& name, const Eigen::Matrix<T, R, C>& mat) const;
+        template<int R, int C>
+        void print(const std::string& name, const Matrix<Scalar, R, C>& mat) const;
 };
 
+template<typename D>
+inline void ConstraintIMU::print(const std::string& name, const Eigen::MatrixBase<D>& mat) const {}
+//template<typename T, int R, int C>
+//inline void ConstraintIMU::print(const std::string& name, const Eigen::Matrix<T, R, C>& mat) const {}
+template<int R, int C>
+inline void ConstraintIMU::print(const std::string& name, const Matrix<Scalar, R, C>& mat) const
+{
+    if (mat.cols() == 1)
+    {
+        WOLF_TRACE(name, ": ", mat.transpose());
+    }
+    else if (mat.rows() == 1)
+    {
+        WOLF_TRACE(name, ": ", mat);
+    }
+    else
+    {
+        WOLF_TRACE(name, ":\n", mat);
+    }
+}
+
+
 inline ConstraintIMU::ConstraintIMU(const FeatureIMUPtr&    _ftr_ptr,
                                     const CaptureIMUPtr&    _cap_origin_ptr,
                                     const ProcessorBasePtr& _processor_ptr,
@@ -163,9 +194,9 @@ inline ConstraintIMU::ConstraintIMU(const FeatureIMUPtr&    _ftr_ptr,
                         _ftr_ptr->getFramePtr()->getOPtr(),
                         _ftr_ptr->getFramePtr()->getVPtr(),
                         _ftr_ptr->getCapturePtr()->getSensorIntrinsicPtr()),
-        dp_preint_(_ftr_ptr->getDpPreint()), // dp, dv, dq at preintegration time
-        dq_preint_(_ftr_ptr->getDqPreint()),
-        dv_preint_(_ftr_ptr->getDvPreint()),
+        dp_preint_(_ftr_ptr->getMeasurement().head<3>()), // dp, dv, dq at preintegration time
+        dq_preint_(_ftr_ptr->getMeasurement().data()+3),
+        dv_preint_(_ftr_ptr->getMeasurement().tail<3>()),
         acc_bias_preint_(_ftr_ptr->getAccBiasPreint()), // state biases at preintegration time
         gyro_bias_preint_(_ftr_ptr->getGyroBiasPreint()),
         dDp_dab_(_ftr_ptr->getJacobianBias().block(0,0,3,3)), // Jacs of dp dv dq wrt biases
@@ -180,8 +211,39 @@ inline ConstraintIMU::ConstraintIMU(const FeatureIMUPtr&    _ftr_ptr,
         sqrt_W_r_dt_inv((Eigen::Matrix3s::Identity() * wb_rate_stdev_ * sqrt(dt_)).inverse())
 {
     setType("IMU");
+
+//    WOLF_TRACE("Constr IMU  (f", _ftr_ptr->id(),
+//               " C", _ftr_ptr->getCapturePtr()->id(),
+//               " F", _ftr_ptr->getCapturePtr()->getFramePtr()->id(),
+//               ") (Co", _cap_origin_ptr->id(),
+//               " Fo", _cap_origin_ptr->getFramePtr()->id(), ")");
+//
+//    WOLF_TRACE("dt: ", dt_);
+//
+//    WOLF_TRACE("delta preint: ", std::static_pointer_cast<CaptureMotion>(_ftr_ptr->getCapturePtr())->getDeltaPreint().transpose());
+////    WOLF_TRACE("Dp preint : ", dp_preint_.transpose()); // OK
+////    WOLF_TRACE("Dq preint : ", dq_preint_.coeffs().transpose()); // OK
+////    WOLF_TRACE("Dv preint : ", dv_preint_.transpose()); // OK
+//
+//    WOLF_TRACE("bias: ", std::static_pointer_cast<CaptureMotion>(_ftr_ptr->getCapturePtr())->getCalibrationPreint().transpose());
+////    WOLF_TRACE("bias acc : ", acc_bias_preint_.transpose()); // OK
+////    WOLF_TRACE("bias gyro: ", gyro_bias_preint_.transpose()); // OK
+//
+//    WOLF_TRACE("Jac bias : \n", std::static_pointer_cast<CaptureMotion>(_ftr_ptr->getCapturePtr())->getJacobianCalib());
+////    WOLF_TRACE("jac Dp_ab: \n", dDp_dab_); // OK
+////    WOLF_TRACE("jac Dv_ab: \n", dDv_dab_); // OK
+////    WOLF_TRACE("jac Dp_wb: \n", dDp_dwb_); // OK
+////    WOLF_TRACE("jac Dq_wb: \n", dDq_dwb_); // OK
+////    WOLF_TRACE("jac Dv_wb: \n", dDv_dwb_); // OK
+//
+//    WOLF_TRACE("Omega_delta.sqrt: \n", _ftr_ptr->getMeasurementSquareRootInformationUpper());
+//    WOLF_TRACE("Omega_acc.sqrt: \n", sqrt_A_r_dt_inv);
+//    WOLF_TRACE("Omega_gyro.sqrt: \n", sqrt_W_r_dt_inv);
+
 }
 
+
+
 template<typename T>
 inline bool ConstraintIMU::operator ()(const T* const _p1,
                                        const T* const _q1,
@@ -228,6 +290,32 @@ inline bool ConstraintIMU::residual(const Eigen::MatrixBase<D1> &       _p1,
                                     const Eigen::MatrixBase<D1> &       _wb2,
                                     Eigen::MatrixBase<D3> &             _res) const
 {
+
+    /* Two methods are possible (select with #define below this note)
+     *
+     *  Common computations:
+     *    D_exp = between(x1,x2,dt)     // Predict delta from states
+     *    step  = J * (b - b_preint)    // compute the delta correction step due to bias change
+     *
+     *  Method 1:
+     *    corr  = plus(D_preint, step)  // correct the pre-integrated delta with correction step due to bias change
+     *    err   = diff(D_exp, D_corr)   // compare against expected delta
+     *    res   = W.sqrt * err
+     *
+     *  results in:
+     *    res   = W.sqrt * ( diff ( D_exp, (D_preint * retract(J * (b - b_preint) ) ) )
+     *
+     *  Method 2:
+     *    diff  = diff(D_preint, D_exp) // compare pre-integrated against expected delta
+     *    err   = diff - step           // the difference should be the correction step due to bias change
+     *    res   = W.sqrt * err
+     *
+     *  results in :
+     *    res   = W.sqrt * ( diff ( D_preint , D_exp ) ) - J * (b - b_preint)
+     */
+#define METHOD_1
+
+
     //needed typedefs
     typedef typename D2::Scalar T;
 
@@ -240,56 +328,139 @@ inline bool ConstraintIMU::residual(const Eigen::MatrixBase<D1> &       _p1,
 
     imu::betweenStates(_p1, _q1, _v1, _p2, _q2, _v2, (T)dt_, dp_exp, dq_exp, dv_exp);
 
+
     // 2. Corrected integrated delta: delta_corr = delta_preint (+) J_bias * (bias_current - bias_preint)
+
+    // 2.a. Compute the delta step in tangent space:   step = J_bias * (bias - bias_preint)
+    Eigen::Matrix<T, 9, 1> d_step;
+
+    d_step.block(0,0,3,1) = dDp_dab_.cast<T>() * (_ab1 - acc_bias_preint_ .cast<T>()) + dDp_dwb_.cast<T>() * (_wb1 - gyro_bias_preint_.cast<T>());
+    d_step.block(3,0,3,1) =                                                             dDq_dwb_.cast<T>() * (_wb1 - gyro_bias_preint_.cast<T>());
+    d_step.block(6,0,3,1) = dDv_dab_.cast<T>() * (_ab1 - acc_bias_preint_ .cast<T>()) + dDv_dwb_.cast<T>() * (_wb1 - gyro_bias_preint_.cast<T>());
+
+#ifdef METHOD_1 // method 1
+    Eigen::Matrix<T, 3, 1> dp_step = d_step.block(0,0,3,1);
+    Eigen::Matrix<T, 3, 1> do_step = d_step.block(3,0,3,1);
+    Eigen::Matrix<T, 3, 1> dv_step = d_step.block(6,0,3,1);
+
+    // 2.b. Add the correction step to the preintegrated delta:    delta_cor = delta_preint (+) step
     Eigen::Matrix<T,3,1> dp_correct;
     Eigen::Quaternion<T> dq_correct;
     Eigen::Matrix<T,3,1> dv_correct;
 
-    imu::plus(dp_preint_.cast<T>(),
-              dq_preint_.cast<T>(),
-              dv_preint_.cast<T>(),
-              dDp_dab_.cast<T>() * (_ab1 - acc_bias_preint_ .cast<T>()) + dDp_dwb_.cast<T>() * (_wb1 - gyro_bias_preint_.cast<T>()),
-              dDq_dwb_.cast<T>() * (_wb1 - gyro_bias_preint_.cast<T>()),
-              dDv_dab_.cast<T>() * (_ab1 - acc_bias_preint_ .cast<T>()) + dDv_dwb_.cast<T>() * (_wb1 - gyro_bias_preint_.cast<T>()),
-              dp_correct,
-              dq_correct,
-              dv_correct);
-
-    // 3. Delta error in minimal form: d_min = log(delta_pred (-) delta_corr)
+    imu::plus(dp_preint_.cast<T>(), dq_preint_.cast<T>(), dv_preint_.cast<T>(),
+              dp_step, do_step, dv_step,
+              dp_correct, dq_correct, dv_correct);
+
+
+    // 3. Delta error in minimal form: d_min = lift(delta_pred (-) delta_corr)
     // Note the Dt here is zero because it's the delta-time between the same time stamps!
-    Eigen::Matrix<T, 9, 1> dpov_error;
-    Eigen::Map<Eigen::Matrix<T, 3, 1> >   dp_error(dpov_error.data()    );
-    Eigen::Map<Eigen::Matrix<T, 3, 1> >   do_error(dpov_error.data() + 3);
-    Eigen::Map<Eigen::Matrix<T, 3, 1> >   dv_error(dpov_error.data() + 6);
+    Eigen::Matrix<T, 9, 1> d_error;
+    Eigen::Map<Eigen::Matrix<T, 3, 1> >   dp_error(d_error.data()    );
+    Eigen::Map<Eigen::Matrix<T, 3, 1> >   do_error(d_error.data() + 3);
+    Eigen::Map<Eigen::Matrix<T, 3, 1> >   dv_error(d_error.data() + 6);
+
+    Eigen::Matrix<T, 3, 3> J_do_dq1, J_do_dq2;
+    Eigen::Matrix<T, 9, 9> J_err_corr;
 
+//#define WITH_JAC
+#ifdef WITH_JAC
+    imu::diff(dp_exp, dq_exp, dv_exp, dp_correct, dq_correct, dv_correct, dp_error, do_error, dv_error, J_do_dq1, J_do_dq2);
+
+    J_err_corr.setIdentity();
+    J_err_corr.block(3,3,3,3) = J_do_dq2;
+
+    // 4. Residuals are the weighted errors
+    _res.head(9)       = J_err_corr.inverse().transpose() * getMeasurementSquareRootInformationTransposed().cast<T>() * d_error;
+#else
     imu::diff(dp_exp, dq_exp, dv_exp, dp_correct, dq_correct, dv_correct, dp_error, do_error, dv_error);
+    _res.head(9)       = getMeasurementSquareRootInformationTransposed().cast<T>() * d_error;
+#endif
+
+#else // method 2
+
+    Eigen::Matrix<T, 9, 1> d_diff;
+    Eigen::Map<Eigen::Matrix<T, 3, 1> >   dp_diff(d_diff.data()    );
+    Eigen::Map<Eigen::Matrix<T, 3, 1> >   do_diff(d_diff.data() + 3);
+    Eigen::Map<Eigen::Matrix<T, 3, 1> >   dv_diff(d_diff.data() + 6);
+
+    imu::diff(dp_preint_.cast<T>(), dq_preint_.cast<T>(), dv_preint_.cast<T>(),
+              dp_exp, dq_exp, dv_exp,
+              dp_diff, do_diff, dv_diff);
+
+    Eigen::Matrix<T, 9, 1> d_error;
+    d_error << d_diff - d_step;
+
+    // 4. Residuals are the weighted errors
+    _res.head(9)       = getMeasurementSquareRootInformationTransposed().cast<T>() * d_error;
+
+#endif
 
     // Errors between biases
     Eigen::Matrix<T,3,1> ab_error(_ab1 - _ab2); // KF1.bias - KF2.bias
     Eigen::Matrix<T,3,1> wb_error(_wb1 - _wb2);
 
     // 4. Residuals are the weighted errors
-    _res.head(9)       = getMeasurementSquareRootInformationTransposed().cast<T>() * dpov_error;
     _res.segment(9,3)  = sqrt_A_r_dt_inv.cast<T>() * ab_error;
     _res.tail(3)       = sqrt_W_r_dt_inv.cast<T>() * wb_error;
 
+
+    /////////////////////////////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////////////////////////////
+#if 0
+    // print values -----------------------
+    Matrix<T, 10, 1> x1; x1 << _p1 , _q1.coeffs(), _v1;
+    Matrix<T, 10, 1> x2; x2 << _p2 , _q2.coeffs(), _v2;
+    print("x1 ", x1);
+    print("x2 ", x2);
+    Matrix<T, 6, 1> bp; bp << acc_bias_preint_.cast<T>(), gyro_bias_preint_.cast<T>();
+    Matrix<T, 6, 1> b1; b1 << _ab1, _wb1;
+    Matrix<T, 6, 1> b2; b2 << _ab2, _wb2;
+    print("bp ", bp);
+    print("b1 ", b1);
+    print("b2 ", b2);
+    Matrix<T, 10, 1> exp; exp << dp_exp , dq_exp.coeffs(), dv_exp;
+    print("D exp", exp);
+    Matrix<T, 10, 1> pre; pre << dp_preint_.cast<T>() , dq_preint_.cast<T>().coeffs(), dv_preint_.cast<T>();
+    print("D preint", pre);
+    Matrix<T, 9, 6> J_b_r0; J_b_r0.setZero();
+    J_b_r0.block(0,0,3,3) = dDp_dab_.cast<T>();
+    J_b_r0.block(0,3,3,3) = dDp_dwb_.cast<T>();
+    J_b_r0.block(3,3,3,3) = dDq_dwb_.cast<T>();
+    J_b_r0.block(6,0,3,3) = dDv_dab_.cast<T>();
+    J_b_r0.block(6,3,3,3) = dDv_dwb_.cast<T>();
+    print("J bias", J_b_r0);
+    print("D step", d_step);
+#ifndef METHOD_1
+    Matrix<T, 9, 1> dif; dif << dp_diff , do_diff, dv_diff;
+    print("D diff", dif);
+#endif
+    print("D err", d_error);
+    WOLF_TRACE("-----------------------------------------")
+#endif
+    /////////////////////////////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////////////////////////////
+
+
+
     return true;
 }
 
+
 inline Eigen::VectorXs ConstraintIMU::expectation() const
 {
     FrameBasePtr frm_current = getFeaturePtr()->getFramePtr();
     FrameBasePtr frm_previous = getFrameOtherPtr();
 
     //get information on current_frame in the ConstraintIMU
-    const Eigen::Vector3s frame_current_pos = frm_current->getPPtr()->getState();
-    const Eigen::Quaternions frame_current_ori(frm_current->getOPtr()->getState().data());
-    const Eigen::Vector3s frame_current_vel = frm_current->getVPtr()->getState();
+    const Eigen::Vector3s frame_current_pos    = (frm_current->getPPtr()->getState());
+    const Eigen::Quaternions frame_current_ori   (frm_current->getOPtr()->getState().data());
+    const Eigen::Vector3s frame_current_vel    = (frm_current->getVPtr()->getState());
 
     // get info on previous frame in the ConstraintIMU
-    const Eigen::Vector3s frame_previous_pos = (frm_previous->getPPtr()->getState());
+    const Eigen::Vector3s frame_previous_pos   = (frm_previous->getPPtr()->getState());
     const Eigen::Quaternions frame_previous_ori  (frm_previous->getOPtr()->getState().data());
-    const Eigen::Vector3s frame_previous_vel = (frm_previous->getVPtr()->getState());
+    const Eigen::Vector3s frame_previous_vel   = (frm_previous->getVPtr()->getState());
 
     // Define results vector and Map bits over it
     Eigen::Matrix<wolf::Scalar, 10, 1> exp;
@@ -326,6 +497,8 @@ inline JacobianMethod ConstraintIMU::getJacobianMethod() const
 }
 
 
+
+
 } // namespace wolf
 
 #endif
diff --git a/src/constraint_odom_3D.h b/src/constraint_odom_3D.h
index e1b6ac1e9ad1a253088e075eef689ea1ae416d9b..851433fab2fffb002f41663cd2a4319f007fcb77 100644
--- a/src/constraint_odom_3D.h
+++ b/src/constraint_odom_3D.h
@@ -93,6 +93,14 @@ inline ConstraintOdom3D::ConstraintOdom3D(const FeatureBasePtr& _ftr_current_ptr
                                         _frame_past_ptr->getOPtr()) // past frame Q
 {
     setType("ODOM 3D");
+
+//    WOLF_TRACE("Constr ODOM 3D  (f", _ftr_current_ptr->id(),
+//               " F", _ftr_current_ptr->getCapturePtr()->getFramePtr()->id(),
+//               ") (Fo", _frame_past_ptr->id(), ")");
+//
+//    WOLF_TRACE("delta preint: ", _ftr_current_ptr->getMeasurement().transpose());
+//
+//    WOLF_TRACE("Omega_delta.sqrt: \n", _ftr_current_ptr->getMeasurementSquareRootInformationUpper());
     //
 }
 
diff --git a/src/feature_imu.cpp b/src/feature_imu.cpp
index d7ecf3b917dd2800f6bd2aba6d69ff1141607b93..b657039367682802ddfe7c285efa59e48a8c6b3a 100644
--- a/src/feature_imu.cpp
+++ b/src/feature_imu.cpp
@@ -2,39 +2,27 @@
 
 namespace wolf {
 
-FeatureIMU::FeatureIMU(const Eigen::VectorXs& _delta_preintegrated, const Eigen::MatrixXs& _delta_preintegrated_covariance) :
-    FeatureBase("IMU", _delta_preintegrated, _delta_preintegrated_covariance),
-    dp_preint_(_delta_preintegrated.head<3>()),dv_preint_(_delta_preintegrated.tail<3>()), dq_preint_(_delta_preintegrated.segment<4>(3))
-{
-    //std::cout << "New FeatureIMU: measurement " << _measurement.transpose() << std::endl << "covariance" << std::endl << _meas_covariance << std::endl;
-}
-
-FeatureIMU::FeatureIMU(const Eigen::VectorXs& _delta_preintegrated, const Eigen::MatrixXs& _delta_preintegrated_covariance, const Eigen::Vector3s& _acc_bias, const Eigen::Vector3s& _gyro_bias, const Eigen::Matrix<wolf::Scalar,9,6>& _dD_db_jacobians) :
+FeatureIMU::FeatureIMU(const Eigen::VectorXs& _delta_preintegrated,
+                       const Eigen::MatrixXs& _delta_preintegrated_covariance,
+                       const Eigen::Vector6s& _bias,
+                       const Eigen::Matrix<wolf::Scalar,9,6>& _dD_db_jacobians,
+                       CaptureMotionPtr _cap_imu_ptr) :
     FeatureBase("IMU", _delta_preintegrated, _delta_preintegrated_covariance),
-    dp_preint_(_delta_preintegrated.head<3>()),dv_preint_(_delta_preintegrated.tail<3>()), dq_preint_(_delta_preintegrated.segment<4>(3)),
-    acc_bias_preint_(_acc_bias), gyro_bias_preint_(_gyro_bias),
+    acc_bias_preint_(_bias.head<3>()),
+    gyro_bias_preint_(_bias.tail<3>()),
     jacobian_bias_(_dD_db_jacobians)
 {
-    //std::cout << "New FeatureIMU: measurement " << _measurement.transpose() << std::endl << "covariance" << std::endl << _meas_covariance << std::endl;
+    if (_cap_imu_ptr)
+        this->setCapturePtr(_cap_imu_ptr);
 }
 
-FeatureIMU::FeatureIMU(const Eigen::VectorXs& _delta_preintegrated,
-                       const Eigen::MatrixXs& _delta_preintegrated_covariance,
-                       const wolf::CaptureIMUPtr _cap_imu_ptr,
-                       const Eigen::Matrix<wolf::Scalar,9,6>& _dD_db_jacobians):
-        FeatureBase("IMU", _delta_preintegrated, _delta_preintegrated_covariance),
-        jacobian_bias_(_dD_db_jacobians)
+FeatureIMU::FeatureIMU(CaptureMotionPtr _cap_imu_ptr):
+        FeatureBase("IMU", _cap_imu_ptr->getDeltaPreint(), _cap_imu_ptr->getDeltaPreintCov()),
+        acc_bias_preint_ (_cap_imu_ptr->getCalibrationPreint().head<3>()),
+        gyro_bias_preint_(_cap_imu_ptr->getCalibrationPreint().tail<3>()),
+        jacobian_bias_(_cap_imu_ptr->getJacobianCalib())
 {
-    //TODO : SIZE ASSERTIONS : make sure _delta_preintegrated and _delta_preintegrated_covariance sizes are correct !
-
     this->setCapturePtr(_cap_imu_ptr);
-
-    dp_preint_ = _delta_preintegrated.head<3>();
-    dv_preint_ = _delta_preintegrated.tail<3>();
-    dq_preint_ = _delta_preintegrated.segment<4>(3);
-
-    acc_bias_preint_ = _cap_imu_ptr->getCalibrationPreint().head(3);
-    gyro_bias_preint_ = _cap_imu_ptr->getCalibrationPreint().tail(3);
 }
 
 FeatureIMU::~FeatureIMU()
diff --git a/src/feature_imu.h b/src/feature_imu.h
index 72b99eb47319ba08294e0e29ee14a3746d3b9c04..83a3e8c4dae7488f23e397a3f8e5de41a32eb87d 100644
--- a/src/feature_imu.h
+++ b/src/feature_imu.h
@@ -14,61 +14,39 @@ namespace wolf {
 //WOLF_PTR_TYPEDEFS(CaptureIMU);
 WOLF_PTR_TYPEDEFS(FeatureIMU);
 
-//class FeatureIMU
 class FeatureIMU : public FeatureBase
 {
     public:
 
-        /** \brief Constructor from capture pointer and measure
-         *
-         * \param _measurement the measurement
-         * \param _meas_covariance the noise of the measurement
-         *
-         */
-        FeatureIMU(const Eigen::VectorXs& _delta_preintegrated, const Eigen::MatrixXs& _delta_preintegrated_covariance);
 
-        /** \brief Constructor from capture pointer and measure
+        /** \brief Constructor from and measures
          *
          * \param _measurement the measurement
          * \param _meas_covariance the noise of the measurement
          * \param _dD_db_jacobians Jacobians of preintegrated delta wrt IMU biases
          * \param acc_bias accelerometer bias of origin frame
          * \param gyro_bias gyroscope bias of origin frame
-         *
+         * \param _cap_imu_ptr pointer to parent CaptureMotion
          */
         FeatureIMU(const Eigen::VectorXs& _delta_preintegrated,
                    const Eigen::MatrixXs& _delta_preintegrated_covariance,
-                   const Eigen::Vector3s& _acc_bias,
-                   const Eigen::Vector3s& _gyro_bias,
-                   const Eigen::Matrix<wolf::Scalar,9,6>& _dD_db_jacobians);
+                   const Eigen::Vector6s& _bias,
+                   const Eigen::Matrix<wolf::Scalar,9,6>& _dD_db_jacobians,
+                   CaptureMotionPtr _cap_imu_ptr = nullptr);
 
-        /** \brief Constructor from capture pointer and measure
-         *
-         * \param _measurement the measurement
-         * \param _meas_covariance the noise of the measurement
-         * \param _dD_db_jacobians Jacobians of preintegrated delta wrt IMU biases
-         * \param _cap_imu_ptr is the CaptureIMUPtr pointing to desired capture containing the origin frame
+        /** \brief Constructor from capture pointer
          *
+         * \param _cap_imu_ptr pointer to parent CaptureMotion
          */
-        FeatureIMU(const Eigen::VectorXs& _delta_preintegrated,
-                   const Eigen::MatrixXs& _delta_preintegrated_covariance,
-                   const wolf::CaptureIMUPtr _cap_imu_ptr,
-                   const Eigen::Matrix<wolf::Scalar,9,6>& _dD_db_jacobians);
+        FeatureIMU(CaptureMotionPtr _cap_imu_ptr);
 
         virtual ~FeatureIMU();
 
-        const Eigen::Vector3s&              getDpPreint()       const;
-        const Eigen::Quaternions&           getDqPreint()       const;
-        const Eigen::Vector3s&              getDvPreint()       const;
         const Eigen::Vector3s&              getAccBiasPreint()  const;
         const Eigen::Vector3s&              getGyroBiasPreint() const;
         const Eigen::Matrix<Scalar, 9, 6>&  getJacobianBias()   const;
 
     private:
-        /// Preintegrated delta
-        Eigen::Vector3s dp_preint_;
-        Eigen::Vector3s dv_preint_;
-        Eigen::Quaternions dq_preint_;
 
         // Used biases
         Eigen::Vector3s acc_bias_preint_;       ///< Acceleration bias used for delta preintegration
@@ -76,22 +54,10 @@ class FeatureIMU : public FeatureBase
 
         Eigen::Matrix<Scalar, 9, 6> jacobian_bias_;
 
+    public:
+      EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
 };
 
-inline const Eigen::Vector3s& FeatureIMU::getDpPreint() const
-{
-    return dp_preint_;
-}
-
-inline const Eigen::Quaternions& FeatureIMU::getDqPreint() const
-{
-    return dq_preint_;
-}
-
-inline const Eigen::Vector3s& FeatureIMU::getDvPreint() const
-{
-    return dv_preint_;
-}
 
 inline const Eigen::Vector3s& FeatureIMU::getAccBiasPreint() const
 {
diff --git a/src/imu_tools.h b/src/imu_tools.h
index 0e2ceff648556386d85cdeee3530137e7ccbcf6b..86900a8d6b80622366700201ab1e269015926576 100644
--- a/src/imu_tools.h
+++ b/src/imu_tools.h
@@ -79,6 +79,24 @@ inline void inverse(const MatrixBase<D1>& d,
     inverse(dp, dq, dv, dt, idp, idq, idv);
 }
 
+template<typename D1, typename D2, class T, typename D3>
+inline void inverse(const MatrixBase<D1>& d,
+                    T dt,
+                    MatrixBase<D2>& id,
+                    MatrixBase<D3>& Jac)
+{
+    MatrixSizeCheck<10, 1>::check(d);
+    MatrixSizeCheck<10, 1>::check(id);
+    MatrixSizeCheck<9, 9>::check(Jac);
+
+    Map<const Quaternion<typename D1::Scalar> >     dq   ( & d( 3 ) );
+
+    inverse(d, dt, id);
+
+    Jac.setIdentity();
+    Jac.block<3,3>(3,3) = -q2R(dq); // d(R.tr) / dR = - R.tr
+}
+
 template<typename D, class T>
 inline Matrix<typename D::Scalar, 10, 1> inverse(const MatrixBase<D>& d,
                                                  T dt)
@@ -215,6 +233,7 @@ inline void between(const MatrixBase<D1>& d1,
     between(dp1, dq1, dv1, dp2, dq2, dv2, dt, diff_p, diff_q, diff_v);
 }
 
+
 template<typename D1, typename D2, class T>
 inline Matrix<typename D1::Scalar, 10, 1> between(const MatrixBase<D1>& d1,
                                                   const MatrixBase<D2>& d2,
@@ -400,6 +419,18 @@ inline void diff(const MatrixBase<D1>& dp1, const QuaternionBase<D2>& dq1, const
         diff_v = dv2 - dv1;
 }
 
+template<typename D1, typename D2, typename D3, typename D4, typename D5, typename D6, typename D7, typename D8, typename D9, typename D10, typename D11>
+inline void diff(const MatrixBase<D1>& dp1, const QuaternionBase<D2>& dq1, const MatrixBase<D3>& dv1,
+                 const MatrixBase<D4>& dp2, const QuaternionBase<D5>& dq2, const MatrixBase<D6>& dv2,
+                 MatrixBase<D7>& diff_p, MatrixBase<D8>& diff_o, MatrixBase<D9>& diff_v ,
+                 MatrixBase<D10>& J_do_dq1, MatrixBase<D11>& J_do_dq2)
+{
+    diff(dp1, dq1, dv1, dp2, dq2, dv2, diff_p, diff_o, diff_v);
+
+    J_do_dq1    = - jac_SO3_left_inv(diff_o);
+    J_do_dq2    =   jac_SO3_right_inv(diff_o);
+}
+
 
 template<typename D1, typename D2, typename D3>
 inline void diff(const MatrixBase<D1>& d1,
@@ -419,6 +450,46 @@ inline void diff(const MatrixBase<D1>& d1,
     diff(dp1, dq1, dv1, dp2, dq2, dv2, diff_p, diff_o, diff_v);
 }
 
+template<typename D1, typename D2, typename D3, typename D4, typename D5>
+inline void diff(const MatrixBase<D1>& d1,
+                 const MatrixBase<D2>& d2,
+                 MatrixBase<D3>& dif,
+                 MatrixBase<D4>& J_diff_d1,
+                 MatrixBase<D5>& J_diff_d2)
+{
+    Map<const Matrix<typename D1::Scalar, 3, 1> >   dp1    ( & d1(0) );
+    Map<const Quaternion<typename D1::Scalar> >     dq1    ( & d1(3) );
+    Map<const Matrix<typename D1::Scalar, 3, 1> >   dv1    ( & d1(7) );
+    Map<const Matrix<typename D2::Scalar, 3, 1> >   dp2    ( & d2(0) );
+    Map<const Quaternion<typename D2::Scalar> >     dq2    ( & d2(3) );
+    Map<const Matrix<typename D2::Scalar, 3, 1> >   dv2    ( & d2(7) );
+    Map<Matrix<typename D3::Scalar, 3, 1> >         diff_p ( & dif(0) );
+    Map<Matrix<typename D3::Scalar, 3, 1> >         diff_o ( & dif(3) );
+    Map<Matrix<typename D3::Scalar, 3, 1> >         diff_v ( & dif(6) );
+
+    Matrix<typename D4::Scalar, 3, 3> J_do_dq1, J_do_dq2;
+
+    diff(dp1, dq1, dv1, dp2, dq2, dv2, diff_p, diff_o, diff_v, J_do_dq1, J_do_dq2);
+
+
+    /* d = diff(d1, d2) is
+     *   dp = dp2 - dp1
+     *   do = Log(dq1.conj * dq2)
+     *   dv = dv2 - dv1
+     *
+     * With trivial Jacobians for dp and dv, and:
+     *   J_do_dq1 = - J_l_inv(theta)
+     *   J_do_dq2 =   J_r_inv(theta)
+     */
+
+    J_diff_d1 = - Matrix<typename D4::Scalar, 9, 9>::Identity();// d(p2  - p1) / d(p1) = - Identity
+    J_diff_d1.block(3,3,3,3) = J_do_dq1;       // d(R1.tr*R2) / d(R1) = - J_l_inv(theta)
+
+    J_diff_d2.setIdentity();                                    // d(R1.tr*R2) / d(R2) =   Identity
+    J_diff_d2.block(3,3,3,3) = J_do_dq2;      // d(R1.tr*R2) / d(R1) =   J_r_inv(theta)
+
+}
+
 template<typename D1, typename D2>
 inline Matrix<typename D1::Scalar, 9, 1> diff(const MatrixBase<D1>& d1,
                                               const MatrixBase<D2>& d2)
@@ -429,6 +500,64 @@ inline Matrix<typename D1::Scalar, 9, 1> diff(const MatrixBase<D1>& d1,
 }
 
 
+template<typename D1, typename D2, typename D3, typename D4, typename D5>
+inline void body2delta(const MatrixBase<D1>& a,
+                       const MatrixBase<D2>& w,
+                       const typename D1::Scalar& dt,
+                       MatrixBase<D3>& dp,
+                       QuaternionBase<D4>& dq,
+                       MatrixBase<D5>& dv)
+{
+    MatrixSizeCheck<3,1>::check(a);
+    MatrixSizeCheck<3,1>::check(w);
+    MatrixSizeCheck<3,1>::check(dp);
+    MatrixSizeCheck<3,1>::check(dv);
+
+    dp = 0.5 * a * dt * dt;
+    dq = exp_q(w * dt);
+    dv =       a * dt;
+}
+
+template<typename D1>
+inline Matrix<typename D1::Scalar, 10, 1> body2delta(const MatrixBase<D1>& body,
+                                                     const typename D1::Scalar& dt)
+{
+    MatrixSizeCheck<6,1>::check(body);
+
+    typedef typename D1::Scalar T;
+
+    Matrix<T, 10, 1> delta;
+
+    Map< Matrix<T, 3, 1>> dp ( & delta(0) );
+    Map< Quaternion<T>>   dq ( & delta(3) );
+    Map< Matrix<T, 3, 1>> dv ( & delta(7) );
+
+    body2delta(body.block(0,0,3,1), body.block(3,0,3,1), dt, dp, dq, dv);
+
+    return delta;
+}
+
+template<typename D1, typename D2, typename D3>
+inline void body2delta(const MatrixBase<D1>& body,
+                       const typename D1::Scalar& dt,
+                       MatrixBase<D2>& delta,
+                       MatrixBase<D3>& jac_body)
+{
+    MatrixSizeCheck<6,1>::check(body);
+    MatrixSizeCheck<9,6>::check(jac_body);
+
+    typedef typename D1::Scalar T;
+
+    delta = body2delta(body, dt);
+
+    Matrix<T, 3, 1> w = body.block(3,0,3,1);
+
+    jac_body.setZero();
+    jac_body.block(0,0,3,3) = 0.5 * dt * dt * Matrix<T, 3, 3>::Identity();
+    jac_body.block(3,3,3,3) =            dt * jac_SO3_right(w * dt);
+    jac_body.block(6,0,3,3) =            dt * Matrix<T, 3, 3>::Identity();
+}
+
 } // namespace imu
 } // namespace wolf
 
diff --git a/src/logging.h b/src/logging.h
index eea75fcc522654ab8d302634c432e267249be632..ea437b0942df836954adcfe68fe2b54017c3e04f 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -109,7 +109,8 @@ inline Logger::Logger(const std::string& name) :
     // [thread num][hour:minutes:seconds.nanoseconds][log type] #log-content
     //set_pattern("[%t][%H:%M:%S.%F][%l] %v");
     // [log type][MM/DD/YY - hour:minutes:seconds.nanoseconds] #log-content
-    set_pattern("[%l][%x - %H:%M:%S.%F] %v");
+//    set_pattern("[%l][%x - %H:%M:%S.%F] %v");
+      set_pattern("[%l][%H:%M:%S] %v");
   else
     // Logging pattern is :
     // [logger name][thread num][hour:minutes:seconds.nanoseconds][log type] #log-content
@@ -137,7 +138,8 @@ inline Logger::Logger(std::string&& name) :
     // [thread num][hour:minutes:seconds.nanoseconds][log type] #log-content
     //set_pattern("[%t][%H:%M:%S.%F][%l] %v");
     // [log type][MM/DD/YY - hour:minutes:seconds.nanoseconds] #log-content
-    set_pattern("[%l][%x - %H:%M:%S.%F] %v");
+//    set_pattern("[%l][%x - %H:%M:%S.%F] %v");
+      set_pattern("[%l][%H:%M:%S] %v");
   else
     // Logging pattern is :
     // [logger name][thread num][hour:minutes:seconds.nanoseconds][log type] #log-content
diff --git a/src/problem.cpp b/src/problem.cpp
index 0a0f6d37e04ab664789e1463213dcbc71d9884a1..fc92b0f7c20e7ad1b1c829216615c6ed80f5fc22 100644
--- a/src/problem.cpp
+++ b/src/problem.cpp
@@ -690,7 +690,8 @@ void Problem::print(int depth, bool constr_by, bool metric, bool state_blocks)
         // Sensors
         for (auto S : getHardwarePtr()->getSensorList())
         {
-            cout << "  S" << S->id();
+            cout << "  S" << S->id() << " " << S->getType();
+            cout << (S->isExtrinsicDynamic() ? " [Dyn," : " [Sta,") << (S->isIntrinsicDynamic() ? "Dyn]" : "Sta]");
             if (depth < 2)
                 cout << " -- " << S->getProcessorList().size() << "p";
             cout << endl;
@@ -709,7 +710,7 @@ void Problem::print(int depth, bool constr_by, bool metric, bool state_blocks)
                 {
                     if (p->isMotion())
                     {
-                        std::cout << "    pm" << p->id() << std::endl;
+                        std::cout << "    pm" << p->id() << " " << p->getType() << endl;
                         ProcessorMotionPtr pm = std::static_pointer_cast<ProcessorMotion>(p);
                         if (pm->getOriginPtr())
                             cout << "      o: C" << pm->getOriginPtr()->id() << " - F"
@@ -724,7 +725,7 @@ void Problem::print(int depth, bool constr_by, bool metric, bool state_blocks)
                     {
                         try
                         {
-                            cout << "    pt" << p->id() << endl;
+                            cout << "    pt" << p->id() << " " << p->getType() << endl;
                             ProcessorTrackerPtr pt = std::static_pointer_cast<ProcessorTracker>(p);
                             if (pt->getOriginPtr())
                                 cout << "      o: C" << pt->getOriginPtr()->id() << " - F"
@@ -761,7 +762,7 @@ void Problem::print(int depth, bool constr_by, bool metric, bool state_blocks)
             cout << endl;
             if (metric)
             {
-                cout << (F->isFixed() ? "    Fixed" : "    Estim") << ", ts=" << std::setprecision(5)
+                cout << (F->isFixed() ? "    Fix" : "    Est") << ", ts=" << std::setprecision(5)
                         << F->getTimeStamp().get();
                 cout << ",\t x = ( " << std::setprecision(2) << F->getState().transpose() << ")";
                 cout << endl;
@@ -779,35 +780,68 @@ void Problem::print(int depth, bool constr_by, bool metric, bool state_blocks)
                 // Captures
                 for (auto C : F->getCaptureList())
                 {
-                    cout << "    C" << C->id();
+                    cout << "    C" << (C->isMotion() ? "M" : "") << C->id() << " " << C->getType();
                     if (C->getSensorPtr()) cout << " -> S" << C->getSensorPtr()->id();
                     else cout << " -> S-";
                     
-                    cout << " [" << C->getType() << ", ";
+                    cout << " [";
                     if(C->getSensorPtr() != nullptr)
+                        cout << (C->getSensorPtr()->isExtrinsicDynamic() ? "Dyn, ": "Sta, ");
+
+                    if(C->getSensorPtr() != nullptr)
+                        cout << (C->getSensorPtr()->isIntrinsicDynamic() ? "Dyn]" : "Sta]");
+
+                    cout << ((depth < 3) ? " -- " + std::to_string(C->getFeatureList().size()) + "f" : "");
+                    if (constr_by)
                     {
-                        if(C->getSensorPtr()->isExtrinsicDynamic()) cout << "Dyn, ";
-                        else cout << "Sta, ";
+                        cout << "  <-- ";
+                        for (auto cby : C->getConstrainedByList())
+                            cout << "c" << cby->id() << " \t";
                     }
+                    cout << endl;
 
-                    if(C->getSensorPtr() != nullptr)
+                    if (state_blocks)
                     {
-                        if(C->getSensorPtr()->isIntrinsicDynamic()) cout << "Dyn | ";
-                        else cout << "Sta | ";
+                        for(auto sb : C->getStateBlockVec())
+                        {
+                            cout << "      sb: ";
+                            if(sb != nullptr)
+                            {
+                                cout << (sb->isFixed() ? "Fix" : "Est");
+                                if (metric)
+                                    cout << std::setprecision(3) << " (" << sb->getState().transpose() << ")";
+                            }
+                            else cout << "nullptr ";
+                            cout << endl;
+                        }
                     }
 
-                    for(auto cpt_sb : C->getStateBlockVec())
-                    if(cpt_sb != nullptr) cout << std::setprecision(3) << cpt_sb->getState().transpose() << " ";
-                    else cout << "nullptr ";
-                    cout << " ]";
+                    if (C->isMotion() && metric)
+                    {
+                        try
+                        {
+                            CaptureMotionPtr CM = std::static_pointer_cast<CaptureMotion>(C);
+                            if ( CM->getCalibSize() > 0 && ! CM->getBuffer().get().empty())
+                            {
+                                cout << "      buffer size  :  " << CM->getBuffer().get().size() << endl;
+                                cout << "      delta preint : (" << CM->getDeltaPreint().transpose() << ")" << endl;
+                                cout << "      calib preint : (" << CM->getCalibrationPreint().transpose() << ")" << endl;
+                                cout << "      jacob preint : (" << CM->getJacobianCalib().row(0) << ")" << endl;
+                                cout << "      calib current: (" << CM->getCalibration().transpose() << ")" << endl;
+                                cout << "      delta correct: (" << CM->getDeltaCorrected(CM->getCalibration()).transpose() << ")" << endl;
+                            }
+                        }
+                        catch  (std::runtime_error& e)
+                        {
+                        }
+                    }
 
-                    cout << ((depth < 3) ? " -- " + std::to_string(C->getFeatureList().size()) + "f" : "") << endl;
                     if (depth >= 3)
                     {
                         // Features
                         for (auto f : C->getFeatureList())
                         {
-                            cout << "      f" << f->id() << ((depth < 4) ? " -- " + std::to_string(f->getConstraintList().size()) + "c  " : "");
+                            cout << "      f" << f->id() << " " << f->getType() << ((depth < 4) ? " -- " + std::to_string(f->getConstraintList().size()) + "c  " : "");
                             if (constr_by)
                             {
                                 cout << "  <--\t";
@@ -828,6 +862,8 @@ void Problem::print(int depth, bool constr_by, bool metric, bool state_blocks)
                                         cout << " A";
                                     if (c->getFrameOtherPtr() != nullptr)
                                         cout << " F" << c->getFrameOtherPtr()->id();
+                                    if (c->getCaptureOtherPtr() != nullptr)
+                                        cout << " C" << c->getCaptureOtherPtr()->id();
                                     if (c->getFeatureOtherPtr() != nullptr)
                                         cout << " f" << c->getFeatureOtherPtr()->id();
                                     if (c->getLandmarkOtherPtr() != nullptr)
@@ -847,7 +883,7 @@ void Problem::print(int depth, bool constr_by, bool metric, bool state_blocks)
         // Landmarks
         for (auto L : getMapPtr()->getLandmarkList())
         {
-            cout << "  L" << L->id();
+            cout << "  L" << L->id() << " " << L->getType();
             if (constr_by)
             {
                 cout << "\t<-- ";
@@ -993,6 +1029,17 @@ bool Problem::check(int verbose_level)
                 cout << endl;
                 cout << "      -> P  @ " << C->getProblem().get() << endl;
                 cout << "      -> F" << C->getFramePtr()->id() << " @ " << C->getFramePtr().get() << endl;
+                for (auto sb : C->getStateBlockVec())
+                {
+                    cout <<  "      sb @ " << sb.get();
+                    if (sb)
+                    {
+                        auto lp = sb->getLocalParametrizationPtr();
+                        if (lp)
+                            cout <<  " (lp @ " << lp.get() << ")";
+                    }
+                    cout << endl;
+                }
             }
             // check problem and frame pointers
             is_consistent = is_consistent && (C->getProblem().get() == P_raw);
@@ -1025,10 +1072,11 @@ bool Problem::check(int verbose_level)
                         cout << "        c" << c->id() << " @ " << c.get();
 
                     auto Fo = c->getFrameOtherPtr();
+                    auto Co = c->getCaptureOtherPtr();
                     auto fo = c->getFeatureOtherPtr();
                     auto Lo = c->getLandmarkOtherPtr();
 
-                    if ( !Fo && !fo && !Lo )    // case ABSOLUTE:
+                    if ( !Fo && !Co && !fo && !Lo )    // case ABSOLUTE:
                     {
                         if (verbose_level > 0)
                             cout << " --> Abs." << endl;
@@ -1052,6 +1100,24 @@ bool Problem::check(int verbose_level)
                         is_consistent = is_consistent && found;
                     }
 
+                    // find constrained_by pointer in constrained capture
+                    if ( Co )  // case CAPTURE:
+                    {
+                        if (verbose_level > 0)
+                            cout << " --> C" << Co->id() << " <- ";
+                        bool found = false;
+                        for (auto cby : Co->getConstrainedByList())
+                        {
+                            if (verbose_level > 0)
+                                cout << " c" << cby->id();
+                            found = found || (c == cby);
+                        }
+                        if (verbose_level > 0)
+                            cout << endl;
+                        // check constrained_by pointer in constrained frame
+                        is_consistent = is_consistent && found;
+                    }
+
                     // find constrained_by pointer in constrained feature
                     if ( fo )   // case FEATURE:
                     {
@@ -1113,23 +1179,32 @@ bool Problem::check(int verbose_level)
                         }
                         // find in own Frame
                         found = found || (std::find(F->getStateBlockVec().begin(), F->getStateBlockVec().end(), sb) != F->getStateBlockVec().end());
+                        // find in own Capture
+                        found = found || (std::find(C->getStateBlockVec().begin(), C->getStateBlockVec().end(), sb) != C->getStateBlockVec().end());
                         // find in own Sensor
                         if (S)
                             found = found || (std::find(S->getStateBlockVec().begin(), S->getStateBlockVec().end(), sb) != S->getStateBlockVec().end());
                         // find in constrained Frame
                         if (Fo)
                             found = found || (std::find(Fo->getStateBlockVec().begin(), Fo->getStateBlockVec().end(), sb) != Fo->getStateBlockVec().end());
+                        // find in constrained Capture
+                        if (Co)
+                            found = found || (std::find(Co->getStateBlockVec().begin(), Co->getStateBlockVec().end(), sb) != Co->getStateBlockVec().end());
+                        // find in constrained Feature
                         if (fo)
                         {
                             // find in constrained feature's Frame
                             FrameBasePtr foF = fo->getFramePtr();
                             found = found || (std::find(foF->getStateBlockVec().begin(), foF->getStateBlockVec().end(), sb) != foF->getStateBlockVec().end());
+                            // find in constrained feature's Capture
+                            CaptureBasePtr foC = fo->getCapturePtr();
+                            found = found || (std::find(foC->getStateBlockVec().begin(), foC->getStateBlockVec().end(), sb) != foC->getStateBlockVec().end());
                             // find in constrained feature's Sensor
                             SensorBasePtr foS = fo->getCapturePtr()->getSensorPtr();
                             found = found || (std::find(foS->getStateBlockVec().begin(), foS->getStateBlockVec().end(), sb) != foS->getStateBlockVec().end());
                         }
+                        // find in constrained landmark
                         if (Lo)
-                            // find in constrained landmark
                             found = found || (std::find(Lo->getStateBlockVec().begin(), Lo->getStateBlockVec().end(), sb) != Lo->getStateBlockVec().end());
                         if (verbose_level > 0)
                         {
@@ -1205,4 +1280,20 @@ bool Problem::check(int verbose_level)
     return is_consistent;
 }
 
+void Problem::print(const std::string& depth, bool constr_by, bool metric, bool state_blocks)
+{
+    if (depth.compare("T") == 0)
+        print(0, constr_by, metric, state_blocks);
+    else if (depth.compare("F") == 0)
+        print(1, constr_by, metric, state_blocks);
+    else if (depth.compare("C") == 0)
+        print(2, constr_by, metric, state_blocks);
+    else if (depth.compare("f") == 0)
+        print(3, constr_by, metric, state_blocks);
+    else if (depth.compare("c") == 0)
+        print(4, constr_by, metric, state_blocks);
+    else
+        print(0, constr_by, metric, state_blocks);
+}
+
 } // namespace wolf
diff --git a/src/problem.h b/src/problem.h
index d7ab78f3b1ae955886bf3588113dbdd06bfd9343..d46e1f6d6785c7f487e966a7ac9b24ae46e1017e 100644
--- a/src/problem.h
+++ b/src/problem.h
@@ -298,6 +298,7 @@ class Problem : public std::enable_shared_from_this<Problem>
          * \param state_blocks : show state blocks
          */
         void print(int depth = 4, bool constr_by = false, bool metric = true, bool state_blocks = false);
+        void print(const std::string& depth, bool constr_by = false, bool metric = true, bool state_blocks = false);
         bool check(int verbose_level = 0);
 
 };
diff --git a/src/processor_imu.cpp b/src/processor_imu.cpp
index 4f35ccebfab7fa59105fa1c40045055d45cab351..d6a1ce78cd0acd30b81502597a8e3c3e0794e036 100644
--- a/src/processor_imu.cpp
+++ b/src/processor_imu.cpp
@@ -86,6 +86,7 @@ Motion ProcessorIMU::interpolate(const Motion& _motion_ref, Motion& _motion_seco
      * Covariances receive linear interpolation
      *    Q_ret = (ts - t_ref) / dt * Q_sec
      */
+    /*
     // resolve out-of-bounds time stamp as if the time stamp was exactly on the bounds
     if (_ts <= _motion_ref.ts_)    // behave as if _ts == _motion_ref.ts_
     {
@@ -97,7 +98,7 @@ Motion ProcessorIMU::interpolate(const Motion& _motion_ref, Motion& _motion_seco
         motion_int.delta_cov_ . setZero();
         return motion_int;
     }
-    if (_motion_second.ts_ <= _ts)    // behave as if _ts == _motion_second.ts_
+    if (_ts >= _motion_second.ts_)    // behave as if _ts == _motion_second.ts_
     {
         // return original second motion. Second motion becomes null motion
         Motion motion_int         ( _motion_second );
@@ -173,6 +174,10 @@ Motion ProcessorIMU::interpolate(const Motion& _motion_ref, Motion& _motion_seco
     //_motion.delta_integr_cov_ = _motion.delta_integr_cov_; // trivial, just leave the code commented
 
     return motion_int;
+    */
+
+    return _motion_ref;
+
 }
 
 CaptureMotionPtr ProcessorIMU::createCapture(const TimeStamp& _ts,
@@ -194,8 +199,7 @@ FeatureBasePtr ProcessorIMU::createFeature(CaptureMotionPtr _capture_motion)
     FeatureIMUPtr key_feature_ptr = std::make_shared<FeatureIMU>(
             _capture_motion->getBuffer().get().back().delta_integr_,
             _capture_motion->getBuffer().get().back().delta_integr_cov_,
-            _capture_motion->getBuffer().getCalibrationPreint().head(3),
-            _capture_motion->getBuffer().getCalibrationPreint().tail(3),
+            _capture_motion->getBuffer().getCalibrationPreint(),
             _capture_motion->getBuffer().get().back().jacobian_calib_);
     return key_feature_ptr;
 }
@@ -205,11 +209,120 @@ ConstraintBasePtr ProcessorIMU::emplaceConstraint(FeatureBasePtr _feature_motion
     CaptureIMUPtr cap_imu = std::static_pointer_cast<CaptureIMU>(_capture_origin);
     FeatureIMUPtr ftr_imu = std::static_pointer_cast<FeatureIMU>(_feature_motion);
     ConstraintIMUPtr ctr_imu = std::make_shared<ConstraintIMU>(ftr_imu, cap_imu, shared_from_this());
+
+    // link ot wolf tree
     _feature_motion->addConstraint(ctr_imu);
-    cap_imu->getFramePtr()->addConstrainedBy(ctr_imu);
+    _capture_origin->addConstrainedBy(ctr_imu);
+    _capture_origin->getFramePtr()->addConstrainedBy(ctr_imu);
+
     return ctr_imu;
 }
 
+void ProcessorIMU::computeCurrentDelta(const Eigen::VectorXs& _data,
+                                       const Eigen::MatrixXs& _data_cov,
+                                       const Eigen::VectorXs& _calib,
+                                       const Scalar _dt,
+                                       Eigen::VectorXs& _delta,
+                                       Eigen::MatrixXs& _delta_cov,
+                                       Eigen::MatrixXs& _jac_delta_calib)
+{
+    assert(_data.size() == data_size_ && "Wrong data size!");
+
+    using namespace Eigen;
+    Matrix<Scalar, 9, 6> jac_data;
+
+    /*
+     * We have the following computing pipeline:
+     *     Input: data, calib, dt
+     *     Output: delta, delta_cov, jac_calib
+     *
+     * We do:
+     *     body         = data - calib
+     *     delta        = body2delta(body, dt) --> jac_body
+     *     jac_data     = jac_body
+     *     jac_calib    = - jac_body
+     *     delta_cov  <-- propagate data_cov using jac_data
+     *
+     * where body2delta(.) produces a delta from body=(a,w) as follows:
+     *     dp = 1/2 * a * dt^2
+     *     dq = exp(w * dt)
+     *     dv = a * dt
+     */
+
+    // create delta
+    imu::body2delta(_data - _calib, _dt, _delta, jac_data); // Jacobians tested in imu_tools
+
+    // compute delta_cov
+    _delta_cov = jac_data * _data_cov * jac_data.transpose();
+
+    // compute jacobian_calib
+    _jac_delta_calib = - jac_data;
+
+}
+
+void ProcessorIMU::deltaPlusDelta(const Eigen::VectorXs& _delta_preint,
+                                  const Eigen::VectorXs& _delta,
+                                  const Scalar _dt,
+                                  Eigen::VectorXs& _delta_preint_plus_delta)
+{
+    /* MATHS according to Sola-16
+     * Dp' = Dp + Dv*dt + 1/2*Dq*(a-a_b)*dt^2    = Dp + Dv*dt + Dq*dp   if  dp = 1/2*(a-a_b)*dt^2
+     * Dv' = Dv + Dq*(a-a_b)*dt                  = Dv + Dq*dv           if  dv = (a-a_b)*dt
+     * Dq' = Dq * exp((w-w_b)*dt)                = Dq * dq              if  dq = exp((w-w_b)*dt)
+     *
+     * where (dp, dq, dv) need to be computed in data2delta(), and Dq*dx =is_equivalent_to= Dq*dx*Dq'.
+     */
+    _delta_preint_plus_delta = imu::compose(_delta_preint, _delta, _dt);
+}
+
+void ProcessorIMU::statePlusDelta(const Eigen::VectorXs& _x,
+                                  const Eigen::VectorXs& _delta,
+                                  const Scalar _dt,
+                                  Eigen::VectorXs& _x_plus_delta)
+{
+    //    assert(_x.size() == 10 && "Wrong _x vector size");
+    //    assert(_delta.size() == 10 && "Wrong _delta vector size");
+    //    assert(_x_plus_delta.size() == 10 && "Wrong _x_plus_delta vector size");
+    assert(_dt >= 0 && "Time interval _Dt is negative!");
+    VectorXs x(_x.head(10));
+    VectorXs x_plus_delta(10);
+    x_plus_delta = imu::composeOverState(x, _delta, _dt);
+    _x_plus_delta.head(10) = x_plus_delta;
+}
+
+void ProcessorIMU::deltaPlusDelta(const Eigen::VectorXs& _delta_preint,
+                                  const Eigen::VectorXs& _delta,
+                                  const Scalar _dt,
+                                  Eigen::VectorXs& _delta_preint_plus_delta,
+                                  Eigen::MatrixXs& _jacobian_delta_preint,
+                                  Eigen::MatrixXs& _jacobian_delta)
+{
+    /*
+     * Expression of the delta integration step, D' = D (+) d:
+     *
+     *     Dp' = Dp + Dv*dt + Dq*dp
+     *     Dv' = Dv + Dq*dv
+     *     Dq' = Dq * dq
+     *
+     * Jacobians for covariance propagation.
+     *
+     * a. With respect to Delta, gives _jacobian_delta_preint = D_D as:
+     *
+     *   D_D = [ I    -DR*skew(dp)   I*dt
+     *           0     dR.tr          0
+     *           0    -DR*skew(dv)    I  ] // See Sola-16
+     *
+     * b. With respect to delta, gives _jacobian_delta = D_d as:
+     *
+     *   D_d = [ DR   0    0
+     *           0    I    0
+     *           0    0    DR ] // See Sola-16
+     *
+     * Note: covariance propagation, i.e.,  P+ = D_D * P * D_D' + D_d * M * D_d', is done in ProcessorMotion.
+     */
+    imu::compose(_delta_preint, _delta, _dt, _delta_preint_plus_delta, _jacobian_delta_preint, _jacobian_delta); // jacobians tested in imu_tools
+}
+
 } // namespace wolf
 
 
diff --git a/src/processor_imu.h b/src/processor_imu.h
index b4f8f1e662ba32a3235902a8c5fc8cb21961e250..f46dcc423bd9038da49db1bf09ad2c59e7f79010 100644
--- a/src/processor_imu.h
+++ b/src/processor_imu.h
@@ -110,150 +110,6 @@ class ProcessorIMU : public ProcessorMotion{
 
 namespace wolf{
 
-inline void ProcessorIMU::computeCurrentDelta(const Eigen::VectorXs& _data,
-                                              const Eigen::MatrixXs& _data_cov,
-                                              const Eigen::VectorXs& _calib,
-                                              const Scalar _dt,
-                                              Eigen::VectorXs& _delta,
-                                              Eigen::MatrixXs& _delta_cov,
-                                              Eigen::MatrixXs& _jacobian_calib)
-{
-    assert(_data.size() == data_size_ && "Wrong data size!");
-
-    using namespace Eigen;
-
-    // acc and gyro measurements corrected with the estimated bias, times dt
-    Vector3s a_dt = (_data.head(3) - _calib.head(3)) * _dt;
-    Vector3s w_dt = (_data.tail(3) - _calib.tail(3)) * _dt;
-
-    /* create delta
-     *
-     * MATHS of delta creation -- Sola-16
-     * dp = 1/2 * (a-a_b) * dt^2 = 1/2 * dv * dt
-     * dv = (a-a_b) * dt
-     * dq = exp((w-w_b)*dt)
-     */
-    Vector3s delta_p = a_dt * _dt / 2;
-    Quaternions delta_q = v2q(w_dt);
-    Vector3s delta_v = a_dt;
-    _delta << delta_p , delta_q.coeffs() , delta_v;
-
-    /* Compute jacobian of delta wrt data
-     *
-     * MATHS : jacobian dd_dn, of delta wrt noise
-     * substituting a and w respectively by (a+a_n) and (w+w_n) (measurement noise is additive)
-     *                 an           wn
-     *         dp [ 0.5*I*dt*dt     0     ]
-     * dd_dn = do [    0           Jr*dt  ]
-     *         dv [    I*dt         0     ] // see Sola-16
-     */
-
-    // we go the sparse way:
-    Matrix3s ddv_dan = Matrix3s::Identity() * _dt;
-    Matrix3s ddp_dan = ddv_dan * _dt / 2;
-    Matrix3s ddo_dwn = jac_SO3_right(w_dt) * _dt;
-
-    /* Covariance computation
-     *
-     * Covariance is sparse:
-     *       [ Cpp   0   Cpv
-     * COV =    0   Coo   0
-     *         Cpv'  0   Cvv ]
-     *
-     * where Cpp, Cpv, Coo and Cvv are computed below
-     */
-    _delta_cov.block<3,3>(0,0).noalias() = ddp_dan*_data_cov.block<3,3>(0,0)*ddp_dan.transpose(); // Cpp = ddp_dan * Caa * ddp_dan'
-    _delta_cov.block<3,3>(0,6).noalias() = ddp_dan*_data_cov.block<3,3>(0,0)*ddv_dan.transpose(); // Cpv = ddp_dan * Caa * ddv_dan'
-    _delta_cov.block<3,3>(3,3).noalias() = ddo_dwn*_data_cov.block<3,3>(3,3)*ddo_dwn.transpose(); // Coo = ddo_dwn * Cww * ddo_dwn'
-    _delta_cov.block<3,3>(6,0)           = _delta_cov.block<3,3>(0,6).transpose();                // Cvp = Cpv'
-    _delta_cov.block<3,3>(6,6).noalias() = ddv_dan*_data_cov.block<3,3>(0,0)*ddv_dan.transpose(); // Cvv = ddv_dan * Caa * ddv_dan'
-
-
-    /* Jacobians of delta wrt calibration parameters -- bias
-     *
-     * We know that d_(meas - bias)/d_bias = -I
-     * so d_delta/d_bias = - d_delta/d_meas
-     * we assign only the non-null ones
-     */
-    _jacobian_calib.setZero(delta_cov_size_,calib_size_); // can be commented usually, more sure this way
-    _jacobian_calib.block(0,0,3,3) = - ddp_dan;
-    _jacobian_calib.block(3,3,3,3) = - ddo_dwn;
-    _jacobian_calib.block(6,0,3,3) = - ddv_dan;
-
-}
-
-inline void ProcessorIMU::deltaPlusDelta(const Eigen::VectorXs& _delta_preint,
-                                         const Eigen::VectorXs& _delta,
-                                         const Scalar _dt,
-                                         Eigen::VectorXs& _delta_preint_plus_delta,
-                                         Eigen::MatrixXs& _jacobian_delta_preint,
-                                         Eigen::MatrixXs& _jacobian_delta)
-{
-
-    /*
-     * Expression of the delta integration step, D' = D (+) d:
-     *
-     *     Dp' = Dp + Dv*dt + Dq*dp
-     *     Dv' = Dv + Dq*dv
-     *     Dq' = Dq * dq
-     */
-
-    /*/////////////////////////////////////////////////////////
-     * Note. Jacobians for covariance propagation.
-     *
-     * 1.a. With respect to Delta, gives _jacobian_delta_preint = D_D as:
-     *
-     *   D_D = [ I    -DR*skew(dp)   I*dt
-     *           0     dR.tr          0
-     *           0    -DR*skew(dv)    I  ] // See Sola-16
-     *
-     * 1.b. With respect to delta, gives _jacobian_delta = D_d as:
-     *
-     *   D_d = [ DR   0    0
-     *           0    I    0
-     *           0    0    DR ] // See Sola-16
-     *
-     * Note: covariance propagation, i.e.,  P+ = D_D * P * D_D' + D_d * M * D_d', is done in ProcessorMotion.
-     */
-
-    imu::compose(_delta_preint, _delta, _dt, _delta_preint_plus_delta, _jacobian_delta_preint, _jacobian_delta);
-}
-
-inline void ProcessorIMU::deltaPlusDelta(const Eigen::VectorXs& _delta_preint,
-                                         const Eigen::VectorXs& _delta,
-                                         const Scalar _dt,
-                                         Eigen::VectorXs& _delta_preint_plus_delta)
-{
-
-    /* MATHS according to Sola-16
-     * Dp' = Dp + Dv*dt + 1/2*Dq*(a-a_b)*dt^2    = Dp + Dv*dt + Dq*dp   if  dp = 1/2*(a-a_b)*dt^2
-     * Dv' = Dv + Dq*(a-a_b)*dt                  = Dv + Dq*dv           if  dv = (a-a_b)*dt
-     * Dq' = Dq * exp((w-w_b)*dt)                = Dq * dq              if  dq = exp((w-w_b)*dt)
-     *
-     * where (dp, dq, dv) need to be computed in data2delta(), and Dq*dx =is_equivalent_to= Dq*dx*Dq'.
-     */
-
-    imu::compose(_delta_preint, _delta, _delta_preint_plus_delta);
-
-}
-
-inline void ProcessorIMU::statePlusDelta(const Eigen::VectorXs& _x,
-                                     const Eigen::VectorXs& _delta,
-                                     const Scalar _dt,
-                                     Eigen::VectorXs& _x_plus_delta)
-{
-//    assert(_x.size() == 10 && "Wrong _x vector size");
-//    assert(_delta.size() == 10 && "Wrong _delta vector size");
-//    assert(_x_plus_delta.size() == 10 && "Wrong _x_plus_delta vector size");
-    assert(_dt >= 0 && "Time interval _Dt is negative!");
-
-    VectorXs x( _x.head(10) );
-    VectorXs x_plus_delta(10);
-
-    x_plus_delta = imu::composeOverState(x, _delta, _dt);
-    _x_plus_delta.head(10) = x_plus_delta;
-}
-
 inline Eigen::VectorXs ProcessorIMU::deltaZero() const
 {
     return (Eigen::VectorXs(10) << 0,0,0,  0,0,0,1,  0,0,0 ).finished(); // p, q, v
diff --git a/src/processor_imu_UnitTester.cpp b/src/processor_imu_UnitTester.cpp
index fa1b49c060514de69ea33b0413ca7c1131ac244d..4716356e6415f3ed3b803c6ebb68cdd3a8b5036e 100644
--- a/src/processor_imu_UnitTester.cpp
+++ b/src/processor_imu_UnitTester.cpp
@@ -9,13 +9,5 @@ ProcessorIMU_UnitTester::ProcessorIMU_UnitTester() :
 
 ProcessorIMU_UnitTester::~ProcessorIMU_UnitTester(){}
 
-ProcessorBasePtr ProcessorIMU_UnitTester::create(const std::string& _unique_name, const ProcessorParamsBasePtr _params, const SensorBasePtr)
-{
-    ProcessorIMUPtr prc_ptr = std::make_shared<ProcessorIMU>();
-    prc_ptr->setName(_unique_name);
-    return prc_ptr;
-}
-
-
 } // namespace wolf
 
diff --git a/src/processor_imu_UnitTester.h b/src/processor_imu_UnitTester.h
index b0a714ee18216b349888fcae8fef8e8b78652e25..2c70cec4ac2570229dc4169072e9ad9586829990 100644
--- a/src/processor_imu_UnitTester.h
+++ b/src/processor_imu_UnitTester.h
@@ -9,9 +9,23 @@
 namespace wolf {
     struct IMU_jac_bias{ //struct used for checking jacobians by finite difference
 
-        IMU_jac_bias(Eigen::Matrix<Eigen::VectorXs,6,1> _Deltas_noisy_vect, Eigen::VectorXs _Delta0 , Eigen::Matrix3s _dDp_dab, Eigen::Matrix3s _dDv_dab, 
-                    Eigen::Matrix3s _dDp_dwb, Eigen::Matrix3s _dDv_dwb, Eigen::Matrix3s _dDq_dwb) : Deltas_noisy_vect_(_Deltas_noisy_vect), Delta0_(_Delta0) ,
-                    dDp_dab_(_dDp_dab), dDv_dab_(_dDv_dab), dDp_dwb_(_dDp_dwb), dDv_dwb_(_dDv_dwb), dDq_dwb_(_dDq_dwb){}
+        IMU_jac_bias(Eigen::Matrix<Eigen::VectorXs,6,1> _Deltas_noisy_vect,
+                     Eigen::VectorXs _Delta0 ,
+                     Eigen::Matrix3s _dDp_dab,
+                     Eigen::Matrix3s _dDv_dab,
+                     Eigen::Matrix3s _dDp_dwb,
+                     Eigen::Matrix3s _dDv_dwb,
+                     Eigen::Matrix3s _dDq_dwb) :
+                         Deltas_noisy_vect_(_Deltas_noisy_vect),
+                         Delta0_(_Delta0) ,
+                         dDp_dab_(_dDp_dab),
+                         dDv_dab_(_dDv_dab),
+                         dDp_dwb_(_dDp_dwb),
+                         dDv_dwb_(_dDv_dwb),
+                         dDq_dwb_(_dDq_dwb)
+        {
+            //
+        }
                 
         IMU_jac_bias(){
 
@@ -30,7 +44,7 @@ namespace wolf {
         IMU_jac_bias(IMU_jac_bias const & toCopy){
 
             Deltas_noisy_vect_ = toCopy.Deltas_noisy_vect_;
-            Delta0_ = toCopy.Delta0_;
+            Delta0_  = toCopy.Delta0_;
             dDp_dab_ = toCopy.dDp_dab_;
             dDv_dab_ = toCopy.dDv_dab_;
             dDp_dwb_ = toCopy.dDp_dwb_;
@@ -56,7 +70,7 @@ namespace wolf {
             void copyfrom(IMU_jac_bias const& right){
 
                 Deltas_noisy_vect_ = right.Deltas_noisy_vect_;
-                Delta0_ = right.Delta0_;
+                Delta0_  = right.Delta0_;
                 dDp_dab_ = right.dDp_dab_;
                 dDv_dab_ = right.dDv_dab_;
                 dDp_dwb_ = right.dDp_dwb_;
@@ -67,10 +81,21 @@ namespace wolf {
 
     struct IMU_jac_deltas{
 
-        IMU_jac_deltas(Eigen::VectorXs _Delta0, Eigen::VectorXs _delta0, Eigen::Matrix<Eigen::VectorXs,9,1> _Delta_noisy_vect, Eigen::Matrix<Eigen::VectorXs,9,1> _delta_noisy_vect, 
-                        Eigen::MatrixXs _jacobian_delta_preint, Eigen::MatrixXs _jacobian_delta ) :
-                        Delta0_(_Delta0), delta0_(_delta0), Delta_noisy_vect_(_Delta_noisy_vect), delta_noisy_vect_(_delta_noisy_vect), 
-                       jacobian_delta_preint_(_jacobian_delta_preint), jacobian_delta_(_jacobian_delta) {}
+        IMU_jac_deltas(Eigen::VectorXs _Delta0,
+                       Eigen::VectorXs _delta0,
+                       Eigen::Matrix<Eigen::VectorXs,9,1> _Delta_noisy_vect,
+                       Eigen::Matrix<Eigen::VectorXs,9,1> _delta_noisy_vect,
+                       Eigen::MatrixXs _jacobian_delta_preint,
+                       Eigen::MatrixXs _jacobian_delta ) :
+                           Delta0_(_Delta0),
+                           delta0_(_delta0),
+                           Delta_noisy_vect_(_Delta_noisy_vect),
+                           delta_noisy_vect_(_delta_noisy_vect),
+                           jacobian_delta_preint_(_jacobian_delta_preint),
+                           jacobian_delta_(_jacobian_delta)
+        {
+            //
+        }
 
         IMU_jac_deltas(){
             for (int i=0; i<9; i++){
@@ -114,12 +139,12 @@ namespace wolf {
         public:
             void copyfrom(IMU_jac_deltas const& right){
 
-                Delta_noisy_vect_ = right.Delta_noisy_vect_;
-                delta_noisy_vect_ = right.delta_noisy_vect_;
-                Delta0_ = right.Delta0_;
-                delta0_ = right.delta0_;
-                jacobian_delta_preint_ = right.jacobian_delta_preint_;
-                jacobian_delta_ = right.jacobian_delta_;
+                Delta_noisy_vect_       = right.Delta_noisy_vect_;
+                delta_noisy_vect_       = right.delta_noisy_vect_;
+                Delta0_                 = right.Delta0_;
+                delta0_                 = right.delta0_;
+                jacobian_delta_preint_  = right.jacobian_delta_preint_;
+                jacobian_delta_         = right.jacobian_delta_;
             }
     };
 
@@ -137,7 +162,10 @@ namespace wolf {
             _dt : time interval between 2 IMU measurements
             da_b : bias noise to add - scalar because adding the same noise to each component of bias (abx, aby, abz, wbx, wby, wbz) one by one. 
          */
-        IMU_jac_bias finite_diff_ab(const Scalar _dt, Eigen::Vector6s& _data, const wolf::Scalar& da_b, const Eigen::Matrix<wolf::Scalar,10,1>& _delta_preint0);
+        IMU_jac_bias finite_diff_ab(const Scalar _dt,
+                                    Eigen::Vector6s& _data,
+                                    const wolf::Scalar& da_b,
+                                    const Eigen::Matrix<wolf::Scalar,10,1>& _delta_preint0);
 
         /* params :
             _data : input data vector (size 6 : ax,ay,az,wx,wy,wz)
@@ -145,10 +173,16 @@ namespace wolf {
             _Delta_noise : noise to add to Delta_preint (D1 in D = D1 + d), vector 9 because rotation expressed as a vector (R2v(q.matrix()))
             _delta_noise : noise to add to instantaneous delta (d in D = D1 + d), vector 9 because rotation expressed as a vector (R2v(q.matrix()))
          */
-        IMU_jac_deltas finite_diff_noise(const Scalar& _dt, Eigen::Vector6s& _data, const Eigen::Matrix<wolf::Scalar,9,1>& _Delta_noise, const Eigen::Matrix<wolf::Scalar,9,1>& _delta_noise, const Eigen::Matrix<wolf::Scalar,10,1>& _Delta0);
+        IMU_jac_deltas finite_diff_noise(const Scalar& _dt,
+                                         Eigen::Vector6s& _data,
+                                         const Eigen::Matrix<wolf::Scalar,9,1>& _Delta_noise,
+                                         const Eigen::Matrix<wolf::Scalar,9,1>& _delta_noise,
+                                         const Eigen::Matrix<wolf::Scalar,10,1>& _Delta0);
 
         public:
-        static ProcessorBasePtr create(const std::string& _unique_name, const ProcessorParamsBasePtr _params, const SensorBasePtr = nullptr);
+        static ProcessorBasePtr create(const std::string& _unique_name,
+                                       const ProcessorParamsBasePtr _params,
+                                       const SensorBasePtr = nullptr);
 
         public:
         // Maps quat, to be used as temporary
@@ -170,7 +204,10 @@ namespace wolf {
 namespace wolf{
 
     //Functions to test jacobians with finite difference method
-inline IMU_jac_bias ProcessorIMU_UnitTester::finite_diff_ab(const Scalar _dt, Eigen::Vector6s& _data, const wolf::Scalar& da_b, const Eigen::Matrix<wolf::Scalar,10,1>& _delta_preint0)
+inline IMU_jac_bias ProcessorIMU_UnitTester::finite_diff_ab(const Scalar _dt,
+                                                            Eigen::Vector6s& _data,
+                                                            const wolf::Scalar& da_b,
+                                                            const Eigen::Matrix<wolf::Scalar,10,1>& _delta_preint0)
 {
     //TODO : need to use a reset function here to make sure jacobians have not been used before --> reset everything
     ///Define all the needed variables
@@ -225,7 +262,7 @@ inline IMU_jac_bias ProcessorIMU_UnitTester::finite_diff_ab(const Scalar _dt, Ei
         _data = data0;
         _data(i) = _data(i) - da_b; //- because a = a_m − a_b + a_n, in out case, a = a_m − a_b - da_b + a_n
         //data2delta
-        computeCurrentDelta(_data, data_cov, bias, _dt,delta_,delta_cov_,jacobian_delta_calib_);
+        computeCurrentDelta(_data, data_cov, bias, _dt, delta_, delta_cov_, jacobian_delta_calib_);
         deltaPlusDelta(_delta_preint0, delta_, _dt, delta_preint_plus_delta0, jacobian_delta_preint, jacobian_delta);
         Deltas_noisy_vect(i) = delta_preint_plus_delta0; //preintegrated deltas affected by added bias noise
     }
diff --git a/src/processor_motion.cpp b/src/processor_motion.cpp
index b33f06087a6e0d98b00da00a274e3d65139f9a26..f00fbfb36d3d3b55d1f49259622dd07589ee0427 100644
--- a/src/processor_motion.cpp
+++ b/src/processor_motion.cpp
@@ -48,7 +48,6 @@ void ProcessorMotion::process(CaptureBasePtr _incoming_ptr)
 
     if (status_ == IDLE)
     {
-//        std::cout << "PM: IDLE" << std::endl;
         TimeStamp t0 = _incoming_ptr->getTimeStamp();
 
         if (origin_ptr_ == nullptr)
@@ -69,7 +68,6 @@ void ProcessorMotion::process(CaptureBasePtr _incoming_ptr)
             }
         }
         status_ = RUNNING;
-//        std::cout << "PM: RUNNING" << std::endl;
     }
 
     incoming_ptr_ = getIncomingCaptureMotion(_incoming_ptr);
@@ -106,17 +104,21 @@ void ProcessorMotion::process(CaptureBasePtr _incoming_ptr)
                                                                 getCurrentState(),
                                                                 getCurrentTimeStamp());
         // create a new capture
-        CaptureMotionPtr new_capture_ptr = emplaceCapture(key_frame_ptr->getTimeStamp(),
+        CaptureMotionPtr new_capture_ptr = emplaceCapture(new_frame_ptr,
                                                           getSensorPtr(),
+                                                          key_frame_ptr->getTimeStamp(),
                                                           Eigen::VectorXs::Zero(data_size_),
                                                           Eigen::MatrixXs::Zero(data_size_, data_size_),
-                                                          new_frame_ptr,
+                                                          last_ptr_->getCalibration(),
+                                                          last_ptr_->getCalibration(),
                                                           key_frame_ptr);
         // reset the new buffer
+        new_capture_ptr->getBuffer().get().clear();
         new_capture_ptr->getBuffer().get().push_back( motionZero(key_frame_ptr->getTimeStamp()) ) ;
 
-
         // reset integrals
+        delta_ = deltaZero();
+        delta_cov_.setZero();
         delta_integrated_ = deltaZero();
         delta_integrated_cov_.setZero();
         jacobian_calib_.setZero();
@@ -183,51 +185,49 @@ void ProcessorMotion::setOrigin(FrameBasePtr _origin_frame)
             && "ProcessorMotion::setOrigin: origin frame must be in the trajectory.");
     assert(_origin_frame->isKey() && "ProcessorMotion::setOrigin: origin frame must be KEY FRAME.");
 
+    // -------- ORIGIN ---------
     // emplace (empty) origin Capture
-    origin_ptr_ = emplaceCapture(_origin_frame->getTimeStamp(),
+    origin_ptr_ = emplaceCapture(_origin_frame,
                                  getSensorPtr(),
+                                 _origin_frame->getTimeStamp(),
                                  Eigen::VectorXs::Zero(data_size_),
                                  Eigen::MatrixXs::Zero(data_size_, data_size_),
-                                 _origin_frame,
+                                 getSensorPtr()->getCalibration(),
+                                 getSensorPtr()->getCalibration(),
                                  nullptr);
 
+    // ---------- LAST ----------
     // Make non-key-frame for last Capture
     FrameBasePtr new_frame_ptr = getProblem()->emplaceFrame(NON_KEY_FRAME,
                                                            _origin_frame->getState(),
                                                            _origin_frame->getTimeStamp());
     // emplace (emtpy) last Capture
-    last_ptr_ = emplaceCapture(_origin_frame->getTimeStamp(),
+    last_ptr_ = emplaceCapture(new_frame_ptr,
                                getSensorPtr(),
+                               _origin_frame->getTimeStamp(),
                                Eigen::VectorXs::Zero(data_size_),
                                Eigen::MatrixXs::Zero(data_size_, data_size_),
-                               new_frame_ptr,
+                               getSensorPtr()->getCalibration(),
+                               getSensorPtr()->getCalibration(),
                                _origin_frame);
 
-    /* Status:
-     * KF --- F ---
-     * o      l     i
-     */
+    // clear and reset buffer
+    getBuffer().get().clear();
+    getBuffer().get().push_back(motionZero(_origin_frame->getTimeStamp()));
 
-    // Reset deltas
+    // Reset integrals
     delta_ = deltaZero();
-    delta_integrated_ = deltaZero();
     delta_cov_.setZero();
+    delta_integrated_ = deltaZero();
     delta_integrated_cov_.setZero();
     jacobian_calib_.setZero();
 
-    // clear and reset buffer
-    getBuffer().get().clear();
-    getBuffer().setCalibrationPreint(origin_ptr_->getCalibration());
-    getBuffer().get().push_back(motionZero(_origin_frame->getTimeStamp()));
-
     // Reset derived things
     resetDerived();
 }
 
 bool ProcessorMotion::keyFrameCallback(FrameBasePtr _new_keyframe, const Scalar& _time_tol_other)
 {
-//    std::cout << "PM: KF" << _new_keyframe->id() << " callback received at ts= " << _new_keyframe->getTimeStamp().get() << std::endl;
-//    std::cout << "    while last ts= " << last_ptr_->getTimeStamp().get() << std::endl;
 
     assert(_new_keyframe->getTrajectoryPtr() != nullptr
             && "ProcessorMotion::keyFrameCallback: key frame must be in the trajectory.");
@@ -235,26 +235,27 @@ bool ProcessorMotion::keyFrameCallback(FrameBasePtr _new_keyframe, const Scalar&
     // get keyframe's time stamp
     TimeStamp new_ts = _new_keyframe->getTimeStamp();
 
-    // find capture whose buffer is affected by the new keyframe
+    // find the capture whose buffer is affected by the new keyframe
     CaptureMotionPtr existing_capture = findCaptureContainingTimeStamp(new_ts);
     assert(existing_capture != nullptr
             && "ProcessorMotion::keyFrameCallback: no motion capture containing the required TimeStamp found");
 
     // Find the frame acting as the capture's origin
-    FrameBasePtr new_keyframe_origin = existing_capture->getOriginFramePtr();
+    FrameBasePtr keyframe_origin = existing_capture->getOriginFramePtr();
 
     // emplace a new motion capture to the new keyframe
-    CaptureMotionPtr new_capture = emplaceCapture(new_ts,
+    CaptureMotionPtr new_capture = emplaceCapture(_new_keyframe,
                                                   getSensorPtr(),
+                                                  new_ts,
                                                   Eigen::VectorXs::Zero(data_size_),
-                                                  Eigen::MatrixXs::Zero(data_size_, data_size_),
-                                                  _new_keyframe,
-                                                  new_keyframe_origin);
+                                                  existing_capture->getDataCovariance(),
+                                                  existing_capture->getCalibration(),
+                                                  existing_capture->getCalibration(),
+                                                  keyframe_origin);
 
     // split the buffer
-    // and give the old buffer to the key_capture
+    // and give the part of the buffer before the new keyframe to the key_capture
     existing_capture->getBuffer().split(new_ts, new_capture->getBuffer());
-    new_capture->getBuffer().setCalibrationPreint(new_capture->getCalibration());
 
     // interpolate individual delta
     if (!existing_capture->getBuffer().get().empty() && new_capture->getBuffer().get().back().ts_ != new_ts)
@@ -271,7 +272,7 @@ bool ProcessorMotion::keyFrameCallback(FrameBasePtr _new_keyframe, const Scalar&
     FeatureBasePtr new_feature = emplaceFeature(new_capture);
 
     // create motion constraint and add it to the feature, and constrain to the other capture (origin)
-    emplaceConstraint(new_feature, new_keyframe_origin->getCaptureOf(getSensorPtr())); // XXX it was new_keyframe_origin
+    emplaceConstraint(new_feature, keyframe_origin->getCaptureOf(getSensorPtr()) ); // XXX it was new_keyframe_origin
 
 
 
@@ -332,7 +333,10 @@ void ProcessorMotion::integrateOneStep()
 
     // integrate Jacobian wrt calib
     if (calib_size_ > 0)
+    {
         jacobian_calib_ = jacobian_delta_preint_ * getBuffer().get().back().jacobian_calib_ + jacobian_delta_ * jacobian_delta_calib_;
+        // WOLF_TRACE("jac calib: ", jacobian_calib_.row(0));
+    }
 
     // Integrate covariance
     delta_integrated_cov_ = jacobian_delta_preint_ * getBuffer().get().back().delta_integr_cov_ * jacobian_delta_preint_.transpose()
@@ -356,6 +360,8 @@ void ProcessorMotion::reintegrateBuffer(CaptureMotionPtr _capture_ptr)
     // start with empty motion
     _capture_ptr->getBuffer().get().push_front(motionZero(_capture_ptr->getOriginFramePtr()->getTimeStamp()));
 
+    VectorXs calib = _capture_ptr->getCalibrationPreint();
+
     // Iterate all the buffer
     auto motion_it = _capture_ptr->getBuffer().get().begin();
     auto prev_motion_it = motion_it;
@@ -366,8 +372,6 @@ void ProcessorMotion::reintegrateBuffer(CaptureMotionPtr _capture_ptr)
         const Scalar dt = motion_it->ts_ - prev_motion_it->ts_;
 
         // re-convert data to delta with the new calibration parameters
-        VectorXs calib = _capture_ptr->getBuffer().getCalibrationPreint();
-
         computeCurrentDelta(motion_it->data_,
                             motion_it->data_cov_,
                             calib,
@@ -430,11 +434,13 @@ CaptureMotionPtr ProcessorMotion::getCaptureMotionContainingTimeStamp(const Time
     return capture_motion;
 }
 
-CaptureMotionPtr ProcessorMotion::emplaceCapture(const TimeStamp& _ts,
+CaptureMotionPtr ProcessorMotion::emplaceCapture(const FrameBasePtr& _frame_own,
                                                  const SensorBasePtr& _sensor,
+                                                 const TimeStamp& _ts,
                                                  const VectorXs& _data,
                                                  const MatrixXs& _data_cov,
-                                                 const FrameBasePtr& _frame_own,
+                                                 const VectorXs& _calib,
+                                                 const VectorXs& _calib_preint,
                                                  const FrameBasePtr& _frame_origin)
 {
     CaptureMotionPtr capture = createCapture(_ts,
@@ -443,16 +449,8 @@ CaptureMotionPtr ProcessorMotion::emplaceCapture(const TimeStamp& _ts,
                                              _data_cov,
                                              _frame_origin);
 
-    // Only assign calibration params to this Capture if these parameters are dynamic
-    if (getSensorPtr()->isExtrinsicDynamic() || getSensorPtr()->isIntrinsicDynamic())
-    {
-        if (origin_ptr_)
-            // get calib params from origin capture
-            capture->setCalibration(origin_ptr_->getCalibration());
-        else
-            // get calib params from sensor
-            capture->setCalibration(getSensorPtr()->getCalibration());
-    }
+    capture->setCalibration(_calib);
+    capture->setCalibrationPreint(_calib_preint);
 
     // add to wolf tree
     _frame_own->addCapture(capture);
diff --git a/src/processor_motion.h b/src/processor_motion.h
index 5e88ba9a070b98674a0d625f83dc4bb508ae94b6..51e2fdb491f19d3291a170a6cf7306f069491f13 100644
--- a/src/processor_motion.h
+++ b/src/processor_motion.h
@@ -371,11 +371,13 @@ class ProcessorMotion : public ProcessorBase
          * \param _frame_own frame owning the Capture to create
          * \param _frame_origin frame acting as the origin of motions for the MorionBuffer of the created MotionCapture
          */
-        CaptureMotionPtr emplaceCapture(const TimeStamp& _ts,
+        CaptureMotionPtr emplaceCapture(const FrameBasePtr& _frame_own,
                                         const SensorBasePtr& _sensor,
+                                        const TimeStamp& _ts,
                                         const VectorXs& _data,
                                         const MatrixXs& _data_cov,
-                                        const FrameBasePtr& _frame_own,
+                                        const VectorXs& _calib,
+                                        const VectorXs& _calib_preint,
                                         const FrameBasePtr& _frame_origin);
 
         virtual CaptureMotionPtr createCapture(const TimeStamp& _ts,
diff --git a/src/processor_odom_3D.cpp b/src/processor_odom_3D.cpp
index 8a1eee69cbc863434d6269784c0a74692d053528..8dcfa2b61e8c279d400c4f5d093d83d933bb2639 100644
--- a/src/processor_odom_3D.cpp
+++ b/src/processor_odom_3D.cpp
@@ -12,6 +12,8 @@ ProcessorOdom3D::ProcessorOdom3D(ProcessorOdom3DParamsPtr _params, SensorOdom3DP
                 q1_(nullptr), q2_(nullptr), q_out_(nullptr)
         {
             setup(_sensor_ptr);
+            delta_ = deltaZero();
+            delta_integrated_ = deltaZero();
             jacobian_delta_preint_.setZero(delta_cov_size_, delta_cov_size_);
             jacobian_delta_.setZero(delta_cov_size_, delta_cov_size_);
         }
@@ -293,14 +295,14 @@ bool ProcessorOdom3D::voteForKeyFrame()
         return true;
     }
     // distance traveled
-    Scalar dist = delta_integrated_.head(3).norm();
+    Scalar dist = getMotion().delta_integr_.head(3).norm();
     if (dist > dist_traveled_)
     {
         WOLF_DEBUG( "PM: vote: distance traveled" );
         return true;
     }
     // angle turned
-    Scalar angle = 2.0 * acos(delta_integrated_(6));
+    Scalar angle = q2v(Quaternions(getMotion().delta_integr_.data()+3)).norm(); // 2.0 * acos(getMotion().delta_integr_(6));
     if (angle > angle_turned_)
     {
         WOLF_DEBUG( "PM: vote: angle turned" );
diff --git a/src/rotations.h b/src/rotations.h
index caa57f0d442e7a0c3afc66b1315a16a6ba8aebe0..35776c25c9b5121cfe81f1b22b216b5fbfef9efd 100644
--- a/src/rotations.h
+++ b/src/rotations.h
@@ -71,9 +71,9 @@ inline Eigen::Matrix<typename Derived::Scalar, 3, 3> skew(const Eigen::MatrixBas
 
     Eigen::Matrix<T, 3, 3> sk;
 
-    sk <<   0.0 , -_v(2), +_v(1),
-          +_v(2),   0.0 , -_v(0),
-          -_v(1), +_v(0),  0.0;
+    sk << (T)0.0 , -_v(2), +_v(1),
+           +_v(2), (T)0.0, -_v(0),
+           -_v(1), +_v(0), (T)0.0;
 
     return sk;
 }
@@ -354,7 +354,7 @@ inline Eigen::Matrix<typename Derived::Scalar, 3, 3> jac_SO3_right_inv(const Eig
         return Eigen::Matrix<T, 3, 3>::Identity() + (T)0.5 * W; // Small angle approximation
     T theta = sqrt(theta2);  // rotation angle
     Eigen::Matrix<T, 3, 3> M;
-    M.noalias() = ((T)1 / theta2 - (1 + cos(theta)) / ((T)2 * theta * sin(theta))) * (W * W);
+    M.noalias() = ((T)1.0 / theta2 - ((T)1.0 + cos(theta)) / ((T)2.0 * theta * sin(theta))) * (W * W);
     return Eigen::Matrix<T, 3, 3>::Identity() + (T)0.5 * W + M; //is this really more optimized?
 }
 
@@ -426,28 +426,62 @@ inline Eigen::Matrix<typename Derived::Scalar, 3, 3> jac_SO3_left_inv(const Eige
         return Eigen::Matrix<T, 3, 3>::Identity() + (T)0.5 * W; // Small angle approximation
     T theta = sqrt(theta2);  // rotation angle
     Eigen::Matrix<T, 3, 3> M;
-    M.noalias() = ((T)1 / theta2 - (1 + cos(theta)) / ((T)2 * theta * sin(theta))) * (W * W);
+    M.noalias() = ((T)1.0 / theta2 - ((T)1.0 + cos(theta)) / ((T)2.0 * theta * sin(theta))) * (W * W);
     return Eigen::Matrix<T, 3, 3>::Identity() - (T)0.5 * W + M; //is this really more optimized?
 }
 
+template<typename D1, typename D2, typename D3, typename D4, typename D5>
+inline void compose(const Eigen::QuaternionBase<D1>& _q1,
+                    const Eigen::QuaternionBase<D2>& _q2,
+                    Eigen::QuaternionBase<D3>& _q_comp,
+                    Eigen::MatrixBase<D4>& _J_comp_q1,
+                    Eigen::MatrixBase<D5>& _J_comp_q2)
+{
+    MatrixSizeCheck<3, 3>::check(_J_comp_q1);
+    MatrixSizeCheck<3, 3>::check(_J_comp_q2);
+
+    _q_comp = _q1 * _q2;
+
+    _J_comp_q1 = q2R(_q2.conjugate()); //  R2.tr
+    _J_comp_q2 . setIdentity();
+}
+
+template<typename D1, typename D2, typename D3, typename D4, typename D5>
+inline void between(const Eigen::QuaternionBase<D1>& _q1,
+                    const Eigen::QuaternionBase<D2>& _q2,
+                    Eigen::QuaternionBase<D3>& _q_between,
+                    Eigen::MatrixBase<D4>& _J_between_q1,
+                    Eigen::MatrixBase<D5>& _J_between_q2)
+{
+    MatrixSizeCheck<3, 3>::check(_J_between_q1);
+    MatrixSizeCheck<3, 3>::check(_J_between_q2);
+
+    _q_between = _q1.conjugate() * _q2;
+
+    _J_between_q1 = -q2R(_q2.conjugate()*_q1); // - R2.tr * R1
+    _J_between_q2 . setIdentity();
+}
+
 template<typename T>
 inline Eigen::Matrix<T, 3, 3> matrixRollPitchYaw(const T roll,
                                                  const T pitch,
                                                  const T yaw)
 {
-  const Eigen::AngleAxis<T> ax = Eigen::AngleAxis<T>(roll,  Eigen::Matrix<T, 3, 1>::UnitX());
-  const Eigen::AngleAxis<T> ay = Eigen::AngleAxis<T>(pitch, Eigen::Matrix<T, 3, 1>::UnitY());
-  const Eigen::AngleAxis<T> az = Eigen::AngleAxis<T>(yaw,   Eigen::Matrix<T, 3, 1>::UnitZ());
+    const Eigen::AngleAxis<T> ax = Eigen::AngleAxis<T>(roll,  Eigen::Matrix<T, 3, 1>::UnitX());
+    const Eigen::AngleAxis<T> ay = Eigen::AngleAxis<T>(pitch, Eigen::Matrix<T, 3, 1>::UnitY());
+    const Eigen::AngleAxis<T> az = Eigen::AngleAxis<T>(yaw,   Eigen::Matrix<T, 3, 1>::UnitZ());
 
-  return (az * ay * ax).toRotationMatrix().matrix();
+    return (az * ay * ax).toRotationMatrix().matrix();
 }
 
 template <typename Derived>
 inline typename Eigen::MatrixBase<Derived>::Scalar
-getYaw(const Eigen::MatrixBase<Derived>& r)
+getYaw(const Eigen::MatrixBase<Derived>& R)
 {
-  using std::atan2;
-  return atan2( r(1, 0), r(0, 0) );
+    MatrixSizeCheck<3, 3>::check(R);
+
+    using std::atan2;
+    return atan2( R(1, 0), R(0, 0) );
 }
 
 } // namespace wolf
diff --git a/src/sensor_base.cpp b/src/sensor_base.cpp
index d78c08c1f45fc1fdc3e313882ba3ccc473bb3078..fc88b489e679f4f22acb159600c2fddb20974c9e 100644
--- a/src/sensor_base.cpp
+++ b/src/sensor_base.cpp
@@ -25,6 +25,8 @@ SensorBase::SensorBase(const std::string& _type,
         noise_std_(_noise_size),
         noise_cov_(_noise_size, _noise_size)
 {
+    noise_std_.setZero();
+    noise_cov_.setZero();
     state_block_vec_[0] = _p_ptr;
     state_block_vec_[1] = _o_ptr;
     state_block_vec_[2] = _intr_ptr;
@@ -53,9 +55,7 @@ SensorBase::SensorBase(const std::string& _type,
     state_block_vec_[0] = _p_ptr;
     state_block_vec_[1] = _o_ptr;
     state_block_vec_[2] = _intr_ptr;
-    noise_cov_.setZero();
-    for (unsigned int i = 0; i < _noise_std.size(); i++)
-        noise_cov_(i, i) = noise_std_(i) * noise_std_(i);
+    setNoiseStd(_noise_std);
     updateCalibSize();
 }
 
@@ -203,11 +203,19 @@ void SensorBase::registerNewStateBlocks()
     }
 }
 
-void SensorBase::setNoise(const Eigen::VectorXs& _noise_std) {
-	noise_std_ = _noise_std;
-	noise_cov_.setZero();
-	for (unsigned int i=0; i<_noise_std.size(); i++)
-		noise_cov_(i,i) = _noise_std(i) * _noise_std(i);
+void SensorBase::setNoiseStd(const Eigen::VectorXs& _noise_std) {
+    noise_std_ = _noise_std;
+    noise_cov_.setZero();
+    for (unsigned int i=0; i<_noise_std.size(); i++)
+    {
+        Scalar var_i = _noise_std(i) * _noise_std(i);
+        noise_cov_(i,i) = var_i;
+    }
+}
+
+void SensorBase::setNoiseCov(const Eigen::MatrixXs& _noise_cov) {
+    noise_std_ = _noise_cov.diagonal().array().sqrt();
+    noise_cov_ = _noise_cov;
 }
 
 CaptureBasePtr SensorBase::lastCapture(const TimeStamp& _ts)
diff --git a/src/sensor_base.h b/src/sensor_base.h
index 0ee9736e74902f726d6a9ce29e67ea04d6f03e80..07eb80a0db04a61df97557ae7e0013be853713cf 100644
--- a/src/sensor_base.h
+++ b/src/sensor_base.h
@@ -145,7 +145,8 @@ class SensorBase : public NodeBase, public std::enable_shared_from_this<SensorBa
         bool extrinsicsInCaptures() const { return extrinsic_dynamic_ && has_capture_; }
         bool intrinsicsInCaptures() const { return intrinsic_dynamic_ && has_capture_; }
 
-        void setNoise(const Eigen::VectorXs & _noise_std);
+        void setNoiseStd(const Eigen::VectorXs & _noise_std);
+        void setNoiseCov(const Eigen::MatrixXs & _noise_std);
         Eigen::VectorXs getNoiseStd();
         Eigen::MatrixXs getNoiseCov();
 
diff --git a/src/sensor_odom_3D.cpp b/src/sensor_odom_3D.cpp
index e65d2eb907feb4c07801152a76ee2998b98ee4e3..21acafbbb484f81cbedf94376e370e6504fa9b5b 100644
--- a/src/sensor_odom_3D.cpp
+++ b/src/sensor_odom_3D.cpp
@@ -21,6 +21,7 @@ SensorOdom3D::SensorOdom3D(StateBlockPtr _p_ptr, StateQuaternionPtr _o_ptr, Intr
         min_rot_var_(params->min_rot_var)
 {
     noise_cov_ = (Eigen::Vector6s() << min_disp_var_, min_disp_var_, min_disp_var_, min_rot_var_, min_rot_var_, min_rot_var_).finished().asDiagonal();
+    setNoiseCov(noise_cov_); // sets also noise_std_
 }
 
 SensorOdom3D::~SensorOdom3D()
diff --git a/src/test/gtest_constraint_imu.cpp b/src/test/gtest_constraint_imu.cpp
index 2b3dcc789431ee0ead33f650a8f66544d542bc73..33702a760048897b6781b6b4bd4a429429248d38 100644
--- a/src/test/gtest_constraint_imu.cpp
+++ b/src/test/gtest_constraint_imu.cpp
@@ -101,7 +101,7 @@ class ConstraintIMU_biasTest_Static_NullBias : public testing::Test
         Eigen::Vector6s data_imu;
         data_imu << -wolf::gravity(), 0,0,0;
 
-        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, Eigen::Matrix6s::Identity(), origin_bias); //set data on IMU (measure only gravity here)
+        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, sen_imu->getNoiseCov(), origin_bias); //set data on IMU (measure only gravity here)
 
         for(unsigned int i = 0; i < 1000; i++) //integrate during 1 second
         {
@@ -170,7 +170,7 @@ class ConstraintIMU_biasTest_Static_NonNullAccBias : public testing::Test
         x_origin.resize(10);
         x_origin << 0,0,0, 0,0,0,1, 0,0,0;
         t.set(0);
-        origin_bias << 0.002, 0.005, 0.1, 0,0,0;
+        origin_bias << 0.02, 0.05, 0.1, 0,0,0;
 
         expected_final_state = x_origin; //null bias + static
 
@@ -185,7 +185,7 @@ class ConstraintIMU_biasTest_Static_NonNullAccBias : public testing::Test
         data_imu << -wolf::gravity(), 0,0,0;
         data_imu = data_imu + origin_bias;
 
-        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, Eigen::Matrix6s::Identity(), origin_bias); //set data on IMU (measure only gravity here)
+        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, sen_imu->getNoiseCov(), origin_bias); //set data on IMU (measure only gravity here)
 
         for(unsigned int i = 0; i < 1000; i++) //integrate during 1 second
         {
@@ -236,9 +236,9 @@ class ConstraintIMU_biasTest_Static_NonNullGyroBias : public testing::Test
 
         // CERES WRAPPER
         ceres::Solver::Options ceres_options;
-        ceres_options.minimizer_type = ceres::TRUST_REGION; //ceres::TRUST_REGION;ceres::LINE_SEARCH
-        ceres_options.max_line_search_step_contraction = 1e-3;
-        ceres_options.max_num_iterations = 1e4;
+//        ceres_options.minimizer_type = ceres::TRUST_REGION; //ceres::TRUST_REGION;ceres::LINE_SEARCH
+//        ceres_options.max_line_search_step_contraction = 1e-3;
+//        ceres_options.max_num_iterations = 1e4;
         ceres_manager_wolf_diff = new CeresManager(wolf_problem_ptr_, ceres_options);
 
         // SENSOR + PROCESSOR IMU
@@ -254,9 +254,7 @@ class ConstraintIMU_biasTest_Static_NonNullGyroBias : public testing::Test
         x_origin.resize(10);
         x_origin << 0,0,0, 0,0,0,1, 0,0,0;
         t.set(0);
-        origin_bias << 0, 0, 0, 0.07,-0.035,-0.1;
-        origin_bias *= 1e-2;
-        //origin_bias << 0.002, 0.005, 0.1, 0.07,-0.035,-0.1;
+        origin_bias << 0, 0, 0, 0.0002, 0.0005, 0.001;
 
         expected_final_state = x_origin; //null bias + static,
 
@@ -271,7 +269,7 @@ class ConstraintIMU_biasTest_Static_NonNullGyroBias : public testing::Test
         data_imu << -wolf::gravity(), 0,0,0;
         data_imu = data_imu + origin_bias;
 
-        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, Eigen::Matrix6s::Identity() );//, origin_bias); //set data on IMU (measure only gravity here)
+        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, sen_imu->getNoiseCov() );//, origin_bias); //set data on IMU (measure only gravity here)
 
         for(unsigned int i = 0; i < 1000; i++) //integrate during 1 second
         {
@@ -357,7 +355,7 @@ class ConstraintIMU_biasTest_Static_NonNullBias : public testing::Test
         data_imu << -wolf::gravity(), 0,0,0;
         data_imu = data_imu + origin_bias;
 
-        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, Eigen::Matrix6s::Identity(), Eigen::Vector6s::Zero()); //set data on IMU (measure only gravity here)
+        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, sen_imu->getNoiseCov(), Eigen::Vector6s::Zero()); //set data on IMU (measure only gravity here)
 
         for(unsigned int i = 0; i < 1000; i++) //integrate during 1 second
         {
@@ -445,7 +443,7 @@ class ConstraintIMU_biasTest_Move_NullBias : public testing::Test
         //===================================================== PROCESS DATA
         // PROCESS DATA
 
-        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, Eigen::Matrix6s::Identity(), Eigen::Vector6s::Zero());
+        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, sen_imu->getNoiseCov(), Eigen::Vector6s::Zero());
 
         for(unsigned int i = 0; i < 1000; i++) //integrate during 1 second
         {
@@ -528,7 +526,7 @@ class ConstraintIMU_biasTest_Move_NonNullBias : public testing::Test
         //===================================================== PROCESS DATA
         // PROCESS DATA
 
-        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, Eigen::Matrix6s::Identity(), Eigen::Vector6s::Zero());
+        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, sen_imu->getNoiseCov(), Eigen::Vector6s::Zero());
 
         for(unsigned int i = 0; i < 1000; i++) //integrate during 1 second
         {
@@ -617,7 +615,7 @@ class ConstraintIMU_biasTest_Move_NonNullBiasRotCst : public testing::Test
         //===================================================== END{INITIALIZATION}
         //===================================================== PROCESS DATA
 
-        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, Eigen::Matrix6s::Identity(), Eigen::Vector6s::Zero());
+        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, sen_imu->getNoiseCov(), Eigen::Vector6s::Zero());
 
         for(unsigned int i = 0; i < 1000; i++) //integrate during 1 second
         {
@@ -716,7 +714,7 @@ class ConstraintIMU_biasTest_Move_NonNullBiasRotAndVCst : public testing::Test
         //===================================================== PROCESS DATA
         // PROCESS DATA
 
-        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, Eigen::Matrix6s::Identity(), Eigen::Vector6s::Zero());
+        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(t, sen_imu, data_imu, sen_imu->getNoiseCov(), Eigen::Vector6s::Zero());
 
         for(unsigned int i = 0; i < 1000; i++) //integrate during 1 second
         {
@@ -790,10 +788,9 @@ class ConstraintIMU_biasTest_Move_NonNullBiasRot : public testing::Test
         expected_final_state.resize(10);
         x_origin.resize(10);
         x_origin << 0,0,0, 0,0,0,1, 0,0,0;
-        origin_bias << 0.0015, 0.004, -0.002, 0.005, -0.0074, -0.003;
-        origin_bias *= 0.01;
+        origin_bias << 0.01, 0.02, 0.003, 0.002, 0.005, 0.01;
         t.set(0);
-        Eigen::Quaternions current_quatState(Eigen::Quaternions::Identity());
+        Eigen::Quaternions quat_current(Eigen::Quaternions::Identity());
 
         //set origin of the problem
         origin_KF = processor_ptr_imu->setOrigin(x_origin, t);
@@ -807,20 +804,20 @@ class ConstraintIMU_biasTest_Move_NonNullBiasRot : public testing::Test
 
         Scalar dt(0.001);
         TimeStamp ts(0);
-        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(ts, sen_imu, data_imu, 0.01*Eigen::Matrix6s::Identity(), Eigen::Vector6s::Zero());
+        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(ts, sen_imu, data_imu, sen_imu->getNoiseCov(), Eigen::Vector6s::Zero());
 
         while( ts.get() < 1 )
         {
             // PROCESS IMU DATA
             // Time and data variables
-            ts.set(ts.get() + dt);
+            ts += dt;
             
-            rateOfTurn = Eigen::Vector3s::Random()*10; //to have rate of turn > 0 deg/s
-            data_imu.tail(3) = rateOfTurn* M_PI/180.0;
-            data_imu.head(3) =  current_quatState.conjugate() * (- wolf::gravity()); //gravity measured, we have no other translation movement
+            rateOfTurn << .1, .2, .3; //to have rate of turn > 0 deg/s
+            data_imu.head(3) = quat_current.conjugate() * (- wolf::gravity()); //gravity measured, we have no other translation movement
+            data_imu.tail(3) = rateOfTurn;
 
             //compute current orientaton taking this measure into account
-            current_quatState = current_quatState * wolf::v2q(data_imu.tail(3)*dt);
+            quat_current = quat_current * wolf::v2q(rateOfTurn*dt);
 
             //set timestamp, add bias, set data and process
             imu_ptr->setTimeStamp(ts);
@@ -829,7 +826,7 @@ class ConstraintIMU_biasTest_Move_NonNullBiasRot : public testing::Test
             sen_imu->process(imu_ptr);
         }
 
-        expected_final_state << 0,0,0, current_quatState.x(), current_quatState.y(), current_quatState.z(), current_quatState.w(), 0,0,0;
+        expected_final_state << 0,0,0, quat_current.x(), quat_current.y(), quat_current.z(), quat_current.w(), 0,0,0;
         last_KF = wolf_problem_ptr_->getTrajectoryPtr()->closestKeyFrameToTimeStamp(ts);
         last_KF->setState(expected_final_state);
 
@@ -845,15 +842,18 @@ class ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRot : public testing::Test
 {
     public:
         wolf::TimeStamp t;
-        SensorIMUPtr sen_imu;
-        SensorOdom3DPtr sen_odom3D;
-        ProblemPtr wolf_problem_ptr_;
-        CeresManagerPtr ceres_manager_wolf_diff;
-        ProcessorBasePtr processor_ptr_;
-        ProcessorIMUPtr processor_ptr_imu;
-        ProcessorOdom3DPtr processor_ptr_odom3D;
+        ProblemPtr problem;
+        CeresManagerPtr ceres_manager;
+        SensorBasePtr sensor;
+        SensorIMUPtr sensor_imu;
+        SensorOdom3DPtr sensor_odo;
+        ProcessorBasePtr processor;
+        ProcessorIMUPtr processor_imu;
+        ProcessorOdom3DPtr processor_odo;
         FrameBasePtr origin_KF;
         FrameBasePtr last_KF;
+        CaptureIMUPtr    capture_imu;
+        CaptureOdom3DPtr capture_odo;
         Eigen::Vector6s origin_bias;
         Eigen::VectorXs expected_final_state;
         Eigen::VectorXs x_origin;
@@ -868,111 +868,156 @@ class ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRot : public testing::Test
         
         //===================================================== SETTING PROBLEM
         // WOLF PROBLEM
-        wolf_problem_ptr_ = Problem::create("POV 3D");
+        problem = Problem::create("POV 3D");
 
         // CERES WRAPPER
         ceres::Solver::Options ceres_options;
         ceres_options.minimizer_type = ceres::TRUST_REGION;
-//        ceres_options.max_line_search_step_contraction = 1e-3;
-//        ceres_options.max_num_iterations = 1e4;
-        ceres_manager_wolf_diff = std::make_shared<CeresManager>(wolf_problem_ptr_, ceres_options);
+        ceres_manager = std::make_shared<CeresManager>(problem, ceres_options);
 
         // SENSOR + PROCESSOR IMU
-        SensorBasePtr sen0_ptr = wolf_problem_ptr_->installSensor("IMU", "Main IMU", (Vector7s()<<0,0,0,0,0,0,1).finished(), wolf_root + "/src/examples/sensor_imu.yaml");
-        processor_ptr_ = wolf_problem_ptr_->installProcessor("IMU", "IMU pre-integrator", "Main IMU", wolf_root + "/src/examples/processor_imu_t6.yaml");
-        sen_imu = std::static_pointer_cast<SensorIMU>(sen0_ptr);
-        processor_ptr_imu = std::static_pointer_cast<ProcessorIMU>(processor_ptr_);
+        sensor          = problem->installSensor("IMU", "Main IMU", (Vector7s()<<0,0,0,0,0,0,1).finished(), wolf_root + "/src/examples/sensor_imu.yaml");
+        sensor_imu      = std::static_pointer_cast<SensorIMU>(sensor);
+        processor       = problem->installProcessor("IMU", "IMU pre-integrator", "Main IMU", wolf_root + "/src/examples/processor_imu_t6.yaml");
+        processor_imu   = std::static_pointer_cast<ProcessorIMU>(processor);
 
         // SENSOR + PROCESSOR ODOM 3D
-        SensorBasePtr sen1_ptr = wolf_problem_ptr_->installSensor("ODOM 3D", "odom", (Vector7s()<<0,0,0,0,0,0,1).finished(), wolf_root + "/src/examples/sensor_odom_3D_HQ.yaml");
+        sensor          = problem->installSensor("ODOM 3D", "odom", (Vector7s()<<0,0,0,0,0,0,1).finished(), wolf_root + "/src/examples/sensor_odom_3D_HQ.yaml");
+        sensor_odo      = std::static_pointer_cast<SensorOdom3D>(sensor);
+
+        sensor_imu->setNoiseStd((sensor_imu->getNoiseStd()/10.0).eval());
+        sensor_odo->setNoiseStd((sensor_odo->getNoiseStd()/10.0).eval());
+        WOLF_TRACE("IMU cov: ", sensor_imu->getNoiseCov().diagonal().transpose());
+        WOLF_TRACE("ODO cov: ", sensor_odo->getNoiseCov().diagonal().transpose());
+
         ProcessorOdom3DParamsPtr prc_odom3D_params = std::make_shared<ProcessorOdom3DParams>();
-        prc_odom3D_params->max_time_span = 0.9999;
-        prc_odom3D_params->max_buff_length = 1000000000; //make it very high so that this condition will not pass
-        prc_odom3D_params->dist_traveled = 1000000000;
-        prc_odom3D_params->angle_turned = 1000000000;
+        prc_odom3D_params->max_time_span    = 0.0099;
+        prc_odom3D_params->max_buff_length  = 1000; //make it very high so that this condition will not pass
+        prc_odom3D_params->dist_traveled    = 1000;
+        prc_odom3D_params->angle_turned     = 1000;
 
-        ProcessorBasePtr processor_ptr_odom = wolf_problem_ptr_->installProcessor("ODOM 3D", "odom", sen1_ptr, prc_odom3D_params);
-        sen_odom3D = std::static_pointer_cast<SensorOdom3D>(sen1_ptr);
-        processor_ptr_odom3D = std::static_pointer_cast<ProcessorOdom3D>(processor_ptr_odom);
+        processor       = problem->installProcessor("ODOM 3D", "odom", sensor_odo, prc_odom3D_params);
+        processor_odo   = std::static_pointer_cast<ProcessorOdom3D>(processor);
 
-        WOLF_TRACE("IMU noise cov: ", sen0_ptr->getNoiseCov());
-        WOLF_TRACE("ODO noise cov: ", sen1_ptr->getNoiseCov());
-    
         //===================================================== END{SETTING PROBLEM}
         //===================================================== INITIALIZATION
 
-        expected_final_state.resize(10);
         x_origin.resize(10);
         x_origin << 0,0,0, 0,0,0,1, 0,0,0;
         origin_bias << 0.0015, 0.004, -0.002, 0.005, -0.0074, -0.003;
+//        origin_bias /= 100;
         t.set(0);
-        Eigen::Quaternions odom_quat(Eigen::Quaternions::Identity());
-        Eigen::Quaternions current_quatState(Eigen::Quaternions::Identity());
 
         //set origin of the problem
 
-        origin_KF = processor_ptr_imu->setOrigin(x_origin, t);
-        processor_ptr_odom3D->setOrigin(origin_KF);
+        origin_KF = processor_imu->setOrigin(x_origin, t);
+        processor_odo->setOrigin(origin_KF);
 
         //===================================================== END{INITIALIZATION}
         //===================================================== PROCESS DATA
         // PROCESS DATA
 
-        Eigen::Vector6s data_imu(Eigen::Vector6s::Zero()), data_odom3D(Eigen::Vector6s::Zero());
+        Eigen::Vector6s data_imu(Eigen::Vector6s::Zero()),
+                        data_odo(Eigen::Vector6s::Zero());
         Eigen::Vector3s rateOfTurn(Eigen::Vector3s::Zero()); //deg/s
 
-        Scalar   dt_imu(0.001), dt_odo(1.0);
         TimeStamp t_imu(0.0),    t_odo(0.0);
-        wolf::CaptureIMUPtr     imu_ptr = std::make_shared<CaptureIMU>   (t_imu, sen_imu, data_imu, Eigen::Matrix6s::Identity()*1e-6, Eigen::Vector6s::Zero());
-        wolf::CaptureOdom3DPtr  odo_ptr = std::make_shared<CaptureOdom3D>(t_odo, sen_odom3D, data_odom3D, nullptr);
-        sen_odom3D->process(odo_ptr);
-        //first odometry data will be processed at this timestamp
-        t_odo += dt_odo;
+        Scalar   dt_imu(0.001), dt_odo(.01);
 
-        //when we find a IMU timestamp corresponding with this odometry timestamp then we process odometry measurement
+        capture_imu = std::make_shared<CaptureIMU>   (t_imu, sensor_imu, data_imu, sensor_imu->getNoiseCov(), sensor_imu->getCalibration(), nullptr);
+
+        capture_odo = std::make_shared<CaptureOdom3D>(t_odo, sensor_odo, data_odo, sensor_odo->getNoiseCov(), nullptr);
+        sensor_odo->process(capture_odo);
+        t_odo += dt_odo;        //first odometry data will be processed at this timestamp
+
+        // ground truth for quaternion:
+        Eigen::Quaternions quat_odo(Eigen::Quaternions::Identity());
+        Eigen::Quaternions quat_imu(Eigen::Quaternions::Identity());
+
+        WOLF_TRACE("last delta preint: ", processor_imu->getLastPtr()->getDeltaPreint().transpose());
+        WOLF_TRACE("last jacoob bias : ", processor_imu->getLastPtr()->getJacobianCalib().row(0));
 
         for(unsigned int i = 1; i<=1000; i++)
         {
+
             // PROCESS IMU DATA
             // Time and data variables
-            t_imu.set(i*dt_imu);
+            t_imu += dt_imu;
             
-            rateOfTurn = Eigen::Vector3s::Random()*10; //to have rate of turn > 0.99 deg/s
-            data_imu.tail<3>() = rateOfTurn* M_PI/180.0;
-            data_imu.head<3>() =  current_quatState.conjugate() * (- wolf::gravity()); //gravity measured, we have no other translation movement
+//            rateOfTurn = Eigen::Vector3s::Random()*10; //to have rate of turn > 0.99 deg/s
+            rateOfTurn << 5, 10, 15; // deg/s
+            data_imu.tail<3>() = rateOfTurn * M_PI/180.0;
+            data_imu.head<3>() =  quat_imu.conjugate() * (- wolf::gravity()); //gravity measured, we have no other translation movement
 
             //compute odometry + current orientaton taking this measure into account
-            odom_quat = odom_quat * wolf::v2q(data_imu.tail(3)*dt_imu);
-            current_quatState = current_quatState * wolf::v2q(data_imu.tail(3)*dt_imu);
+            quat_odo = quat_odo * wolf::v2q(data_imu.tail(3)*dt_imu);
+            quat_imu = quat_imu * wolf::v2q(data_imu.tail(3)*dt_imu);
 
             //set timestamp, add bias, set data and process
-            imu_ptr->setTimeStamp(t_imu);
+            capture_imu->setTimeStamp(t_imu);
             data_imu = data_imu + origin_bias;
-            imu_ptr->setData(data_imu);
-            sen_imu->process(imu_ptr);
+            capture_imu->setData(data_imu);
+            sensor_imu->process(capture_imu);
+
+            WOLF_TRACE("last delta preint: ", processor_imu->getLastPtr()->getDeltaPreint().transpose());
+            WOLF_TRACE("last jacoob bias : ", processor_imu->getLastPtr()->getJacobianCalib().row(0));
 
+            //when we find a IMU timestamp corresponding with this odometry timestamp then we process odometry measurement
             if(t_imu.get() >= t_odo.get())
             {
+                WOLF_TRACE("====== create ODOM KF ========");
+//                WOLF_TRACE("Jac calib: ", processor_imu->getLastPtr()->getJacobianCalib().row(0));
+//                WOLF_TRACE("last calib: ", processor_imu->getLastPtr()->getCalibration().transpose());
+//                WOLF_TRACE("last calib preint: ", processor_imu->getLastPtr()->getCalibrationPreint().transpose());
+
                 // PROCESS ODOM 3D DATA
-                data_odom3D.head(3) << 0,0,0;
-                data_odom3D.tail(3) = q2v(odom_quat);
-                odo_ptr->setTimeStamp(t_odo);
-                odo_ptr->setData(data_odom3D);
-                sen_odom3D->process(odo_ptr);
+                data_odo.head(3) << 0,0,0;
+                data_odo.tail(3) = q2v(quat_odo);
+                capture_odo->setTimeStamp(t_odo);
+                capture_odo->setData(data_odo);
+
+//                WOLF_TRACE("Jac calib: ", processor_imu->getLastPtr()->getJacobianCalib().row(0));
+//                WOLF_TRACE("last calib: ", processor_imu->getLastPtr()->getCalibration().transpose());
+//                WOLF_TRACE("last calib preint: ", processor_imu->getLastPtr()->getCalibrationPreint().transpose());
+
+                sensor_odo->process(capture_odo);
+
+//                WOLF_TRACE("Jac calib: ", std::static_pointer_cast<CaptureMotion>(processor_imu->getOriginPtr())->getJacobianCalib().row(0));
+//                WOLF_TRACE("orig calib: ", processor_imu->getOriginPtr()->getCalibration().transpose());
+//                WOLF_TRACE("orig calib preint: ", std::static_pointer_cast<CaptureMotion>(processor_imu->getOriginPtr())->getCalibrationPreint().transpose());
 
                 //prepare next odometry measurement
-                odom_quat = Eigen::Quaternions::Identity(); //set to identity to have next odom relative to this last KF
+                quat_odo = Eigen::Quaternions::Identity(); //set to identity to have next odom relative to this last KF
                 t_odo += dt_odo;
+                break;
             }
         }
 
-        expected_final_state << 0,0,0, current_quatState.x(), current_quatState.y(), current_quatState.z(), current_quatState.w(), 0,0,0;
-        last_KF = wolf_problem_ptr_->getTrajectoryPtr()->closestKeyFrameToTimeStamp(t_imu);
+        expected_final_state.resize(10);
+        expected_final_state << 0,0,0, quat_imu.x(), quat_imu.y(), quat_imu.z(), quat_imu.w(), 0,0,0;
+
+        last_KF = problem->getTrajectoryPtr()->closestKeyFrameToTimeStamp(t_imu);
         last_KF->setState(expected_final_state);
 
         //===================================================== END{PROCESS DATA}
         origin_KF->unfix();
         last_KF->unfix();
+
+        CaptureBasePtr origin_CB = origin_KF->getCaptureOf(sensor_imu);
+        CaptureMotionPtr last_CM   = std::static_pointer_cast<CaptureMotion>(last_KF  ->getCaptureOf(sensor_imu));
+
+//        WOLF_TRACE("KF1 calib    : ", origin_CB->getCalibration().transpose());
+//        WOLF_TRACE("KF2 calib pre: ", last_CM  ->getCalibrationPreint().transpose());
+//        WOLF_TRACE("KF2 calib    : ", last_CM  ->getCalibration().transpose());
+//        WOLF_TRACE("KF2 delta pre: ", last_CM  ->getDeltaPreint().transpose());
+//        WOLF_TRACE("KF2 delta cor: ", last_CM  ->getDeltaCorrected(origin_CB->getCalibration()).transpose());
+//        WOLF_TRACE("KF2 jacob    : ", last_CM  ->getJacobianCalib().row(0));
+
+
+        // ==================================================== show problem status
+
+        problem->print(4,1,1,1);
+
     }
 
     virtual void TearDown(){}
@@ -1058,8 +1103,8 @@ class ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRotY : public testing::Test
 
         Scalar dt(0.0010), dt_odom(1.0);
         TimeStamp ts(0.0), t_odom(0.0);
-        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(ts, sen_imu, data_imu, Eigen::Matrix6s::Identity(), Eigen::Vector6s::Zero());
-        wolf::CaptureMotionPtr mot_ptr = std::make_shared<CaptureMotion>(t, sen_odom3D, data_odom3D, 7, 6, nullptr);
+        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(ts, sen_imu, data_imu, sen_imu->getNoiseCov(), Eigen::Vector6s::Zero());
+        wolf::CaptureMotionPtr mot_ptr = std::make_shared<CaptureMotion>(t, sen_odom3D, data_odom3D, sen_odom3D->getNoiseCov(), 7, 6, nullptr);
         sen_odom3D->process(mot_ptr);
         //first odometry data will be processed at this timestamp
         t_odom.set(t_odom.get() + dt_odom);
@@ -1190,7 +1235,7 @@ class ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRotXY : public testing::Test
 
         Scalar dt(0.0010), dt_odom(1.0);
         TimeStamp ts(0.0), t_odom(1.0);
-        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(ts, sen_imu, data_imu, Eigen::Matrix6s::Identity(), Eigen::Vector6s::Zero());
+        wolf::CaptureIMUPtr imu_ptr = std::make_shared<CaptureIMU>(ts, sen_imu, data_imu, sen_imu->getNoiseCov(), Eigen::Vector6s::Zero());
         wolf::CaptureMotionPtr mot_ptr = std::make_shared<CaptureMotion>(t, sen_odom3D, data_odom3D, 7, 6, nullptr);
         sen_odom3D->process(mot_ptr);
         //first odometry data will be processed at this timestamp
@@ -1536,12 +1581,8 @@ TEST_F(ConstraintIMU_biasTest_Static_NonNullGyroBias,VarB1B2_InvarP1Q1V1P2Q2V2_i
     last_KF->getOPtr()->fix();
     last_KF->getVPtr()->fix();
 
-    WOLF_DEBUG("bias before solving : ",origin_KF->getCaptureOf(sen_imu)->getCalibration().transpose());
-
     std::string report = ceres_manager_wolf_diff->solve(1); // 0: nothing, 1: BriefReport, 2: FullReport;
 
-    WOLF_DEBUG("bias after  solving : ",origin_KF->getCaptureOf(sen_imu)->getCalibration().transpose());
-
     //Only biases are unfixed
     ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
     ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
@@ -1587,12 +1628,8 @@ TEST_F(ConstraintIMU_biasTest_Static_NonNullGyroBias,VarB1B2_InvarP1Q1V1P2Q2V2_E
     origin_KF->getCaptureOf(sen_imu)->setCalibration(perturbed_bias);
     last_KF->getCaptureOf(sen_imu)->setCalibration(origin_bias);
 
-    WOLF_DEBUG("bias before solving : ",origin_KF->getCaptureOf(sen_imu)->getCalibration().transpose());
-
     report = ceres_manager_wolf_diff->solve(1); // 0: nothing, 1: BriefReport, 2: FullReport;
 
-    WOLF_DEBUG("bias after  solving : ",origin_KF->getCaptureOf(sen_imu)->getCalibration().transpose());
-
     //Only biases are unfixed
     ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
     ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
@@ -2134,26 +2171,26 @@ TEST_F(ConstraintIMU_biasTest_Move_NonNullBiasRotAndVCst, VarB1B2V1P2V2_InvarP1Q
 
 }
 
-TEST_F(ConstraintIMU_biasTest_Move_NonNullBiasRot, VarB1B2V1P2V2_InvarP1Q1Q2_initOK)
-{
-    //prepare problem for solving
-    origin_KF->getPPtr()->fix();
-    origin_KF->getOPtr()->fix();
-    origin_KF->getVPtr()->unfix();
-
-    last_KF->getPPtr()->unfix();
-    last_KF->getOPtr()->fix();
-    last_KF->getVPtr()->unfix();
-
-    last_KF->setState(expected_final_state);
-
-    std::string report = ceres_manager_wolf_diff->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
-
-    //Only biases are unfixed
-    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
-    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
-
-}
+//TEST_F(ConstraintIMU_biasTest_Move_NonNullBiasRot, VarB1B2V1P2V2_InvarP1Q1Q2_initOK)
+//{
+//    //prepare problem for solving
+//    origin_KF->getPPtr()->fix();
+//    origin_KF->getOPtr()->fix();
+//    origin_KF->getVPtr()->unfix();
+//
+//    last_KF->getPPtr()->unfix();
+//    last_KF->getOPtr()->fix();
+//    last_KF->getVPtr()->unfix();
+//
+//    last_KF->setState(expected_final_state);
+//
+//    std::string report = ceres_manager_wolf_diff->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
+//
+//    //Only biases are unfixed
+//    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-3)
+//    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-3)
+//
+//}
 
 TEST_F(ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRotY, VarB1B2_InvarP1Q1V1P2Q2V2_initOK)
 {
@@ -2180,20 +2217,20 @@ TEST_F(ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRotY, VarB1B2_InvarP1Q1V1P2Q2
     ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
     ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
 
-    Eigen::Matrix<wolf::Scalar, 16, 1> cov_stdev, actual_state(last_KF->getState());
-    Eigen::MatrixXs covX(16,16);
+    Eigen::Matrix<wolf::Scalar, 10, 1> cov_stdev, actual_state(last_KF->getState());
+    Eigen::MatrixXs covX(10,10);
         
     //get data from covariance blocks
     wolf_problem_ptr_->getFrameCovariance(last_KF, covX);
 
-    for(int i = 0; i<16; i++)
+    for(int i = 0; i<10; i++)
         cov_stdev(i) = ( covX(i,i)? 2*sqrt(covX(i,i)):0); //if diagonal value is 0 then store 0 else store 2*sqrt(diag_value)
     
     /*TEST_COUT << "2*std : " << cov_stdev.transpose();
     TEST_COUT << "expect : " << expected_final_state.transpose(); //expected final state
     TEST_COUT << "estim : " << last_KF->getState().transpose(); //estimated final state*/
 
-    for(unsigned int i = 0; i<16; i++)
+    for(unsigned int i = 0; i<10; i++)
         assert((expected_final_state(i) <= actual_state(i) + cov_stdev(i)) && (expected_final_state(i) >= actual_state(i) - cov_stdev(i)));
 
     if(cov_stdev.tail(6).maxCoeff()>=1)
@@ -2226,20 +2263,20 @@ TEST_F(ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRotY, VarB1B2V2_InvarP1Q1V1P2
 
     ASSERT_MATRIX_APPROX(last_KF->getVPtr()->getState(), expected_final_state.segment(7,3), wolf::Constants::EPS*100)
 
-    Eigen::Matrix<wolf::Scalar, 16, 1> cov_stdev, actual_state(last_KF->getState());
-    Eigen::MatrixXs covX(16,16);
+    Eigen::Matrix<wolf::Scalar, 10, 1> cov_stdev, actual_state(last_KF->getState());
+    Eigen::MatrixXs covX(10,10);
         
     //get data from covariance blocks
     wolf_problem_ptr_->getFrameCovariance(last_KF, covX);
 
-    for(int i = 0; i<16; i++)
+    for(int i = 0; i<10; i++)
         cov_stdev(i) = ( covX(i,i)? 2*sqrt(covX(i,i)):0); //if diagonal value is 0 then store 0 else store 2*sqrt(diag_value)
     
     /*TEST_COUT << "2*std : " << cov_stdev.transpose();
     TEST_COUT << "expect : " << expected_final_state.transpose(); //expected final state
     TEST_COUT << "estim : " << last_KF->getState().transpose(); //estimated final state*/
 
-    for(unsigned int i = 0; i<16; i++)
+    for(unsigned int i = 0; i<10; i++)
         assert((expected_final_state(i) <= actual_state(i) + cov_stdev(i)) && (expected_final_state(i) >= actual_state(i) - cov_stdev(i)));
 
     if(cov_stdev.tail(6).maxCoeff()>=1)
@@ -2393,20 +2430,20 @@ TEST_F(ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRotY, VarB1B2P2Q2V2_InvarP1Q1
     Eigen::Map<const Eigen::Quaternions> estimatedLastQuat(last_KF->getOPtr()->getState().data()), expectedLastQuat(expected_final_state.segment(3,4).data());
     ASSERT_QUATERNION_APPROX(estimatedLastQuat, expectedLastQuat, wolf::Constants::EPS*100)
 
-    Eigen::Matrix<wolf::Scalar, 16, 1> cov_stdev, actual_state(last_KF->getState());
-    Eigen::MatrixXs covX(16,16);
+    Eigen::Matrix<wolf::Scalar, 10, 1> cov_stdev, actual_state(last_KF->getState());
+    Eigen::MatrixXs covX(10,10);
         
     //get data from covariance blocks
     wolf_problem_ptr_->getFrameCovariance(last_KF, covX);
 
-    for(int i = 0; i<16; i++)
+    for(int i = 0; i<10; i++)
         cov_stdev(i) = ( covX(i,i)? 2*sqrt(covX(i,i)):0); //if diagonal value is 0 then store 0 else store 2*sqrt(diag_value)
     
     /*TEST_COUT << "2*std : " << cov_stdev.transpose();
     TEST_COUT << "expect : " << expected_final_state.transpose(); //expected final state
     TEST_COUT << "estim : " << last_KF->getState().transpose(); //estimated final state*/
 
-    for(unsigned int i = 0; i<16; i++)
+    for(unsigned int i = 0; i<10; i++)
         assert((expected_final_state(i) <= actual_state(i) + cov_stdev(i)) && (expected_final_state(i) >= actual_state(i) - cov_stdev(i)));
 
     if(cov_stdev.tail(6).maxCoeff()>=1)
@@ -2451,20 +2488,20 @@ TEST_F(ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRotY, VarQ1B1B2P2Q2_InvarP1V1
     Eigen::Map<const Eigen::Quaternions> estimatedLastQuat(last_KF->getOPtr()->getState().data()), expectedLastQuat(expected_final_state.segment(3,4).data());
     ASSERT_QUATERNION_APPROX(estimatedLastQuat, expectedLastQuat, wolf::Constants::EPS*100)
 
-    Eigen::Matrix<wolf::Scalar, 16, 1> cov_stdev, actual_state(last_KF->getState());
-    Eigen::MatrixXs covX(16,16);
+    Eigen::Matrix<wolf::Scalar, 10, 1> cov_stdev, actual_state(last_KF->getState());
+    Eigen::MatrixXs covX(10,10);
         
     //get data from covariance blocks
     wolf_problem_ptr_->getFrameCovariance(last_KF, covX);
 
-    for(int i = 0; i<16; i++)
+    for(int i = 0; i<10; i++)
         cov_stdev(i) = ( covX(i,i)? 2*sqrt(covX(i,i)):0); //if diagonal value is 0 then store 0 else store 2*sqrt(diag_value)
     
     /*TEST_COUT << "2*std : " << cov_stdev.transpose();
     TEST_COUT << "expect : " << expected_final_state.transpose(); //expected final state
     TEST_COUT << "estim : " << last_KF->getState().transpose(); //estimated final state*/
 
-    for(unsigned int i = 0; i<16; i++)
+    for(unsigned int i = 0; i<10; i++)
         assert((expected_final_state(i) <= actual_state(i) + cov_stdev(i)) && (expected_final_state(i) >= actual_state(i) - cov_stdev(i)));
     
     if(cov_stdev.tail(6).maxCoeff()>=1)
@@ -2509,20 +2546,20 @@ TEST_F(ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRotXY, VarQ1B1B2P2Q2_InvarP1V
     Eigen::Map<const Eigen::Quaternions> estimatedLastQuat(last_KF->getOPtr()->getState().data()), expectedLastQuat(expected_final_state.segment(3,4).data());
     ASSERT_QUATERNION_APPROX(estimatedLastQuat, expectedLastQuat, wolf::Constants::EPS*100)
 
-    Eigen::Matrix<wolf::Scalar, 16, 1> cov_stdev, actual_state(last_KF->getState());
-    Eigen::MatrixXs covX(16,16);
+    Eigen::Matrix<wolf::Scalar, 10, 1> cov_stdev, actual_state(last_KF->getState());
+    Eigen::MatrixXs covX(10,10);
         
     //get data from covariance blocks
     wolf_problem_ptr_->getFrameCovariance(last_KF, covX);
 
-    for(int i = 0; i<16; i++)
+    for(int i = 0; i<10; i++)
         cov_stdev(i) = ( covX(i,i)? 2*sqrt(covX(i,i)):0); //if diagonal value is 0 then store 0 else store 2*sqrt(diag_value)
     
     /*TEST_COUT << "2*std : " << cov_stdev.transpose();
     TEST_COUT << "expect : " << expected_final_state.transpose(); //expected final state
     TEST_COUT << "estim : " << last_KF->getState().transpose(); //estimated final state*/
 
-    for(unsigned int i = 0; i<16; i++)
+    for(unsigned int i = 0; i<10; i++)
         assert((expected_final_state(i) <= actual_state(i) + cov_stdev(i)) && (expected_final_state(i) >= actual_state(i) - cov_stdev(i)));
     
     if(cov_stdev.tail(6).maxCoeff()>=1)
@@ -2544,30 +2581,36 @@ TEST_F(ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRot, VarB1B2_InvarP1Q1V1P2Q2V
 
     //perturbation of origin bias
     Eigen::Vector6s random_err(Eigen::Vector6s::Random() * 0.001);
-    Eigen::Vector6s bias = origin_KF->getCaptureOf(sen_imu)->getCalibration();
-    origin_KF->getCaptureOf(sen_imu)->setCalibration(bias + random_err);
+    Eigen::Vector6s bias = origin_KF->getCaptureOf(sensor_imu)->getCalibration();
+    origin_KF->getCaptureOf(sensor_imu)->setCalibration(bias + random_err);
 
-    std::string report = ceres_manager_wolf_diff->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
-    ceres_manager_wolf_diff->computeCovariances(ALL);
+//    WOLF_TRACE("real   bias : ", origin_bias.transpose());
+//    WOLF_TRACE("origin bias : ", origin_KF->getCaptureOf(sensor_imu)->getCalibration().transpose());
+//    WOLF_TRACE("last   bias : ", last_KF->getCaptureOf(sensor_imu)->getCalibration().transpose());
+//    WOLF_TRACE("jacob  bias : ", std::static_pointer_cast<CaptureIMU>(last_KF->getCaptureOf(sensor_imu))->getJacobianCalib().row(0));
+
+    std::string report = ceres_manager->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
+    std::cout << report << std::endl;
+    ceres_manager->computeCovariances(ALL);
 
     //Only biases are unfixed
-    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
-    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
+    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sensor_imu)->getCalibration(), origin_bias, 1e-5)
+    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sensor_imu)->getCalibration(), origin_bias, 1e-5)
 
-    Eigen::Matrix<wolf::Scalar, 16, 1> cov_stdev, actual_state(last_KF->getState());
-    Eigen::MatrixXs covX(16,16);
+    Eigen::Matrix<wolf::Scalar, 10, 1> cov_stdev, actual_state(last_KF->getState());
+    Eigen::MatrixXs covX(10,10);
         
     //get data from covariance blocks
-    wolf_problem_ptr_->getFrameCovariance(last_KF, covX);
+    problem->getFrameCovariance(last_KF, covX);
 
-    for(int i = 0; i<16; i++)
+    for(int i = 0; i<10; i++)
         cov_stdev(i) = ( covX(i,i)? 2*sqrt(covX(i,i)):0); //if diagonal value is 0 then store 0 else store 2*sqrt(diag_value)
     
     /*TEST_COUT << "2*std : " << cov_stdev.transpose();
     TEST_COUT << "expect : " << expected_final_state.transpose(); //expected final state
     TEST_COUT << "estim : " << last_KF->getState().transpose(); //estimated final state*/
 
-    for(unsigned int i = 0; i<16; i++)
+    for(unsigned int i = 0; i<10; i++)
         assert((expected_final_state(i) <= actual_state(i) + cov_stdev(i)) && (expected_final_state(i) >= actual_state(i) - cov_stdev(i)));
 
     if(cov_stdev.tail(6).maxCoeff()>=1)
@@ -2589,31 +2632,31 @@ TEST_F(ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRot, VarB1B2V2_InvarP1Q1V1P2Q
 
     //perturbation of origin bias
     Eigen::Vector6s random_err(Eigen::Vector6s::Random() * 0.001);
-    Eigen::Vector6s bias = origin_KF->getCaptureOf(sen_imu)->getCalibration();
-    origin_KF->getCaptureOf(sen_imu)->setCalibration(bias + random_err);
+    Eigen::Vector6s bias = origin_KF->getCaptureOf(sensor_imu)->getCalibration();
+    origin_KF->getCaptureOf(sensor_imu)->setCalibration(bias + random_err);
 
-    std::string report = ceres_manager_wolf_diff->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
-    ceres_manager_wolf_diff->computeCovariances(ALL);
+    std::string report = ceres_manager->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
+    ceres_manager->computeCovariances(ALL);
 
-    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
-    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
+    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sensor_imu)->getCalibration(), origin_bias, 1e-5)
+    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sensor_imu)->getCalibration(), origin_bias, 1e-5)
 
     ASSERT_MATRIX_APPROX(last_KF->getVPtr()->getState(), expected_final_state.segment(7,3), wolf::Constants::EPS*100)
 
-    Eigen::Matrix<wolf::Scalar, 16, 1> cov_stdev, actual_state(last_KF->getState());
-    Eigen::MatrixXs covX(16,16);
+    Eigen::Matrix<wolf::Scalar, 10, 1> cov_stdev, actual_state(last_KF->getState());
+    Eigen::MatrixXs covX(10,10);
         
     //get data from covariance blocks
-    wolf_problem_ptr_->getFrameCovariance(last_KF, covX);
+    problem->getFrameCovariance(last_KF, covX);
 
-    for(int i = 0; i<16; i++)
+    for(int i = 0; i<10; i++)
         cov_stdev(i) = ( covX(i,i)? 2*sqrt(covX(i,i)):0); //if diagonal value is 0 then store 0 else store 2*sqrt(diag_value)
     
     /*TEST_COUT << "2*std : " << cov_stdev.transpose();
     TEST_COUT << "expect : " << expected_final_state.transpose(); //expected final state
     TEST_COUT << "estim : " << last_KF->getState().transpose(); //estimated final state*/
 
-    for(unsigned int i = 0; i<16; i++)
+    for(unsigned int i = 0; i<10; i++)
         assert((expected_final_state(i) <= actual_state(i) + cov_stdev(i)) && (expected_final_state(i) >= actual_state(i) - cov_stdev(i)));
 
     if(cov_stdev.tail(6).maxCoeff()>=1)
@@ -2635,14 +2678,14 @@ TEST_F(ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRot, VarB1B2V1V2_InvarP1Q1P2Q
 
     //perturbation of origin bias
     Eigen::Vector6s random_err(Eigen::Vector6s::Random() * 0.00001);
-    Eigen::Vector6s bias = origin_KF->getCaptureOf(sen_imu)->getCalibration();
-    origin_KF->getCaptureOf(sen_imu)->setCalibration(bias + random_err);
+    Eigen::Vector6s bias = origin_KF->getCaptureOf(sensor_imu)->getCalibration();
+    origin_KF->getCaptureOf(sensor_imu)->setCalibration(bias + random_err);
 
-    std::string report = ceres_manager_wolf_diff->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
+    std::string report = ceres_manager->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
 
     ASSERT_MATRIX_APPROX(origin_KF->getVPtr()->getState(), x_origin.segment(7,3), wolf::Constants::EPS*1000)
-    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
-    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
+    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sensor_imu)->getCalibration(), origin_bias, 1e-5)
+    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sensor_imu)->getCalibration(), origin_bias, 1e-5)
 
     ASSERT_MATRIX_APPROX(last_KF->getVPtr()->getState(), expected_final_state.segment(7,3), wolf::Constants::EPS*1000)
 }
@@ -2662,14 +2705,14 @@ TEST_F(ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRot, VarB1B2V1Q2V2_InvarP1Q1P
 
     //perturbation of origin bias
     Eigen::Vector6s random_err(Eigen::Vector6s::Random() * 0.00001);
-    Eigen::Vector6s bias = origin_KF->getCaptureOf(sen_imu)->getCalibration();
-    origin_KF->getCaptureOf(sen_imu)->setCalibration(bias + random_err);
+    Eigen::Vector6s bias = origin_KF->getCaptureOf(sensor_imu)->getCalibration();
+    origin_KF->getCaptureOf(sensor_imu)->setCalibration(bias + random_err);
 
-    std::string report = ceres_manager_wolf_diff->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
+    std::string report = ceres_manager->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
 
     ASSERT_MATRIX_APPROX(origin_KF->getVPtr()->getState(), x_origin.segment(7,3), wolf::Constants::EPS*1000)
-    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
-    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
+    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sensor_imu)->getCalibration(), origin_bias, 1e-5)
+    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sensor_imu)->getCalibration(), origin_bias, 1e-5)
     
     ASSERT_MATRIX_APPROX(last_KF->getVPtr()->getState(), expected_final_state.segment(7,3), wolf::Constants::EPS*1000)
     Eigen::Map<const Eigen::Quaternions> estimatedLastQuat(last_KF->getOPtr()->getState().data()), expectedLastQuat(expected_final_state.segment(3,4).data());
@@ -2691,14 +2734,14 @@ TEST_F(ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRot, VarB1B2V1P2V2_InvarP1Q1Q
 
     //perturbation of origin bias
     Eigen::Vector6s random_err(Eigen::Vector6s::Random() * 0.00001);
-    Eigen::Vector6s bias = origin_KF->getCaptureOf(sen_imu)->getCalibration();
-    origin_KF->getCaptureOf(sen_imu)->setCalibration(bias + random_err);
+    Eigen::Vector6s bias = origin_KF->getCaptureOf(sensor_imu)->getCalibration();
+    origin_KF->getCaptureOf(sensor_imu)->setCalibration(bias + random_err);
 
-    std::string report = ceres_manager_wolf_diff->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
+    std::string report = ceres_manager->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
 
     ASSERT_MATRIX_APPROX(origin_KF->getVPtr()->getState(), x_origin.segment(7,3), wolf::Constants::EPS*1000)
-    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
-    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
+    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sensor_imu)->getCalibration(), origin_bias, 1e-5)
+    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sensor_imu)->getCalibration(), origin_bias, 1e-5)
     
     ASSERT_MATRIX_APPROX(last_KF->getVPtr()->getState(), expected_final_state.segment(7,3), wolf::Constants::EPS*1000)
 }
@@ -2718,14 +2761,14 @@ TEST_F(ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRot, VarB1B2V1P2Q2V2_InvarP1Q
 
     //perturbation of origin bias
     Eigen::Vector6s random_err(Eigen::Vector6s::Random() * 0.0001);
-    Eigen::Vector6s bias = origin_KF->getCaptureOf(sen_imu)->getCalibration();
-    origin_KF->getCaptureOf(sen_imu)->setCalibration(bias + random_err);
+    Eigen::Vector6s bias = origin_KF->getCaptureOf(sensor_imu)->getCalibration();
+    origin_KF->getCaptureOf(sensor_imu)->setCalibration(bias + random_err);
 
-    std::string report = ceres_manager_wolf_diff->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
+    std::string report = ceres_manager->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
 
     ASSERT_MATRIX_APPROX(origin_KF->getVPtr()->getState(), x_origin.segment(7,3), wolf::Constants::EPS*10000)
-    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
-    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
+    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sensor_imu)->getCalibration(), origin_bias, 1e-5)
+    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sensor_imu)->getCalibration(), origin_bias, 1e-5)
     
     ASSERT_MATRIX_APPROX(last_KF->getVPtr()->getState(), expected_final_state.segment(7,3), wolf::Constants::EPS*10000)
     Eigen::Map<const Eigen::Quaternions> estimatedLastQuat(last_KF->getOPtr()->getState().data()), expectedLastQuat(expected_final_state.segment(3,4).data());
@@ -2747,39 +2790,39 @@ TEST_F(ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRot, VarB1B2P2Q2V2_InvarP1Q1V
 
     //perturbation of origin bias
     Eigen::Vector6s random_err(Eigen::Vector6s::Random() * 0.00001);
-    Eigen::Vector6s bias = origin_KF->getCaptureOf(sen_imu)->getCalibration();
-    origin_KF->getCaptureOf(sen_imu)->setCalibration(bias + random_err);
+    Eigen::Vector6s bias = origin_KF->getCaptureOf(sensor_imu)->getCalibration();
+    origin_KF->getCaptureOf(sensor_imu)->setCalibration(bias + random_err);
 
-    std::string report = ceres_manager_wolf_diff->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
-    ceres_manager_wolf_diff->computeCovariances(ALL);
+    std::string report = ceres_manager->solve(1);// 0: nothing, 1: BriefReport, 2: FullReport
+    ceres_manager->computeCovariances(ALL);
 
     ASSERT_MATRIX_APPROX(origin_KF->getVPtr()->getState(), x_origin.segment(7,3), wolf::Constants::EPS*1000)
-    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
-    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sen_imu)->getCalibration(), origin_bias, 1e-5)
+    ASSERT_MATRIX_APPROX(origin_KF->getCaptureOf(sensor_imu)->getCalibration(), origin_bias, 1e-5)
+    ASSERT_MATRIX_APPROX(last_KF  ->getCaptureOf(sensor_imu)->getCalibration(), origin_bias, 1e-5)
     
     ASSERT_MATRIX_APPROX(last_KF->getPPtr()->getState(), expected_final_state.head(3), wolf::Constants::EPS*100)
     ASSERT_MATRIX_APPROX(last_KF->getVPtr()->getState(), expected_final_state.segment(7,3), wolf::Constants::EPS*1000)
     Eigen::Map<const Eigen::Quaternions> estimatedLastQuat(last_KF->getOPtr()->getState().data()), expectedLastQuat(expected_final_state.segment(3,4).data());
     ASSERT_QUATERNION_APPROX(estimatedLastQuat, expectedLastQuat, wolf::Constants::EPS*100)
 
-    Eigen::Matrix<wolf::Scalar, 16, 1> cov_stdev, actual_state(last_KF->getState());
-    Eigen::MatrixXs covX(16,16);
+    Eigen::Matrix<wolf::Scalar, 10, 1> cov_stdev, actual_state(last_KF->getState());
+    Eigen::MatrixXs covX(10,10);
         
     //get data from covariance blocks
-    wolf_problem_ptr_->getFrameCovariance(last_KF, covX);
+    problem->getFrameCovariance(last_KF, covX);
 
-    for(int i = 0; i<16; i++)
+    for(int i = 0; i<10; i++)
         cov_stdev(i) = ( covX(i,i)? 2*sqrt(covX(i,i)):0); //if diagonal value is 0 then store 0 else store 2*sqrt(diag_value)
     
     /*TEST_COUT << "2*std : " << cov_stdev.transpose();
     TEST_COUT << "expect : " << expected_final_state.transpose(); //expected final state
     TEST_COUT << "estim : " << last_KF->getState().transpose(); //estimated final state*/
 
-    for(unsigned int i = 0; i<16; i++)
+    for(unsigned int i = 0; i<10; i++)
         assert((expected_final_state(i) <= actual_state(i) + cov_stdev(i)) && (expected_final_state(i) >= actual_state(i) - cov_stdev(i)));
 
-    if(cov_stdev.tail(6).maxCoeff()>=1)
-        WOLF_WARN("Big 2*stdev on one or more biases! Max coeff :", cov_stdev.tail(6).maxCoeff())
+//    if(cov_stdev.tail(6).maxCoeff()>=1)
+//        WOLF_WARN("Big 2*stdev on one or more biases! Max coeff :", cov_stdev.tail(6).maxCoeff())
 }
 
 //Tests related to noise
@@ -2787,8 +2830,12 @@ TEST_F(ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRot, VarB1B2P2Q2V2_InvarP1Q1V
 int main(int argc, char **argv)
 {
   testing::InitGoogleTest(&argc, argv);
-  //::testing::GTEST_FLAG(filter) = "ConstraintIMU_biasTest_Move_NonNullBiasRot.*:ConstraintIMU_biasTest_Static_NullBias.*:ConstraintIMU_biasTest_Static_NonNullAccBias.*:ConstraintIMU_biasTest_Static_NonNullGyroBias.*";
-  ::testing::GTEST_FLAG(filter) = "ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRot.*";
+  ::testing::GTEST_FLAG(filter) = "ConstraintIMU_biasTest_Move_NonNullBiasRot.*:ConstraintIMU_biasTest_Static_NullBias.*:ConstraintIMU_biasTest_Static_NonNullAccBias.*:ConstraintIMU_biasTest_Static_NonNullGyroBias.*";
+//  ::testing::GTEST_FLAG(filter) = "ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRotY.VarB1B2V1V2_InvarP1Q1P2Q2_initOK";
+//    ::testing::GTEST_FLAG(filter) = "ConstraintIMU_ODOM_biasTest_Move_NonNullBiasRot.VarB1B2_InvarP1Q1V1P2Q2V2_initOK";
+//    ::testing::GTEST_FLAG(filter) = "ConstraintIMU_biasTest_Move_NonNullBiasRot.VarB1B2V1P2V2_InvarP1Q1Q2_initOK";
+
+
 
   return RUN_ALL_TESTS();
 }
diff --git a/src/test/gtest_feature_imu.cpp b/src/test/gtest_feature_imu.cpp
index 0c0ed288631dfe3c39ccfeaefba62280252934ce..bf0956ac15fbeb34f65fbcd227d88382637a7093 100644
--- a/src/test/gtest_feature_imu.cpp
+++ b/src/test/gtest_feature_imu.cpp
@@ -14,7 +14,7 @@ class FeatureIMU_test : public testing::Test
 {
 
     public: //These can be accessed in fixtures
-        wolf::ProblemPtr wolf_problem_ptr_;
+        wolf::ProblemPtr problem;
         wolf::TimeStamp ts;
         wolf::CaptureIMUPtr imu_ptr;
         Eigen::VectorXs state_vec;
@@ -35,12 +35,13 @@ class FeatureIMU_test : public testing::Test
         using std::static_pointer_cast;
 
         // Wolf problem
-        wolf_problem_ptr_ = Problem::create("POV 3D");
+        problem = Problem::create("POV 3D");
         Eigen::VectorXs IMU_extrinsics(7);
         IMU_extrinsics << 0,0,0, 0,0,0,1; // IMU pose in the robot
         IntrinsicsIMUPtr sen_imu_params = std::make_shared<IntrinsicsIMU>();
-        SensorBasePtr sensor_ptr = wolf_problem_ptr_->installSensor("IMU", "Main IMU", IMU_extrinsics, sen_imu_params);
-        processor_ptr_ = wolf_problem_ptr_->installProcessor("IMU", "IMU pre-integrator", "Main IMU", "");
+        SensorBasePtr sensor_ptr = problem->installSensor("IMU", "Main IMU", IMU_extrinsics, sen_imu_params);
+        ProcessorIMUParamsPtr prc_imu_params = std::make_shared<ProcessorIMUParams>();
+        processor_ptr_ = problem->installProcessor("IMU", "IMU pre-integrator", sensor_ptr, prc_imu_params);
 
     // Time and data variables
         TimeStamp t;
@@ -51,7 +52,7 @@ class FeatureIMU_test : public testing::Test
     // Set the origin
         Eigen::VectorXs x0(10);
         x0 << 0,0,0,  0,0,0,1,  0,0,0;
-        origin_frame = wolf_problem_ptr_->getProcessorMotionPtr()->setOrigin(x0, t);  //create a keyframe at origin
+        origin_frame = problem->getProcessorMotionPtr()->setOrigin(x0, t);  //create a keyframe at origin
     
     // Create one capture to store the IMU data arriving from (sensor / callback / file / etc.)
     // give the capture a big covariance, otherwise it will be so small that it won't pass following assertions
@@ -67,27 +68,25 @@ class FeatureIMU_test : public testing::Test
         imu_ptr->setData(data_);
         imu_ptr->setTimeStamp(t);
     // process data in capture
-        WOLF_TRACE("D_ini: ", wolf_problem_ptr_->getProcessorMotionPtr()->getMotion().delta_integr_.transpose());
         sensor_ptr->process(imu_ptr);
-        WOLF_TRACE("D_pre: ", wolf_problem_ptr_->getProcessorMotionPtr()->getMotion().delta_integr_.transpose());
 
     //create Frame
-        ts = wolf_problem_ptr_->getProcessorMotionPtr()->getBuffer().get().back().ts_;
-        state_vec = wolf_problem_ptr_->getProcessorMotionPtr()->getCurrentState();
+        ts = problem->getProcessorMotionPtr()->getBuffer().get().back().ts_;
+        state_vec = problem->getProcessorMotionPtr()->getCurrentState();
    	    last_frame = std::make_shared<FrameBase>(KEY_FRAME, ts, std::make_shared<StateBlock>(state_vec.head(3)), std::make_shared<StateBlock>(state_vec.segment(3,4)), std::make_shared<StateBlock>(state_vec.head(3)));
-        wolf_problem_ptr_->getTrajectoryPtr()->addFrame(last_frame);
+        problem->getTrajectoryPtr()->addFrame(last_frame);
         
     //create a feature
-        delta_preint = wolf_problem_ptr_->getProcessorMotionPtr()->getMotion().delta_integr_;
-        delta_preint_cov = wolf_problem_ptr_->getProcessorMotionPtr()->getMotion().delta_integr_cov_;
-        //feat_imu = std::make_shared<FeatureIMU>(delta_preint, delta_preint_cov);
-        WOLF_TRACE("D_pre: ", delta_preint.transpose());
-        dD_db_jacobians = std::static_pointer_cast<wolf::ProcessorIMU>(wolf_problem_ptr_->getProcessorMotionPtr())->getLastPtr()->getJacobianCalib();
-        feat_imu = std::make_shared<FeatureIMU>(delta_preint, delta_preint_cov, imu_ptr, dD_db_jacobians);
+        delta_preint = problem->getProcessorMotionPtr()->getMotion().delta_integr_;
+        delta_preint_cov = problem->getProcessorMotionPtr()->getMotion().delta_integr_cov_;
+        VectorXs calib_preint = problem->getProcessorMotionPtr()->getBuffer().getCalibrationPreint();
+        dD_db_jacobians = problem->getProcessorMotionPtr()->getMotion().jacobian_calib_;
+        feat_imu = std::make_shared<FeatureIMU>(delta_preint,
+                                                delta_preint_cov,
+                                                calib_preint,
+                                                dD_db_jacobians,
+                                                imu_ptr);
         feat_imu->setCapturePtr(imu_ptr); //associate the feature to a capture
-        WOLF_TRACE("P_pre: ", feat_imu->getDpPreint().transpose());
-        WOLF_TRACE("Q_pre: ", feat_imu->getDqPreint().coeffs().transpose());
-        WOLF_TRACE("V_pre: ", feat_imu->getDvPreint().transpose());
 
     }
 
@@ -115,8 +114,9 @@ TEST_F(FeatureIMU_test, check_frame)
     left_frame->getTimeStamp(t);
     origin_frame->getTimeStamp(ts);
 
-    ASSERT_EQ(t,ts) << "t != ts \t t=" << t << "\t ts=" << ts << std::endl;
+    ASSERT_NEAR(t.get(), ts.get(), wolf::Constants::EPS_SMALL) << "t != ts \t t=" << t << "\t ts=" << ts << std::endl;
     ASSERT_TRUE(origin_frame->isKey());
+    ASSERT_TRUE(last_frame->isKey());
     ASSERT_TRUE(left_frame->isKey());
 
     wolf::StateBlockPtr origin_pptr, origin_optr, origin_vptr, left_pptr, left_optr, left_vptr;
@@ -142,11 +142,7 @@ TEST_F(FeatureIMU_test, access_members)
     Eigen::VectorXs delta(10);
     //dx = 0.5*2*0.1^2 = 0.01; dvx = 2*0.1 = 0.2; dz = 0.5*9.8*0.1^2 = 0.049; dvz = 9.8*0.1 = 0.98
     delta << 0.01,0,0.049, 0,0,0,1, 0.2,0,0.98;
-    ASSERT_MATRIX_APPROX(feat_imu->getDpPreint(), delta.head<3>(), wolf::Constants::EPS_SMALL);
-    ASSERT_MATRIX_APPROX(feat_imu->getDvPreint(), delta.tail<3>(), wolf::Constants::EPS);
-
-    Eigen::Map<const Eigen::Quaternions> delta_quat(delta.segment<4>(3).data());
-    ASSERT_QUATERNION_APPROX(feat_imu->getDqPreint(), delta_quat, wolf::Constants::EPS_SMALL);
+    ASSERT_MATRIX_APPROX(feat_imu->getMeasurement().transpose(), delta.transpose(), wolf::Constants::EPS);
 }
 
 //TEST_F(FeatureIMU_test, addConstraint)
diff --git a/src/test/gtest_imu_tools.cpp b/src/test/gtest_imu_tools.cpp
index b44ddfebb8052a49957c2130bc2b9a6beced5cf4..e53214a6bbee5c0dc2ae5ebfe1a40c3fba4bbdc5 100644
--- a/src/test/gtest_imu_tools.cpp
+++ b/src/test/gtest_imu_tools.cpp
@@ -148,6 +148,22 @@ TEST(IMU_tools, lift_retract)
     ASSERT_MATRIX_APPROX(delta_from_d, delta, 1e-10);
 }
 
+TEST(IMU_tools, plus)
+{
+    VectorXs d1(10), d2(10), d3(10);
+    VectorXs err(9);
+    Vector4s qv = (Vector4s() << 3, 4, 5, 6).finished().normalized();
+    d1 << 0, 1, 2, qv, 7, 8, 9;
+    err << 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09;
+
+    d3.head(3) = d1.head(3) + err.head(3);
+    d3.segment(3,4) = (Quaternions(qv.data()) * exp_q(err.segment(3,3))).coeffs();
+    d3.tail(3) = d1.tail(3) + err.tail(3);
+
+    plus(d1, err, d2);
+    ASSERT_MATRIX_APPROX(diff(d3, d2), VectorXs::Zero(9), 1e-10);
+}
+
 TEST(IMU_tools, diff)
 {
     VectorXs d1(10), d2(10);
@@ -159,6 +175,15 @@ TEST(IMU_tools, diff)
     diff(d1, d2, err);
     ASSERT_MATRIX_APPROX(err, VectorXs::Zero(9), 1e-10);
     ASSERT_MATRIX_APPROX(diff(d1, d2), VectorXs::Zero(9), 1e-10);
+
+    VectorXs d3(10);
+    d3.setRandom(); d3.segment(3,4).normalize();
+    err.head(3) = d3.head(3) - d1.head(3);
+    err.segment(3,3) = log_q(Quaternions(d1.data()+3).conjugate()*Quaternions(d3.data()+3));
+    err.tail(3) = d3.tail(3) - d1.tail(3);
+
+    ASSERT_MATRIX_APPROX(err, diff(d1, d3), 1e-10);
+
 }
 
 TEST(IMU_tools, compose_jacobians)
@@ -201,9 +226,330 @@ TEST(IMU_tools, compose_jacobians)
     ASSERT_MATRIX_APPROX(J2_a, J2_n, 1e-4);
 }
 
+TEST(IMU_tools, diff_jacobians)
+{
+    VectorXs d1(10), d2(10), d3(9), d1_pert(10), d2_pert(10), d3_pert(9); // deltas
+    VectorXs t1(9), t2(9); // tangents
+    Matrix<Scalar, 9, 9> J1_a, J2_a, J1_n, J2_n;
+    Vector4s qv1, qv2;
+    Scalar dx = 1e-6;
+    qv1 = (Vector4s() << 3, 4, 5, 6).finished().normalized();
+    d1 << 0, 1, 2, qv1, 7, 8, 9;
+    qv2 = (Vector4s() << 1, 2, 3, 4).finished().normalized();
+    d2 << 9, 8, 7, qv2, 2, 1, 0;
+
+    // analytical jacobians
+    diff(d1, d2, d3, J1_a, J2_a);
+
+    // numerical jacobians
+    for (unsigned int i = 0; i<9; i++)
+    {
+        t1      . setZero();
+        t1(i)   = dx;
+
+        // Jac wrt first delta
+        d1_pert = plus(d1, t1);                 //          (d1 + t1)
+        d3_pert = diff(d1_pert, d2);            //     d2 - (d1 + t1)
+        t2      = d3_pert - d3;                 //   { d2 - (d1 + t1) } - { d2 - d1 }
+        J1_n.col(i) = t2/dx;                    // [ { d2 - (d1 + t1) } - { d2 - d1 } ] / dx
+
+        // Jac wrt second delta
+        d2_pert = plus(d2, t1);                 //     (d2 + t1)
+        d3_pert = diff(d1, d2_pert);            //     (d2 + t1) - d1
+        t2      = d3_pert - d3;                 //   { (d2 + t1) - d1 } - { d2 - d1 }
+        J2_n.col(i) = t2/dx;                    // [ { (d2 + t1) - d1 } - { d2 - d1 } ] / dx
+    }
+
+    // check that numerical and analytical match
+    ASSERT_MATRIX_APPROX(J1_a, J1_n, 1e-4);
+    ASSERT_MATRIX_APPROX(J2_a, J2_n, 1e-4);
+}
+
+TEST(IMU_tools, body2delta_jacobians)
+{
+    VectorXs delta(10), delta_pert(10); // deltas
+    VectorXs body(6), pert(6);
+    VectorXs tang(9); // tangents
+    Matrix<Scalar, 9, 6> J_a, J_n; // analytic and numerical jacs
+    Vector4s qv;;
+    Scalar dt = 0.1;
+    Scalar dx = 1e-6;
+    qv = (Vector4s() << 3, 4, 5, 6).finished().normalized();
+    delta << 0, 1, 2,   qv,   7, 8, 9;
+    body << 1, 2, 3,   3, 2, 1;
+
+    // analytical jacobians
+    body2delta(body, dt, delta, J_a);
+
+    // numerical jacobians
+    for (unsigned int i = 0; i<6; i++)
+    {
+        pert      . setZero();
+        pert(i)   = dx;
+
+        // Jac wrt first delta
+        delta_pert = body2delta(body + pert, dt);   //     delta(body + pert)
+        tang       = diff(delta, delta_pert);       //   delta(body + pert) -- delta(body)
+        J_n.col(i) = tang/dx;                       // ( delta(body + pert) -- delta(body) ) / dx
+    }
+
+    // check that numerical and analytical match
+    ASSERT_MATRIX_APPROX(J_a, J_n, 1e-4);
+}
+
+/* Create IMU data from body motion
+ * Input:
+ *   motion: [ax, ay, az, wx, wy, wz] the motion in body frame
+ *   q: the current orientation wrt horizontal
+ *   bias: the bias of the IMU
+ * Output:
+ *   return: the data vector as created by the IMU (with motion, gravity, and bias)
+ */
+VectorXs motion2data(const VectorXs& body, const Quaternions& q, const VectorXs& bias)
+{
+    VectorXs data(6);
+    data = body;                                // start with body motion
+    data.head(3) -= q.conjugate()*gravity();    // add -g
+    data = data + bias;                         // add bias
+    return data;
+}
+
+/* Integrate acc and angVel motion, obtain Delta_preintegrated
+ * Input:
+ *   N: number of steps
+ *   q0: initial orientaiton
+ *   motion: [ax, ay, az, wx, wy, wz] as the true magnitudes in body brame
+ *   bias_real: the real bias of the IMU
+ *   bias_preint: the bias used for Delta pre-integration
+ * Output:
+ *   return: the preintegrated delta
+ */
+VectorXs integrateDelta(int N, const Quaternions& q0, const VectorXs& motion, const VectorXs& bias_real, const VectorXs& bias_preint, Scalar dt)
+{
+    VectorXs data(6);
+    VectorXs body(6);
+    VectorXs delta(10);
+    VectorXs Delta(10), Delta_plus(10);
+    Delta << 0,0,0, 0,0,0,1, 0,0,0;
+    Quaternions q(q0);
+    for (int n = 0; n<N; n++)
+    {
+        data        = motion2data(motion, q, bias_real);
+        q           = q*exp_q(motion.tail(3)*dt);
+        body        = data - bias_preint;
+        delta       = body2delta(body, dt);
+        Delta_plus  = compose(Delta, delta, dt);
+        Delta       = Delta_plus;
+    }
+    return Delta;
+}
+
+/* Integrate acc and angVel motion, obtain Delta_preintegrated
+ * Input:
+ *   N: number of steps
+ *   q0: initial orientaiton
+ *   motion: [ax, ay, az, wx, wy, wz] as the true magnitudes in body brame
+ *   bias_real: the real bias of the IMU
+ *   bias_preint: the bias used for Delta pre-integration
+ * Output:
+ *   J_D_b: the Jacobian of the preintegrated delta wrt the bias
+ *   return: the preintegrated delta
+ */
+VectorXs integrateDelta(int N, const Quaternions& q0, const VectorXs& motion, const VectorXs& bias_real, const VectorXs& bias_preint, Scalar dt, Matrix<Scalar, 9, 6>& J_D_b)
+{
+    VectorXs data(6);
+    VectorXs body(6);
+    VectorXs delta(10);
+    Matrix<Scalar, 9, 6> J_d_d, J_d_b;
+    Matrix<Scalar, 9, 9> J_D_D, J_D_d;
+    VectorXs Delta(10), Delta_plus(10);
+    Quaternions q;
+
+    Delta << 0,0,0, 0,0,0,1, 0,0,0;
+    J_D_b.setZero();
+    q = q0;
+    for (int n = 0; n<N; n++)
+    {
+        // Simulate data
+        data = motion2data(motion, q, bias_real);
+        q    = q*exp_q(motion.tail(3)*dt);
+        // Motion::integrateOneStep()
+        {   // IMU::computeCurrentDelta
+            body  = data - bias_preint;
+            body2delta(body, dt, delta, J_d_d);
+            J_d_b = - J_d_d;
+        }
+        {   // IMU::deltaPlusDelta
+            compose(Delta, delta, dt, Delta_plus, J_D_D, J_D_d);
+        }
+        // Motion:: jac calib
+        J_D_b = J_D_D*J_D_b + J_D_d*J_d_b;
+        // Motion:: buffer
+        Delta = Delta_plus;
+    }
+    return Delta;
+}
+
+TEST(IMU_tools, integral_jacobian_bias)
+{
+    VectorXs Delta(10), Delta_pert(10); // deltas
+    VectorXs bias_real(6), pert(6), bias_pert(6), bias_preint(6);
+    VectorXs body(6), data(6), motion(6);
+    VectorXs tang(9); // tangents
+    Matrix<Scalar, 9, 6> J_a, J_n; // analytic and numerical jacs
+    Scalar dt = 0.001;
+    Scalar dx = 1e-4;
+    Quaternions q0(3, 4, 5, 6); q0.normalize();
+    motion << .1, .2, .3,   .3, .2, .1;
+    bias_real   << .002, .004, .001,   .003, .002, .001;
+    bias_preint << .004, .005, .006,   .001, .002, .003;
+
+    int N = 500; // # steps of integration
+
+    // Analytical Jacobians
+    Delta = integrateDelta(N, q0, motion, bias_real, bias_preint, dt, J_a);
+
+    // numerical jacobians
+    for (unsigned int i = 0; i<6; i++)
+    {
+        pert       . setZero();
+        pert(i)    = dx;
+
+        bias_pert  = bias_preint + pert;
+        Delta_pert = integrateDelta(N, q0, motion, bias_real, bias_pert, dt);
+        tang       = diff(Delta, Delta_pert);       //   Delta(bias + pert) -- Delta(bias)
+        J_n.col(i) = tang/dx;                       // ( Delta(bias + pert) -- Delta(bias) ) / dx
+    }
+
+    // check that numerical and analytical match
+    ASSERT_MATRIX_APPROX(J_a, J_n, 1e-4);
+}
+
+TEST(IMU_tools, delta_correction)
+{
+    VectorXs Delta(10), Delta_preint(10), Delta_corr; // deltas
+    VectorXs bias(6), pert(6), bias_preint(6);
+    VectorXs body(6), motion(6);
+    VectorXs tang(9); // tangents
+    Matrix<Scalar, 9, 6> J_b; // analytic and numerical jacs
+    Vector4s qv;;
+    Scalar dt = 0.001;
+    Quaternions q0(3, 4, 5, 6); q0.normalize();
+    motion << 1, 2, 3,   3, 2, 1; motion *= .1;
+
+    int N = 1000; // # steps of integration
+
+    // preintegration with correct bias
+    bias << .004, .005, .006,   .001, .002, .003;
+    Delta = integrateDelta(N, q0, motion, bias, bias, dt);
+
+    // preintegration with wrong bias
+    pert << .002, -.001, .003,   -.0003, .0002, -.0001;
+    bias_preint = bias + pert;
+    Delta_preint = integrateDelta(N, q0, motion, bias, bias_preint, dt, J_b);
+
+    // correct perturbated
+    Vector9s step = J_b*(bias-bias_preint);
+    Delta_corr = plus(Delta_preint, step);
+
+    // Corrected delta should match real delta
+    ASSERT_MATRIX_APPROX(Delta, Delta_corr, 1e-5);
+
+    // diff between real and corrected should be zero
+    ASSERT_MATRIX_APPROX(diff(Delta, Delta_corr), Vector9s::Zero(), 1e-5);
+
+    // diff between preint and corrected deltas should be the jacobian-computed step
+    ASSERT_MATRIX_APPROX(diff(Delta_preint, Delta_corr), step, 1e-5);
+}
+
+TEST(IMU_tools, full_delta_residual)
+{
+    VectorXs x1(10), x2(10);
+    VectorXs Delta(10), Delta_preint(10), Delta_corr(10);
+    VectorXs Delta_real(9), Delta_err(9), Delta_diff(10), Delta_exp(10);
+    VectorXs bias(6), pert(6), bias_preint(6), bias_null(6);
+    VectorXs body(6), motion(6);
+    VectorXs tang(9); // tangents
+    Matrix<Scalar, 9, 6> J_b; // analytic and numerical jacs
+    Scalar dt = 0.001;
+    Quaternions q0; q0.setIdentity();
+
+    x1          << 0,0,0, 0,0,0,1, 0,0,0;
+    motion      <<  .3,    .2,    .1,      .1,     .2,     .3; // acc and gyro
+//    motion      <<  .3,    .2,    .1,      .0,     .0,     .0; // only acc
+//    motion      <<  .0,    .0,    .0,      .1,     .2,     .3; // only gyro
+    bias        << 0.01, 0.02, 0.003,   0.002, 0.005, 0.01;
+    bias_null   << 0,     0,     0,       0,      0,      0;
+//    bias_preint << 0.003, 0.002, 0.001,   0.001,  0.002,  0.003;
+    bias_preint = bias_null;
+
+    int N = 1000; // # steps of integration
+
+    // preintegration with no bias
+    Delta_real = integrateDelta(N, q0, motion, bias_null, bias_null, dt);
+
+    // final state
+    x2 = composeOverState(x1, Delta_real, 1000*dt);
+
+    // preintegration with the correct bias
+    Delta = integrateDelta(N, q0, motion, bias, bias, dt);
+
+    ASSERT_MATRIX_APPROX(Delta, Delta_real, 1e-4);
+
+    // preintegration with wrong bias
+    Delta_preint = integrateDelta(N, q0, motion, bias, bias_preint, dt, J_b);
+
+    // compute correction step
+    Vector9s step = J_b*(bias-bias_preint);
+
+    // correct preintegrated delta
+    Delta_corr = plus(Delta_preint, step);
+
+    // Corrected delta should match real delta
+    ASSERT_MATRIX_APPROX(Delta_real, Delta_corr, 1e-3);
+
+    // diff between real and corrected should be zero
+    ASSERT_MATRIX_APPROX(diff(Delta_real, Delta_corr), Vector9s::Zero(), 1e-3);
+
+    // expected delta
+    Delta_exp = betweenStates(x1, x2, N*dt);
+
+    ASSERT_MATRIX_APPROX(Delta_exp, Delta_corr, 1e-3);
+
+    // compute diff between expected and corrected
+    Matrix<Scalar, 9, 9> J_err_exp, J_err_corr;
+    diff(Delta_exp, Delta_corr, Delta_err, J_err_exp, J_err_corr);
+
+    ASSERT_MATRIX_APPROX(Delta_err, Vector9s::Zero(), 1e-3);
+
+    // compute error between expected and preintegrated
+    Delta_err = diff(Delta_preint, Delta_exp);
+
+    // diff between preint and corrected deltas should be the jacobian-computed step
+    ASSERT_MATRIX_APPROX(diff(Delta_preint, Delta_corr), step, 1e-3);
+    /* This implies:
+     *   - Since D_corr is expected to be similar to D_exp
+     *      step ~ diff(Delta_preint, Delta_exp)
+     *   - the residual can be computed with a regular '-' :
+     *      residual = diff(D_preint, D_exp) - J_bias * (bias - bias_preint)
+     */
+
+//    WOLF_TRACE("Delta real: ", Delta_real.transpose());
+//    WOLF_TRACE("x2: ", x2.transpose());
+//    WOLF_TRACE("Delta: ", Delta.transpose());
+//    WOLF_TRACE("Delta pre: ", Delta_preint.transpose());
+//    WOLF_TRACE("Jac bias: \n", J_b);
+//    WOLF_TRACE("Delta step: ", step.transpose());
+//    WOLF_TRACE("Delta cor: ", Delta_corr.transpose());
+//    WOLF_TRACE("Delta exp: ", Delta_exp.transpose());
+//    WOLF_TRACE("Delta err: ", Delta_err.transpose());
+//    WOLF_TRACE("Delta err exp-pre: ", Delta_err.transpose());
+}
+
 int main(int argc, char **argv)
 {
   testing::InitGoogleTest(&argc, argv);
+//  ::testing::GTEST_FLAG(filter) = "IMU_tools.delta_correction";
   return RUN_ALL_TESTS();
 }
 
diff --git a/src/test/gtest_rotation.cpp b/src/test/gtest_rotation.cpp
index c9028feb65b51be8f7823ae29cb841b6a6b93640..a241b462b6f7a0cc5d344741f32bb33ea888bed9 100644
--- a/src/test/gtest_rotation.cpp
+++ b/src/test/gtest_rotation.cpp
@@ -583,7 +583,7 @@ TEST(rotations, q2R_R2q)
     ASSERT_MATRIX_APPROX(R,          qq_R.matrix(),   wolf::Constants::EPS);
 }
 
-TEST(rotations, Jr)
+TEST(Jacobians, Jr)
 {
     Vector3s theta; theta.setRandom();
     Vector3s dtheta; dtheta.setRandom(); dtheta *= 1e-4;
@@ -595,7 +595,7 @@ TEST(rotations, Jr)
     ASSERT_MATRIX_APPROX(exp_R(theta+dtheta), (exp_R(theta) * exp_R(Jr*dtheta)), 1e-8);
 }
 
-TEST(rotations, Jl)
+TEST(Jacobians, Jl)
 {
     Vector3s theta; theta.setRandom();
     Vector3s dtheta; dtheta.setRandom(); dtheta *= 1e-4;
@@ -605,9 +605,15 @@ TEST(rotations, Jl)
     Matrix3s Jl = jac_SO3_left(theta);
     ASSERT_QUATERNION_APPROX(exp_q(theta+dtheta), exp_q(Jl*dtheta) * exp_q(theta), 1e-8);
     ASSERT_MATRIX_APPROX(exp_R(theta+dtheta), (exp_R(Jl*dtheta) * exp_R(theta)), 1e-8);
+
+    // Jl = Jr.tr
+    ASSERT_MATRIX_APPROX(Jl, jac_SO3_right(theta).transpose(), 1e-8);
+
+    // Jl = R*Jr
+    ASSERT_MATRIX_APPROX(Jl, exp_R(theta)*jac_SO3_right(theta), 1e-8);
 }
 
-TEST(rotations, Jr_inv)
+TEST(Jacobians, Jr_inv)
 {
     Vector3s theta; theta.setRandom();
     Vector3s dtheta; dtheta.setRandom(); dtheta *= 1e-4;
@@ -621,7 +627,7 @@ TEST(rotations, Jr_inv)
     ASSERT_MATRIX_APPROX(log_R(R * exp_R(dtheta)), log_R(R) + Jr_inv*dtheta, 1e-8);
 }
 
-TEST(rotations, Jl_inv)
+TEST(Jacobians, Jl_inv)
 {
     Vector3s theta; theta.setRandom();
     Vector3s dtheta; dtheta.setRandom(); dtheta *= 1e-4;
@@ -635,6 +641,78 @@ TEST(rotations, Jl_inv)
     ASSERT_MATRIX_APPROX(log_R(exp_R(dtheta) * R), log_R(R) + Jl_inv*dtheta, 1e-8);
 }
 
+TEST(Jacobians, compose)
+{
+
+    Vector3s th1(.1,.2,.3), th2(.3,.1,.2);
+    Quaternions q1(exp_q(th1));
+    Quaternions q2(exp_q(th2));
+    Quaternions qc;
+    Matrix3s J1a, J2a, J1n, J2n;
+
+    // composition and analytic Jacobians
+    wolf::compose(q1, q2, qc, J1a, J2a);
+
+    // Numeric Jacobians
+    Scalar dx = 1e-6;
+    Vector3s pert;
+    Quaternions q1_pert, q2_pert, qc_pert;
+    for (int i = 0; i<3; i++)
+    {
+        pert.setZero();
+        pert(i) = dx;
+
+        // Jac wrt q1
+        q1_pert     = q1*exp_q(pert);
+        qc_pert     = q1_pert * q2;
+        J1n.col(i)  = log_q(qc.conjugate()*qc_pert) / dx;
+
+        // Jac wrt q2
+        q2_pert     = q2*exp_q(pert);
+        qc_pert     = q1 * q2_pert;
+        J2n.col(i)  = log_q(qc.conjugate()*qc_pert) / dx;
+    }
+
+    ASSERT_MATRIX_APPROX(J1a, J1n, 1e-5);
+    ASSERT_MATRIX_APPROX(J2a, J2n, 1e-5);
+}
+
+TEST(Jacobians, between)
+{
+
+    Vector3s th1(.1,.2,.3), th2(.3,.1,.2);
+    Quaternions q1(exp_q(th1));
+    Quaternions q2(exp_q(th2));
+    Quaternions qc;
+    Matrix3s J1a, J2a, J1n, J2n;
+
+    // composition and analytic Jacobians
+    wolf::between(q1, q2, qc, J1a, J2a);
+
+    // Numeric Jacobians
+    Scalar dx = 1e-6;
+    Vector3s pert;
+    Quaternions q1_pert, q2_pert, qc_pert;
+    for (int i = 0; i<3; i++)
+    {
+        pert.setZero();
+        pert(i) = dx;
+
+        // Jac wrt q1
+        q1_pert     = q1*exp_q(pert);
+        qc_pert     = q1_pert.conjugate() * q2;
+        J1n.col(i)  = log_q(qc.conjugate()*qc_pert) / dx;
+
+        // Jac wrt q2
+        q2_pert     = q2*exp_q(pert);
+        qc_pert     = q1.conjugate() * q2_pert;
+        J2n.col(i)  = log_q(qc.conjugate()*qc_pert) / dx;
+    }
+
+    ASSERT_MATRIX_APPROX(J1a, J1n, 1e-5);
+    ASSERT_MATRIX_APPROX(J2a, J2n, 1e-5);
+}
+
 
 int main(int argc, char **argv)
 {