diff --git a/include/core/capture/capture_landmarks_external.h b/include/core/capture/capture_landmarks_external.h
index f5c9ad4632d0c704b73c5273c2d9ba3ee7930d0d..27af193e1f73de46ec3f934c0f89b0ff0f2a9750 100644
--- a/include/core/capture/capture_landmarks_external.h
+++ b/include/core/capture/capture_landmarks_external.h
@@ -28,9 +28,10 @@ namespace wolf {
 
 struct LandmarkDetection
 {
+    int id;                      // id of landmark
     Eigen::VectorXd measure;     // either pose or position
     Eigen::MatrixXd covariance;  // covariance of the measure
-    int id;                      // id of landmark
+    double quality;              // [0, 1] quality of the detection
 };
 
 WOLF_PTR_TYPEDEFS(CaptureLandmarksExternal);
@@ -39,21 +40,22 @@ WOLF_PTR_TYPEDEFS(CaptureLandmarksExternal);
 class CaptureLandmarksExternal : public CaptureBase
 {
     protected:
-        std::list<LandmarkDetection> detections_;
+        std::vector<LandmarkDetection> detections_;
 
     public:
 
         CaptureLandmarksExternal(const TimeStamp& _ts, 
                                  SensorBasePtr _sensor_ptr, 
-                                 const std::vector<Eigen::VectorXd>& _detections, 
-                                 const std::vector<Eigen::MatrixXd>& _covs, 
-                                 const std::vector<int>& _ids);
+                                 const std::vector<int>& _ids = {},
+                                 const std::vector<Eigen::VectorXd>& _detections = {}, 
+                                 const std::vector<Eigen::MatrixXd>& _covs = {}, 
+                                 const std::vector<double>& _qualities = {});
 
         ~CaptureLandmarksExternal() override;
 
-        std::list<LandmarkDetection> getDetections() const {return detections_;};
+        std::vector<LandmarkDetection> getDetections() const {return detections_;};
 
-        void addDetection(const Eigen::VectorXd& _detection, const Eigen::MatrixXd& _cov, const int& _id);
+        void addDetection(const int& _id, const Eigen::VectorXd& _detection, const Eigen::MatrixXd& _cov, const double& quality);
 };
 
 } //namespace wolf
\ No newline at end of file
diff --git a/include/core/factor/factor_relative_pose_3d_with_extrinsics.h b/include/core/factor/factor_relative_pose_3d_with_extrinsics.h
index 0204203f330403b3821dcc784706989b4f7c1c08..680c619c3622092e4e0631fb6c74c594794ce70d 100644
--- a/include/core/factor/factor_relative_pose_3d_with_extrinsics.h
+++ b/include/core/factor/factor_relative_pose_3d_with_extrinsics.h
@@ -179,14 +179,14 @@ inline bool FactorRelativePose3dWithExtrinsics::operator ()(const T* const _p_re
     // FRAME CASE
     if (not getFrameOtherList().empty())
     {
+        expected_p = q_r_s.conjugate() * (q_w_r.conjugate() * (p_w_t + q_w_t * p_r_s - p_w_r) - p_r_s);
         expected_q = q_r_s.conjugate() * q_w_r.conjugate() * q_w_t * q_r_s;
-        expected_p = q_r_s.conjugate() * q_w_r.conjugate() * (p_w_t + q_w_t * p_r_s - (p_w_r + q_w_r * p_r_s));
     }
     // LANDMARK CASE
     else if (not getLandmarkOtherList().empty())
     {
+        expected_p = q_r_s.conjugate() * (q_w_r.conjugate() * (p_w_t - p_w_r) - p_r_s);
         expected_q = q_r_s.conjugate() * q_w_r.conjugate() * q_w_t;
-        expected_p = q_r_s.conjugate() * q_w_r.conjugate() * (p_w_t - (p_w_r + q_w_r * p_r_s));
     }
 
     // Measurement
diff --git a/include/core/processor/processor_tracker_feature_landmark_external.h b/include/core/processor/processor_tracker_feature_landmark_external.h
index 9e3173a37d124e18f108818581b8e1fc1d504d0c..578181788acdc0b74cf938d8e1b2e4dd0ce27d55 100644
--- a/include/core/processor/processor_tracker_feature_landmark_external.h
+++ b/include/core/processor/processor_tracker_feature_landmark_external.h
@@ -32,14 +32,16 @@ WOLF_STRUCT_PTR_TYPEDEFS(ParamsProcessorTrackerFeatureLandmarkExternal);
 
 struct ParamsProcessorTrackerFeatureLandmarkExternal : public ParamsProcessorTrackerFeature
 {
-    unsigned int min_track_length; ///< length of the track necessary to consider the detection
+    unsigned int filter_track_length_th;  ///< length of the track necessary to consider the detection
+    double filter_quality_th;             ///< min quality to consider the detection
 
     ParamsProcessorTrackerFeatureLandmarkExternal() = default;
     ParamsProcessorTrackerFeatureLandmarkExternal(std::string _unique_name,
                                        const wolf::ParamsServer & _server):
         ParamsProcessorTrackerFeature(_unique_name, _server)
     {
-        min_track_length = _server.getParam<unsigned int>(prefix + _unique_name + "/min_track_length");
+        filter_track_length_th  = _server.getParam<unsigned int>(prefix + _unique_name + "/filter_track_length_th");
+        filter_quality_th       = _server.getParam<double>      (prefix + _unique_name + "/filter_quality_th");
     }
 };
 
diff --git a/src/capture/capture_landmarks_external.cpp b/src/capture/capture_landmarks_external.cpp
index ebf00489363b359f08e9ec818395948f559623ad..154dcc83f488b39a91c69f0906a0f59e2fdd56d7 100644
--- a/src/capture/capture_landmarks_external.cpp
+++ b/src/capture/capture_landmarks_external.cpp
@@ -25,16 +25,19 @@ namespace wolf{
 
 CaptureLandmarksExternal::CaptureLandmarksExternal(const TimeStamp& _ts, 
                                                    SensorBasePtr _sensor_ptr, 
+                                                   const std::vector<int>& _ids,
                                                    const std::vector<Eigen::VectorXd>& _detections, 
                                                    const std::vector<Eigen::MatrixXd>& _covs, 
-                                                   const std::vector<int>& _ids) :
+                                                   const std::vector<double>& _qualities) :
 	CaptureBase("CaptureLandmarksExternal", _ts, _sensor_ptr)
 {
-    if (_detections.size() != _covs.size() or _detections.size() != _ids.size())
-        throw std::runtime_error("CaptureLandmarksExternal constructor: '_detections', '_covs' and '_ids' should have the same size.");
+    if (_ids.size() != _detections.size() or 
+        _ids.size() != _covs.size() or 
+        _ids.size() != _qualities.size())
+        throw std::runtime_error("CaptureLandmarksExternal constructor: '_ids', '_detections', '_covs', '_qualities' should have the same size.");
     
     for (auto i = 0; i < _detections.size(); i++)
-        addDetection(_detections.at(i), _covs.at(i), _ids.at(i));
+        addDetection(_ids.at(i), _detections.at(i), _covs.at(i), _qualities.at(i));
 }
 
 CaptureLandmarksExternal::~CaptureLandmarksExternal()
@@ -42,9 +45,9 @@ CaptureLandmarksExternal::~CaptureLandmarksExternal()
 	//
 }
 
-void CaptureLandmarksExternal::addDetection(const Eigen::VectorXd& _detection, const Eigen::MatrixXd& _cov, const int& _id)
+void CaptureLandmarksExternal::addDetection(const int& _id, const Eigen::VectorXd& _detection, const Eigen::MatrixXd& _cov, const double& _quality)
 {
-    detections_.push_back(LandmarkDetection{_detection, _cov, _id});
+    detections_.push_back(LandmarkDetection{_id, _detection, _cov, _quality});
 }
 
 } // namespace wolf
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 1aaf3bc58cca2d9157c998e5fd1d5d0c1cf90e56..73af9e2ed1d3f9fbbdc32240eb2ef83d9b07ff46 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -191,6 +191,9 @@ wolf_add_gtest(gtest_map_yaml gtest_map_yaml.cpp)
 # Parameter prior test
 wolf_add_gtest(gtest_param_prior gtest_param_prior.cpp)
 
+# ProcessorTrackerFeatureLandmarkExternal class test
+wolf_add_gtest(gtest_processor_tracker_feature_landmark_external gtest_processor_tracker_feature_landmark_external.cpp)
+
 # ProcessorFixedWingModel class test
 wolf_add_gtest(gtest_processor_fixed_wing_model gtest_processor_fixed_wing_model.cpp)
 
diff --git a/test/gtest_processor_tracker_feature_landmark_external.cpp b/test/gtest_processor_tracker_feature_landmark_external.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..944d0cd90300fc9a85b4a3c6c78a2ec58969ba24
--- /dev/null
+++ b/test/gtest_processor_tracker_feature_landmark_external.cpp
@@ -0,0 +1,273 @@
+//--------LICENSE_START--------
+//
+// Copyright (C) 2020,2021,2022 Institut de Robòtica i Informàtica Industrial, CSIC-UPC.
+// Authors: Joan Solà Ortega (jsola@iri.upc.edu)
+// All rights reserved.
+//
+// This file is part of WOLF
+// WOLF is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//--------LICENSE_END--------
+
+#include "core/utils/utils_gtest.h"
+#include "core/problem/problem.h"
+#include "core/capture/capture_landmarks_external.h"
+#include "core/landmark/landmark_base.h"
+#include "core/state_block/state_block_derived.h"
+#include "core/state_block/state_angle.h"
+#include "core/state_block/state_quaternion.h"
+
+#include "core/processor/processor_tracker_feature_landmark_external.h"
+
+// STL
+#include <iterator>
+#include <iostream>
+
+using namespace wolf;
+using namespace Eigen;
+
+class ProcessorTrackerFeatureLandmarkExternalTest : public testing::Test
+{
+    protected:
+        int dim;
+        bool orientation;
+        TimeStamp t;
+        double dt;
+
+        ProblemPtr problem;
+        SensorBasePtr sensor;
+        ProcessorTrackerFeatureLandmarkExternalPtr processor;
+        std::vector<LandmarkBasePtr> landmarks;
+
+        // Groundtruth poses
+        VectorXd p_robot, o_robot, p_sensor, o_sensor;
+
+        virtual void SetUp() {};
+        void initProblem(int _dim, bool _orientation);
+        CaptureLandmarksExternalPtr randomStep();
+        void addRandomDetection(CaptureLandmarksExternalPtr _cap, int _id) const;
+};
+
+void ProcessorTrackerFeatureLandmarkExternalTest::initProblem(int _dim, bool _orientation)
+{
+    dim = _dim;
+    orientation = _orientation;
+    t = TimeStamp(0);
+    dt = 0.1;
+
+    problem = Problem::create("PO", dim);
+
+    if (dim == 2)
+        sensor = SensorBase::emplace<SensorBase>(problem->getHardware(),
+                                                 "SensorBase",
+                                                 std::make_shared<StatePoint2d>(Vector2d::Random()),
+                                                 std::make_shared<StateAngle>(Vector1d::Random() * M_PI),
+                                                 nullptr,
+                                                 2);
+    else
+        sensor = SensorBase::emplace<SensorBase>(problem->getHardware(),
+                                                 "SensorBase",
+                                                 std::make_shared<StatePoint3d>(Vector3d::Random()),
+                                                 std::make_shared<StateQuaternion>(Vector4d::Random().normalized()),
+                                                 nullptr,
+                                                 3);
+                                                
+
+    // Emplace processor
+    ParamsProcessorTrackerFeatureLandmarkExternalPtr params = std::make_shared<ParamsProcessorTrackerFeatureLandmarkExternal>();
+    params->time_tolerance = dt/2;
+    params->min_features_for_keyframe = 0;
+    params->max_new_features = -1;
+    params->voting_active = true;
+    params->apply_loss_function = false;
+    processor = ProcessorBase::emplace<ProcessorTrackerFeatureLandmarkExternal>(sensor, params);
+
+    // Emplace frame
+    auto F0 = problem->emplaceFrame(t, (dim == 2 ? VectorXd::Zero(3) : (VectorXd(7) << 0,0,0,0,0,0,1).finished()));
+    F0->fix();
+    problem->keyFrameCallback(F0, nullptr);
+
+    // Emplace 3 random landmarks
+    for (auto i = 0; i < 3; i++)
+    {
+        LandmarkBasePtr lmk;
+        if (dim == 2)
+            lmk = LandmarkBase::emplace<LandmarkBase>(problem->getMap(), 
+                                                      "LandmarkExternal",
+                                                      std::make_shared<StatePoint2d>(Vector2d::Random() * 10),
+                                                      (orientation ? 
+                                                        std::make_shared<StateAngle>(Vector1d::Random() * M_PI) : 
+                                                        nullptr));
+    
+        else 
+            lmk = LandmarkBase::emplace<LandmarkBase>(problem->getMap(), 
+                                                      "LandmarkExternal",
+                                                      std::make_shared<StatePoint3d>(Vector3d::Random() * 10),
+                                                      (orientation ? 
+                                                        std::make_shared<StateQuaternion>(Vector4d::Random().normalized()) : 
+                                                        nullptr));
+        lmk->setId(i);
+        landmarks.push_back(lmk);
+    }
+
+    // Store groundtruth poses
+    p_robot = F0->getP()->getState();
+    o_robot = F0->getO()->getState();
+    p_sensor = sensor->getP()->getState();
+    o_sensor = sensor->getO()->getState();
+}
+
+CaptureLandmarksExternalPtr ProcessorTrackerFeatureLandmarkExternalTest::randomStep()
+{
+    // update groundtruth pose with random movement
+    p_robot += VectorXd::Random(dim) * 0.1;
+    if (dim == 2)
+        o_robot(0) = pi2pi(o_robot(0) + Vector1d::Random()(0) * 0.1);
+    else
+        o_robot = (o_robot + Vector4d::Random() * 0.1).normalized();
+
+    // Detections
+    auto cap = std::make_shared<CaptureLandmarksExternal>(t, sensor);
+    VectorXd p_lmk, o_lmk;
+    for (auto i = 0; i < landmarks.size(); i++)
+    {
+        p_lmk = landmarks.at(i)->getP()->getState();
+        if (orientation)
+            o_lmk = landmarks.at(i)->getO()->getState();
+
+        // compute detection
+        VectorXd meas(orientation?(dim==2?3:7):(dim==2?2:3));
+        if (dim == 2)
+        {
+            meas.head(2) = Rotation2Dd(-o_sensor(0))*(Rotation2Dd(-o_robot(0))*(p_lmk - p_robot) - p_sensor);
+            if (orientation)
+                meas(2) = o_lmk(0) - p_robot(0);
+        }
+        else
+        {
+            auto q_sensor = Quaterniond(Vector4d(o_sensor));
+            auto q_robot = Quaterniond(Vector4d(o_robot));
+
+            meas.head(3) = q_sensor.conjugate() * (q_robot.conjugate() * (p_lmk - p_robot) - p_sensor);
+            if (orientation)
+                meas.tail(4) = (q_sensor.conjugate() * q_robot.conjugate() * Quaterniond(Vector4d(o_lmk))).coeffs();
+        }
+        MatrixXd cov = MatrixXd::Identity(meas.size(), meas.size());
+        if (orientation and dim != 2)
+            cov = MatrixXd::Identity(6, 6);
+
+        // add detection
+        cap->addDetection(meas, cov, landmarks.at(i)->id());
+    }
+
+    return cap;
+}
+
+void ProcessorTrackerFeatureLandmarkExternalTest::addRandomDetection(CaptureLandmarksExternalPtr _cap, int _id) const
+{
+    VectorXd detection;
+    MatrixXd cov;
+    if (orientation)
+    {
+        if (dim == 2)
+        {
+            detection = Vector3d::Random()*5;
+            detection(2) = pi2pi(detection(2));
+            cov = Matrix3d::Identity()*0.1;
+        }
+        else
+        {
+            detection = Vector7d::Random()*5;
+            detection.tail<4>().normalize();
+            cov = Matrix6d::Identity()*0.1;
+        }
+    } 
+    else
+    {
+        if (dim == 2)
+        {
+            detection = Vector2d::Random()*5;
+            cov = Matrix2d::Identity()*0.1;
+        }
+        else
+        {
+            detection = Vector3d::Random()*5;
+            cov = Matrix3d::Identity()*0.1;
+        }
+    }
+    _cap->addDetection(detection, cov, _id);
+}
+
+// TESTS /////////////////////////////////////////////////////////////////////////////////
+
+TEST_F(ProcessorTrackerFeatureLandmarkExternalTest, check_p_2d)
+{
+    initProblem(2, false);
+    ASSERT_TRUE(problem->check());
+
+    for (auto i = 0; i<10; i++)
+    {
+        auto cap = randomStep();
+        ASSERT_TRUE(problem->check());
+
+        cap->process();
+        ASSERT_TRUE(problem->check());
+
+        t+=dt;
+    }
+}
+
+TEST_F(ProcessorTrackerFeatureLandmarkExternalTest, check_po_2d)
+{
+    initProblem(2, true);
+    ASSERT_TRUE(problem->check());
+
+    auto cap = randomStep();
+    ASSERT_TRUE(problem->check());
+
+    cap->process();
+    ASSERT_TRUE(problem->check());
+}
+
+TEST_F(ProcessorTrackerFeatureLandmarkExternalTest, check_p_3d)
+{
+    initProblem(3, false);
+    ASSERT_TRUE(problem->check());
+
+    auto cap = randomStep();
+    ASSERT_TRUE(problem->check());
+
+    cap->process();
+    ASSERT_TRUE(problem->check());
+}
+
+TEST_F(ProcessorTrackerFeatureLandmarkExternalTest, check_po_3d)
+{
+    initProblem(3, true);
+    ASSERT_TRUE(problem->check());
+
+    auto cap = randomStep();
+    ASSERT_TRUE(problem->check());
+
+    cap->process();
+    ASSERT_TRUE(problem->check());
+}
+
+
+
+int main(int argc, char **argv)
+{
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}