From 59839d6d539cc042273db634ad8ed8d33f76e62c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joan=20Vallv=C3=A9=20Navarro?= <jvallve@iri.upc.edu>
Date: Mon, 17 Jan 2022 13:07:58 +0100
Subject: [PATCH] After 2nd RAL submission

---
 .gitignore                                    |  37 +-
 .gitlab-ci.yml                                | 219 +++--
 .vscode/c_cpp_properties.json                 |  20 +
 CMakeLists.txt                                | 210 ++---
 LICENSE                                       | 619 +++++++++++++
 README.md                                     |   2 +-
 ROS-based code style eclipse indented 4sp.xml | 167 ----
 ROS-based wolf code style QT indented 4sp.xml |  39 -
 cmake_modules/wolfgnssConfig.cmake            |  21 +-
 codetemplates eclipse.xml                     |  76 --
 include/gnss/capture/capture_gnss.h           |  80 ++
 include/gnss/capture/capture_gnss_fix.h       |  63 +-
 .../gnss/capture/capture_gnss_single_diff.h   |  63 --
 include/gnss/capture/capture_gnss_tdcp.h      |  96 ++
 ...tor_gnss_fix_2D.h => factor_gnss_fix_2d.h} |  77 +-
 ...tor_gnss_fix_3D.h => factor_gnss_fix_3d.h} |  73 +-
 .../gnss/factor/factor_gnss_pseudo_range.h    | 190 ++++
 .../gnss/factor/factor_gnss_single_diff_2d.h  | 126 +++
 include/gnss/factor/factor_gnss_tdcp.h        | 213 +++++
 ...single_diff_2D.h => factor_gnss_tdcp_2d.h} |  71 +-
 include/gnss/factor/factor_gnss_tdcp_3d.h     | 127 +++
 include/gnss/factor/factor_gnss_tdcp_batch.h  | 139 +++
 include/gnss/feature/feature_gnss_fix.h       |  42 +-
 include/gnss/feature/feature_gnss_satellite.h | 101 ++
 .../gnss/feature/feature_gnss_single_diff.h   |  33 -
 include/gnss/feature/feature_gnss_tdcp.h      |  56 ++
 include/gnss/processor/processor_gnss_fix.h   | 157 +++-
 .../processor/processor_gnss_single_diff.h    | 116 ---
 include/gnss/processor/processor_gnss_tdcp.h  | 154 ++++
 .../gnss/processor/processor_tracker_gnss.h   | 322 +++++++
 include/gnss/sensor/sensor_gnss.h             | 249 +++--
 .../tree_manager_sliding_window_tdcp.h        |  64 ++
 internal/config.h.in                          |   4 +-
 license_header_2022.txt                       |  17 +
 package.xml                                   |   2 -
 src/capture/capture_gnss.cpp                  |  53 ++
 src/capture/capture_gnss_fix.cpp              |  27 +-
 src/processor/processor_gnss_fix.cpp          | 322 ++++---
 src/processor/processor_gnss_single_diff.cpp  |  51 +-
 src/processor/processor_gnss_tdcp.cpp         | 270 ++++++
 src/processor/processor_tracker_gnss.cpp      | 870 ++++++++++++++++++
 src/sensor/sensor_gnss.cpp                    | 343 ++++---
 .../tree_manager_sliding_window_tdcp.cpp      |  66 ++
 test/CMakeLists.txt                           |  22 +-
 test/gtest_example.cpp                        |  21 +
 ...ix_2D.cpp => gtest_factor_gnss_fix_2d.cpp} | 232 ++---
 test/gtest_factor_gnss_pseudo_range.cpp       | 322 +++++++
 test/gtest_factor_gnss_single_diff_2D.cpp     | 267 ------
 test/gtest_factor_gnss_tdcp.cpp               | 440 +++++++++
 49 files changed, 5713 insertions(+), 1638 deletions(-)
 create mode 100644 .vscode/c_cpp_properties.json
 create mode 100644 LICENSE
 delete mode 100644 ROS-based code style eclipse indented 4sp.xml
 delete mode 100644 ROS-based wolf code style QT indented 4sp.xml
 delete mode 100644 codetemplates eclipse.xml
 create mode 100644 include/gnss/capture/capture_gnss.h
 delete mode 100644 include/gnss/capture/capture_gnss_single_diff.h
 create mode 100644 include/gnss/capture/capture_gnss_tdcp.h
 rename include/gnss/factor/{factor_gnss_fix_2D.h => factor_gnss_fix_2d.h} (69%)
 rename include/gnss/factor/{factor_gnss_fix_3D.h => factor_gnss_fix_3d.h} (64%)
 create mode 100644 include/gnss/factor/factor_gnss_pseudo_range.h
 create mode 100644 include/gnss/factor/factor_gnss_single_diff_2d.h
 create mode 100644 include/gnss/factor/factor_gnss_tdcp.h
 rename include/gnss/factor/{factor_gnss_single_diff_2D.h => factor_gnss_tdcp_2d.h} (70%)
 create mode 100644 include/gnss/factor/factor_gnss_tdcp_3d.h
 create mode 100644 include/gnss/factor/factor_gnss_tdcp_batch.h
 create mode 100644 include/gnss/feature/feature_gnss_satellite.h
 delete mode 100644 include/gnss/feature/feature_gnss_single_diff.h
 create mode 100644 include/gnss/feature/feature_gnss_tdcp.h
 delete mode 100644 include/gnss/processor/processor_gnss_single_diff.h
 create mode 100644 include/gnss/processor/processor_gnss_tdcp.h
 create mode 100644 include/gnss/processor/processor_tracker_gnss.h
 create mode 100644 include/gnss/tree_manager/tree_manager_sliding_window_tdcp.h
 create mode 100644 license_header_2022.txt
 create mode 100644 src/capture/capture_gnss.cpp
 create mode 100644 src/processor/processor_gnss_tdcp.cpp
 create mode 100644 src/processor/processor_tracker_gnss.cpp
 create mode 100644 src/tree_manager/tree_manager_sliding_window_tdcp.cpp
 rename test/{gtest_factor_gnss_fix_2D.cpp => gtest_factor_gnss_fix_2d.cpp} (51%)
 create mode 100644 test/gtest_factor_gnss_pseudo_range.cpp
 delete mode 100644 test/gtest_factor_gnss_single_diff_2D.cpp
 create mode 100644 test/gtest_factor_gnss_tdcp.cpp

diff --git a/.gitignore b/.gitignore
index ca5fdede9..16b374548 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,36 +1,9 @@
-.cproject
+
+.settings/language.settings.xml
 .project
-.settings/
-README.txt
-bin/
+.cproject
 build/
-build_debug/
-build_release/
+bin/
 lib/
-.idea/
-./Wolf.user
-./Wolf.config
-./Wolf.creator
-./Wolf.files
-./Wolf.includes
-./Wolf/
-/CMakeLists.txt.user
-src/examples/map_polyline_example_write.yaml
-*.gch
-/CMakeFiles/
-/CMakeCache.txt
-*.dat
-.DS_Store
-src/examples
-*.graffle
-/Default/
-
-src/CMakeCache.txt
-
-src/CMakeFiles/cmake.check_cache
-src/examples/map_apriltag_save.yaml
-
-\.vscode/
-build_release/
-
 gnss.found
+
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e90446258..045cce1f0 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,108 +1,187 @@
-image: labrobotica/ceres:1.14
+stages:
+  - license
+  - build_and_test
 
-before_script:
-  ##
+############ YAML ANCHORS ############
+.preliminaries_template: &preliminaries_definition
   ## Install ssh-agent if not already installed, it is required by Docker.
   ## (change apt-get to yum if you use an RPM-based image)
-  ##
   - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
 
-  ##
   ## Run ssh-agent (inside the build environment)
-  ##
   - eval $(ssh-agent -s)
 
-  ##
   ## Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
   ## We're using tr to fix line endings which makes ed25519 keys work
   ## without extra base64 encoding.
   ## https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556
-  ##
   - mkdir -p ~/.ssh
   - chmod 700 ~/.ssh  
   - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
   # - echo "$SSH_KNOWN_HOSTS" > $HOME/.ssh/known_hosts
   - ssh-keyscan -H -p 2202 gitlab.iri.upc.edu >> $HOME/.ssh/known_hosts
 
-  ##
-  ## Create the SSH directory and give it the right permissions
-  ##
-  - ls
+  # update apt
   - apt-get update
-  - apt-get install -y build-essential cmake 
-
-# SPDLOG
-#  - apt-get install -y libspdlog-dev
-  - if [ -d spdlog ]; then
-  -   echo "directory exists" 
-  -   if [ "$(ls -A ./spdlog)" ]; then 
-  -     echo "directory not empty" 
-  -     cd spdlog
-  -     git pull
-  -   else 
-  -     echo "directory empty" 
-  -     git clone https://github.com/gabime/spdlog.git
-  -     cd spdlog
-  -   fi
+
+  # create 'ci_deps' folder (if not exists)
+  - mkdir -pv ci_deps
+
+.license_header_template: &license_header_definition
+  - cd $CI_PROJECT_DIR
+
+  # configure git
+  - export CI_NEW_BRANCH=ci_processing$RANDOM
+  - echo creating new temporary branch... $CI_NEW_BRANCH
+  - git config --global user.email "${CI_EMAIL}"
+  - git config --global user.name "${CI_USERNAME}"
+  - git checkout -b $CI_NEW_BRANCH # temporary branch
+
+  # license headers
+  - export CURRENT_YEAR=$( date +'%Y' )
+  - echo "current year:" ${CURRENT_YEAR}
+  - if [ -f license_header_${CURRENT_YEAR}.txt ]; then
+      # add license headers to new files
+  -   echo "File license_header_${CURRENT_YEAR}.txt already exists. License headers are assumed to be updated. Adding headers to new files..."
+  -   ./ci_deps/wolf/wolf_scripts/license_manager.sh --add --path=. --license-header=license_header_${CURRENT_YEAR}.txt --exclude=ci_deps
+  - else
+      # update license headers of all files
+  -   export PREV_YEAR=$(( CURRENT_YEAR-1 ))
+  -   echo "Creating new file license_header_${CURRENT_YEAR}.txt..."
+  -   git mv license_header_${PREV_YEAR}.txt license_header_${CURRENT_YEAR}.txt
+  -   sed -i "s/${PREV_YEAR}/${PREV_YEAR},${CURRENT_YEAR}/g" license_header_${CURRENT_YEAR}.txt
+  -   ./ci_deps/wolf/wolf_scripts/license_manager.sh --update --path=. --license-header=license_header_${CURRENT_YEAR}.txt --exclude=ci_deps
+  - fi
+
+  # push changes (if any)
+  - if git commit -a -m "[skip ci] license headers added or modified" ; then
+  -   git remote set-url --push origin "ssh://git@gitlab.iri.upc.edu:2202/${CI_PROJECT_PATH}.git"
+  -   git push origin $CI_NEW_BRANCH:${CI_COMMIT_REF_NAME}
   - else
-  -   echo "directory inexistent" 
-  -   git clone https://github.com/gabime/spdlog.git
-  -   cd spdlog
+  -   echo "No changes, nothing to commit!"
+  - fi
+
+.install_wolf_template: &install_wolf_definition
+  - cd ${CI_PROJECT_DIR}/ci_deps
+  - if [ -d wolf ]; then
+  -   echo "directory wolf exists"
+  -   cd wolf
+  -   git checkout devel
+  -   git pull
+  -   git checkout $WOLF_CORE_BRANCH
+  - else
+  -   git clone ssh://git@gitlab.iri.upc.edu:2202/mobile_robotics/wolf_projects/wolf_lib/wolf.git
+  -   cd wolf
+  -   git checkout $WOLF_CORE_BRANCH
   - fi
-  - git fetch
-  - git checkout v0.17.0
   - mkdir -pv build
   - cd build
-  - ls
-  - cmake -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -fPIC" -DSPDLOG_BUILD_TESTING=OFF ..
+  - cmake -DCMAKE_BUILD_TYPE=release -DBUILD_DEMOS=OFF -DBUILD_TESTS=OFF ..
+  - make -j$(nproc)
   - make install
-  - cd ../..
-
-# YAML
-#  - apt-get install -y libyaml-cpp-dev
-  - if [ -d yaml-cpp ]; then
-  -   echo "directory exists" 
-  -   if [ "$(ls -A ./yaml-cpp)" ]; then 
-  -     echo "directory not empty" 
-  -     cd yaml-cpp
-  -     git pull
-  -   else 
-  -     echo "directory empty" 
-  -     git clone https://github.com/jbeder/yaml-cpp.git
-  -     cd yaml-cpp
-  -   fi
+
+.install_gnssutils_template: &install_gnssutils_definition
+  - cd ${CI_PROJECT_DIR}/ci_deps
+  - if [ -d gnss_utils ]; then
+  -   echo "directory gnss_utils exists"
+  -   cd gnss_utils
+  -   git pull
   - else
-  -   echo "directory inexistent" 
-  -   git clone https://github.com/jbeder/yaml-cpp.git
-  -   cd yaml-cpp
+  -   git clone ssh://git@gitlab.iri.upc.edu:2202/mobile_robotics/gauss_project/gnss_utils.git
+  -   cd gnss_utils
+  -   git submodule update --init
   - fi
   - mkdir -pv build
   - cd build
-  - ls
-  - cmake -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -fPIC" -DYAML_CPP_BUILD_TESTS=OFF ..
+  - cmake -DCMAKE_BUILD_TYPE=release-DBUILD_TESTS=ON ..
+  - make -j$(nproc)
+  - ctest -j$(nproc)
   - make install
-  - cd ../..
-#Wolf core
-  - git clone ssh://git@gitlab.iri.upc.edu:2202/mobile_robotics/wolf_projects/wolf_lib/wolf.git
-  - cd wolf
+
+.build_and_test_template: &build_and_test_definition
+  - cd $CI_PROJECT_DIR
   - mkdir -pv build
   - cd build
-  - cmake -DCMAKE_BUILD_TYPE=release -DBUILD_EXAMPLES=ON -DBUILD_TESTS=ON ..
+  - cmake -DCMAKE_BUILD_TYPE=release -DBUILD_TESTS=ON ..
   - make -j$(nproc)
   - ctest -j$(nproc)
   - make install
-  - cd ../..
 
-wolf_build_and_test:
-  stage: build
+############ LICENSE HEADERS ############
+license_headers:
+  stage: license
+  image: labrobotica/wolf_deps:16.04
+  cache:
+    - key: wolf-xenial
+      paths:
+      - ci_deps/wolf/
+  except:
+    - master
+  before_script:
+    - *preliminaries_definition
+    - *install_wolf_definition
+  script:
+    - *license_header_definition
+
+############ UBUNTU 16.04 TESTS ############
+build_and_test:xenial:
+  image: labrobotica/wolf_deps:16.04
+  stage: build_and_test
+  cache:
+    - key: wolf-xenial
+      paths:
+      - ci_deps/wolf/
+    - key: gnssutils-xenial
+      paths:
+      - ci_deps/gnss_utils/
+  except:
+    - master
+  before_script:
+    - *preliminaries_definition
+    - *install_wolf_definition
+    - *install_gnssutils_definition
+    - ldconfig # update links (shared libraries)
+  script:
+    - *build_and_test_definition
+
+############ UBUNTU 18.04 TESTS ############
+build_and_test:bionic:
+  image: labrobotica/wolf_deps:18.04
+  stage: build_and_test
+  cache:
+    - key: wolf-bionic
+      paths:
+      - ci_deps/wolf/
+    - key: gnssutils-bionic
+      paths:
+      - ci_deps/gnss_utils/
+  except:
+    - master
+  before_script:
+    - *preliminaries_definition
+    - *install_wolf_definition
+    - *install_gnssutils_definition
+    - ldconfig # update links (shared libraries)
+  script:
+    - *build_and_test_definition
+
+############ UBUNTU 20.04 TESTS ############
+build_and_test:focal:
+  image: labrobotica/wolf_deps:20.04
+  stage: build_and_test
+  cache:
+    - key: wolf-focal
+      paths:
+      - ci_deps/wolf/
+    - key: gnssutils-focal
+      paths:
+      - ci_deps/gnss_utils/
   except:
     - master
+  before_script:
+    - *preliminaries_definition
+    - *install_wolf_definition
+    - *install_gnssutils_definition
+    - ldconfig # update links (shared libraries)
   script:
-    - mkdir -pv build
-    - cd build
-    - ls # we can check whether the directory was already full
-    - cmake -DCMAKE_BUILD_TYPE=release -DBUILD_EXAMPLES=ON -DBUILD_TESTS=ON ..
-    - make -j$(nproc)
-    - ctest -j$(nproc)
-    - ctest -V -R single diff
-    - make install
+    - *build_and_test_definition
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
new file mode 100644
index 000000000..c10fac6d6
--- /dev/null
+++ b/.vscode/c_cpp_properties.json
@@ -0,0 +1,20 @@
+{
+    "configurations": [
+        {
+            "name": "Linux",
+            "includePath": [
+                "${workspaceFolder}/**",
+                "/home/aeroarms/iri-lab/external_libs/wolf/include",
+                "/usr/local/include/iri-algorithms/wolf/plugin_core",
+                "/usr/local/include/eigen3",
+                "/usr/local/include/iri-algorithms"
+            ],
+            "defines": [],
+            "compilerPath": "/usr/bin/gcc",
+            "cStandard": "gnu11",
+            "cppStandard": "c++17",
+            "intelliSenseMode": "gcc-x64"
+        }
+    ],
+    "version": 4
+}
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9780c827d..89e8ce877 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,18 +1,19 @@
 # Pre-requisites about cmake itself
-CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
 
 if(COMMAND cmake_policy)
-  cmake_policy(SET CMP0005 NEW) 
+  cmake_policy(SET CMP0005 NEW)
   cmake_policy(SET CMP0003 NEW)
 endif(COMMAND cmake_policy)
 # MAC OSX RPATH
 SET(CMAKE_MACOSX_RPATH 1)
 
-
 # The project name
 PROJECT(gnss)
 set(PLUGIN_NAME wolf${PROJECT_NAME})
 
+MESSAGE("Starting ${PROJECT_NAME} CMakeLists ...")
+
 SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
 SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
 SET(CMAKE_INSTALL_PREFIX /usr/local)
@@ -26,18 +27,14 @@ message(STATUS "Configured to compile in ${CMAKE_BUILD_TYPE} mode.")
 SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -D_REENTRANT")
 SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -D_REENTRANT")
 
-#Set compiler according C++11 support
+#Set compiler according C++14 support
 include(CheckCXXCompilerFlag)
-CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
-CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
-if(COMPILER_SUPPORTS_CXX11)
-		message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has C++11 support.")
-        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
-elseif(COMPILER_SUPPORTS_CXX0X)
-		message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has C++0x support.")
-        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)
+if(COMPILER_SUPPORTS_CXX14)
+		message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has C++14 support.")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
 else()
-        message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
+  message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++14 support. Please use a different C++ compiler.")
 endif()
 
 if(UNIX)
@@ -46,7 +43,6 @@ if(UNIX)
     "${CMAKE_CXX_FLAGS} -Werror=all -Werror=extra -Wno-unknown-pragmas -Wno-sign-compare -Wno-unused-parameter -Wno-missing-field-initializers")
 endif(UNIX)
 
-
 IF(NOT BUILD_TESTS)
   OPTION(BUILD_TESTS "Build Unit tests" ON)
 ENDIF(NOT BUILD_TESTS)
@@ -71,48 +67,24 @@ if(BUILD_TESTS)
     enable_testing()
 endif()
 
-MESSAGE("Starting ${PROJECT_NAME} CMakeLists ...")
-CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
-
 #CMAKE modules
-
 SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake_modules")
 MESSAGE(STATUS ${CMAKE_MODULE_PATH})
 
 # Some wolf compilation options
-
 IF((CMAKE_BUILD_TYPE MATCHES DEBUG) OR (CMAKE_BUILD_TYPE MATCHES debug) OR (CMAKE_BUILD_TYPE MATCHES Debug))
   set(_WOLF_DEBUG true)
 ENDIF()
 
 option(_WOLF_TRACE "Enable wolf tracing macro" ON)
 
-# Does this has any other interest
-# but for the examples ?
-# yes, for the tests !
-IF(BUILD_EXAMPLES OR BUILD_TESTS)
-  string(TOUPPER ${PROJECT_NAME} UPPER_NAME)
-  set(_WOLF_ROOT_DIR ${CMAKE_SOURCE_DIR})
-ENDIF(BUILD_EXAMPLES OR BUILD_TESTS)
-
-
-#find dependencies.
-# ============EXAMPLE==================
-FIND_PACKAGE(wolf REQUIRED)
-
-#FIND_PATH(
-#    Suitesparse_INCLUDE_DIRS
-#    NAMES SuiteSparse_config.h
-#    PATHS /usr/include/suitesparse /usr/local/include/suitesparse)
-#MESSAGE("Found suitesparse_INCLUDE_DIRS:" ${Suitesparse_INCLUDE_DIRS})
-
-#IF(Suitesparse_INCLUDE_DIRS)
-#   SET(Suitesparse_FOUND TRUE)
-#   MESSAGE("Suitesparse FOUND: wolf_solver will be built.")
-#ELSE (Suitesparse_INCLUDE_DIRS)
-#   SET(Suitesparse_FOUND FALSE)
-#   MESSAGE(FATAL_ERROR "Suitesparse NOT FOUND")
-#ENDIF (Suitesparse_INCLUDE_DIRS)
+# ============ DEPENDENCIES ============ 
+FIND_PACKAGE(wolfcore REQUIRED)
+FIND_PACKAGE(gnss_utils REQUIRED)
+
+# ============ CONFIG.H ============ 
+string(TOUPPER ${PROJECT_NAME} UPPER_NAME)
+set(_WOLF_ROOT_DIR ${CMAKE_SOURCE_DIR})
 
 # Define the directory where will be the configured config.h
 SET(WOLF_CONFIG_DIR ${PROJECT_BINARY_DIR}/conf/${PROJECT_NAME}/internal)
@@ -131,125 +103,85 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/internal/config.h.in "${WOLF_CONFIG_D
 message("CONFIG DIRECTORY ${PROJECT_BINARY_DIR}")
 include_directories("${PROJECT_BINARY_DIR}/conf")
 
-#INCLUDES SECTION
-# ==============================
-INCLUDE_DIRECTORIES(${wolf_INCLUDE_DIRS})
-INCLUDE_DIRECTORIES(${laser_utils_INCLUDE_DIRS})
+# ============ INCLUDES ============ 
+INCLUDE_DIRECTORIES(${wolfcore_INCLUDE_DIRS})
+INCLUDE_DIRECTORIES(${gnss_utils_INCLUDE_DIRS})
 INCLUDE_DIRECTORIES(BEFORE "include")
 
-#HEADERS
-SET(HDRS_COMMON
-  )
-SET(HDRS_MATH
-  )
-SET(HDRS_UTILS
-  )
-SET(HDRS_STATE_BLOCK
-  )
+# ============ HEADERS ============ 
 SET(HDRS_CAPTURE
+  include/gnss/capture/capture_gnss.h
   include/gnss/capture/capture_gnss_fix.h
-  include/gnss/capture/capture_gnss_single_diff.h
+  include/gnss/capture/capture_gnss_tdcp.h
   )
 SET(HDRS_FACTOR
-  include/gnss/factor/factor_gnss_fix_2D.h
-  include/gnss/factor/factor_gnss_fix_3D.h
-  include/gnss/factor/factor_gnss_single_diff_2D.h
+  include/gnss/factor/factor_gnss_fix_2d.h
+  include/gnss/factor/factor_gnss_fix_3d.h
+  include/gnss/factor/factor_gnss_pseudo_range.h
+  include/gnss/factor/factor_gnss_single_diff_2d.h
+  include/gnss/factor/factor_gnss_tdcp.h
+  include/gnss/factor/factor_gnss_tdcp_2d.h
+  include/gnss/factor/factor_gnss_tdcp_3d.h
   )
 SET(HDRS_FEATURE
   include/gnss/feature/feature_gnss_fix.h
-  include/gnss/feature/feature_gnss_single_diff.h
-  )
-SET(HDRS_LANDMARK
+  include/gnss/feature/feature_gnss_tdcp.h
+  include/gnss/feature/feature_gnss_satellite.h
   )
 SET(HDRS_PROCESSOR
   include/gnss/processor/processor_gnss_fix.h
-  include/gnss/processor/processor_gnss_single_diff.h
+  include/gnss/processor/processor_gnss_tdcp.h
+  include/gnss/processor/processor_tracker_gnss.h
   )
 SET(HDRS_SENSOR
   include/gnss/sensor/sensor_gnss.h
   )
-SET(HDRS_SOLVER
-  )
-SET(HDRS_DTASSC
+SET(HDRS_TREE_MANAGER
+  include/gnss/tree_manager/tree_manager_sliding_window_tdcp.h
   )
 
-#SOURCES
-SET(SRCS_COMMON
-  )
-SET(SRCS_MATH
-  )
-SET(SRCS_UTILS
-  )
-SET(SRCS_STATE_BLOCK
-  )
+# ============ SOURCES ============ 
 SET(SRCS_CAPTURE
+  src/capture/capture_gnss.cpp
   src/capture/capture_gnss_fix.cpp
   )
-SET(SRCS_FACTOR
-  )
-SET(SRCS_FEATURE
-  )
-SET(SRCS_LANDMARK
-  )
 SET(SRCS_PROCESSOR
   src/processor/processor_gnss_fix.cpp
-  src/processor/processor_gnss_single_diff.cpp
+  src/processor/processor_gnss_tdcp.cpp
+  src/processor/processor_tracker_gnss.cpp
   )
 SET(SRCS_SENSOR
   src/sensor/sensor_gnss.cpp
   )
-SET(SRCS_DTASSC
-  )
-SET(SRCS_SOLVER
-  )
-SET(SRCS_YAML
+SET(SRCS_TREE_MANAGER
+  src/tree_manager/tree_manager_sliding_window_tdcp.cpp
   )
-#OPTIONALS
-#optional HDRS and SRCS
-# ==================EXAMPLE===============
-# IF (Ceres_FOUND)
-#     SET(HDRS_WRAPPER
-#       include/base/solver_suitesparse/sparse_utils.h
-#       include/base/solver/solver_manager.h
-#       include/base/ceres_wrapper/ceres_manager.h
-#       include/base/ceres_wrapper/cost_function_wrapper.h
-#       include/base/ceres_wrapper/create_numeric_diff_cost_function.h
-#       include/base/ceres_wrapper/local_parametrization_wrapper.h 
-#       )
-#     SET(SRCS_WRAPPER
-#       src/solver/solver_manager.cpp
-#       src/ceres_wrapper/ceres_manager.cpp
-#       src/ceres_wrapper/local_parametrization_wrapper.cpp 
-#       )
-# ELSE(Ceres_FOUND)
-#   SET(HDRS_WRAPPER)
-#   SET(SRCS_WRAPPER)
-# ENDIF(Ceres_FOUND)
 
 # create the shared library
 ADD_LIBRARY(${PLUGIN_NAME}
   SHARED
   ${SRCS_CAPTURE}
-  ${SRCS_COMMON}
-  ${SRCS_DTASSC}
-  ${SRCS_FACTOR}
-  ${SRCS_FEATURE}
-  ${SRCS_LANDMARK}
-  ${SRCS_MATH}
   ${SRCS_PROCESSOR}
   ${SRCS_SENSOR}
-  ${SRCS_SOLVER}
-  ${SRCS_STATE_BLOCK}
-  ${SRCS_UTILS}
-  ${SRCS_WRAPPER}
-  ${SRCS_YAML}
+  ${SRCS_TREE_MANAGER}
   )
-  
+
+# Set compiler options
+# ====================
+if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+  message(STATUS "Using C++ compiler clang")
+  target_compile_options(${PLUGIN_NAME} PRIVATE -Winconsistent-missing-override)
+  # using Clang
+elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+  message(STATUS "Using C++ compiler gnu")
+  target_compile_options(${PLUGIN_NAME} PRIVATE -Wsuggest-override)
+  # using GCC
+endif()
+
 #Link the created libraries
 #===============EXAMPLE=========================
-TARGET_LINK_LIBRARIES(${PLUGIN_NAME} ${wolf_LIBRARIES})
-TARGET_LINK_LIBRARIES(${PLUGIN_NAME} ${laser_scan_utils_LIBRARY})
-
+TARGET_LINK_LIBRARIES(${PLUGIN_NAME} ${wolfcore_LIBRARIES})
+TARGET_LINK_LIBRARIES(${PLUGIN_NAME} ${gnss_utils_LIBRARIES})
 
 #Build tests
 #===============EXAMPLE=========================
@@ -262,37 +194,23 @@ ENDIF(BUILD_TESTS)
 #=============================================================
 INSTALL(TARGETS ${PLUGIN_NAME} EXPORT ${PLUGIN_NAME}Targets
       RUNTIME DESTINATION bin
-      LIBRARY DESTINATION lib/iri-algorithms
-      ARCHIVE DESTINATION lib/iri-algorithms)
+      LIBRARY DESTINATION lib
+      ARCHIVE DESTINATION lib)
 
 install(EXPORT ${PLUGIN_NAME}Targets DESTINATION lib/cmake/${PLUGIN_NAME})
 #install headers
-INSTALL(FILES ${HDRS_STATE_BLOCK}
-  DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/state_block)
-INSTALL(FILES ${HDRS_DTASSC}
-  DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/association)
 INSTALL(FILES ${HDRS_CAPTURE}
   DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/capture)
 INSTALL(FILES ${HDRS_FACTOR}
   DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/factor)
 INSTALL(FILES ${HDRS_FEATURE}
   DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/feature)
-INSTALL(FILES ${HDRS_SENSOR}
-  DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/sensor)
 INSTALL(FILES ${HDRS_PROCESSOR}
   DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/processor)
-INSTALL(FILES ${HDRS_LANDMARK}
-  DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/landmark)
-INSTALL(FILES ${HDRS_WRAPPER}
-  DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/ceres_wrapper)
-#INSTALL(FILES ${HDRS_SOLVER_SUITESPARSE}
-#  DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/solver_suitesparse)
-INSTALL(FILES ${HDRS_SOLVER}
-  DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/solver)
-INSTALL(FILES ${HDRS_SERIALIZATION}
-  DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/serialization)
-INSTALL(FILES ${HDRS_YAML}
-  DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/yaml)
+INSTALL(FILES ${HDRS_SENSOR}
+  DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/sensor)
+INSTALL(FILES ${HDRS_TREE_MANAGER}
+  DESTINATION include/iri-algorithms/wolf/plugin_${PROJECT_NAME}/${PROJECT_NAME}/tree_manager)
 
 FILE(WRITE ${PROJECT_NAME}.found "")
 INSTALL(FILES ${PROJECT_NAME}.found
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..281d399f1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,619 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
diff --git a/README.md b/README.md
index 31948d180..9b8566e24 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
 WOLF - Windowed Localization Frames | GNSS Plugin
 ===================================
 
-For installation guide and code documentation, please visit the [documentation website](http://mobile_robotics.pages.iri.upc-csic.es/wolf_projects/wolf_lib/wolf_doc/).
\ No newline at end of file
+For installation guide and code documentation, please visit the [documentation website](http://mobile_robotics.pages.iri.upc-csic.es/wolf_projects/wolf_lib/wolf-doc-sphinx/).
diff --git a/ROS-based code style eclipse indented 4sp.xml b/ROS-based code style eclipse indented 4sp.xml
deleted file mode 100644
index 73a372adf..000000000
--- a/ROS-based code style eclipse indented 4sp.xml	
+++ /dev/null
@@ -1,167 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<profiles version="1">
-<profile kind="CodeFormatterProfile" name="ROS Formatting indented private" version="1">
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.lineSplit" value="120"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_member_access" value="0"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_base_types" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
-<setting id="org.eclipse.cdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_constructor_initializer_list" value="2"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_exception_specification" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_base_types" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.indent_body_declarations_compare_to_access_specifier" value="true"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_exception_specification" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_template_arguments" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.comment.min_distance_between_code_and_line_comment" value="1"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_expressions_in_array_initializer" value="18"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_declarator_list" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_bracket" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.tabulation.size" value="4"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_else_in_if_statement" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_enumerator_list" value="16"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_declarator_list" value="16"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.indent_empty_lines" value="false"/>
-<setting id="org.eclipse.cdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
-<setting id="org.eclipse.cdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.brace_position_for_method_declaration" value="next_line"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_closing_angle_bracket_in_template_arguments" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_base_clause" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.join_wrapped_lines" value="true"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_declarator_list" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_arguments_in_method_invocation" value="18"/>
-<setting id="org.eclipse.cdt.core.formatter.comment.never_indent_line_comments_on_first_column" value="true"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_brackets" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_bracket" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_parameters_in_method_declaration" value="18"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="true"/>
-<setting id="org.eclipse.cdt.core.formatter.brace_position_for_block" value="next_line"/>
-<setting id="org.eclipse.cdt.core.formatter.brace_position_for_type_declaration" value="next_line"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_angle_bracket_in_template_arguments" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_expression_list" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_angle_bracket_in_template_parameters" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.continuation_indentation" value="2"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_expression_list" value="0"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_template_parameters" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_conditional_expression" value="16"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
-<setting id="org.eclipse.cdt.core.formatter.indent_access_specifier_extra_spaces" value="0"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.indent_access_specifier_compare_to_type_header" value="true"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.indent_body_declarations_compare_to_namespace_header" value="false"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_compact_if" value="16"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_assignment" value="16"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_conditional_expression_chain" value="18"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_template_parameters" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_expression_list" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_exception_specification" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_base_clause_in_type_declaration" value="18"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_identifier_in_function_declaration" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_exception_specification" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.indent_declaration_compare_to_template_header" value="false"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.indent_statements_compare_to_body" value="true"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_binary_expression" value="16"/>
-<setting id="org.eclipse.cdt.core.formatter.indent_statements_compare_to_block" value="true"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_template_arguments" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="18"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_closing_paren_in_cast" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_angle_bracket_in_template_parameters" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.tabulation.char" value="space"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_angle_bracket_in_template_parameters" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_colon_in_constructor_initializer_list" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.brace_position_for_block_in_case" value="next_line"/>
-<setting id="org.eclipse.cdt.core.formatter.compact_else_if" value="true"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_new_line_after_template_declaration" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_colon_in_base_clause" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
-<setting id="org.eclipse.cdt.core.formatter.brace_position_for_switch" value="next_line"/>
-<setting id="org.eclipse.cdt.core.formatter.alignment_for_overloaded_left_shift_chain" value="16"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.indentation.size" value="4"/>
-<setting id="org.eclipse.cdt.core.formatter.brace_position_for_namespace_declaration" value="next_line"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_angle_bracket_in_template_arguments" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_namespace_declaration" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_bracket" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_closing_angle_bracket_in_template_parameters" value="insert"/>
-<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_angle_bracket_in_template_arguments" value="do not insert"/>
-</profile>
-</profiles>
diff --git a/ROS-based wolf code style QT indented 4sp.xml b/ROS-based wolf code style QT indented 4sp.xml
deleted file mode 100644
index 7ef65eaad..000000000
--- a/ROS-based wolf code style QT indented 4sp.xml	
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE QtCreatorCodeStyle>
-<!-- Written by QtCreator 3.0.1, 2016-02-22T12:02:02. -->
-<qtcreator>
- <data>
-  <variable>CodeStyleData</variable>
-  <valuemap type="QVariantMap">
-   <value type="bool" key="AlignAssignments">false</value>
-   <value type="bool" key="AutoSpacesForTabs">false</value>
-   <value type="bool" key="BindStarToIdentifier">false</value>
-   <value type="bool" key="BindStarToLeftSpecifier">false</value>
-   <value type="bool" key="BindStarToRightSpecifier">false</value>
-   <value type="bool" key="BindStarToTypeName">true</value>
-   <value type="bool" key="ExtraPaddingForConditionsIfConfusingAlign">true</value>
-   <value type="bool" key="IndentAccessSpecifiers">true</value>
-   <value type="bool" key="IndentBlockBody">true</value>
-   <value type="bool" key="IndentBlockBraces">false</value>
-   <value type="bool" key="IndentBlocksRelativeToSwitchLabels">true</value>
-   <value type="bool" key="IndentClassBraces">false</value>
-   <value type="bool" key="IndentControlFlowRelativeToSwitchLabels">true</value>
-   <value type="bool" key="IndentDeclarationsRelativeToAccessSpecifiers">true</value>
-   <value type="bool" key="IndentEnumBraces">false</value>
-   <value type="bool" key="IndentFunctionBody">true</value>
-   <value type="bool" key="IndentFunctionBraces">false</value>
-   <value type="bool" key="IndentNamespaceBody">false</value>
-   <value type="bool" key="IndentNamespaceBraces">false</value>
-   <value type="int" key="IndentSize">4</value>
-   <value type="bool" key="IndentStatementsRelativeToSwitchLabels">true</value>
-   <value type="bool" key="IndentSwitchLabels">true</value>
-   <value type="int" key="PaddingMode">2</value>
-   <value type="bool" key="SpacesForTabs">true</value>
-   <value type="int" key="TabSize">4</value>
-  </valuemap>
- </data>
- <data>
-  <variable>DisplayName</variable>
-  <value type="QString">ROS wolf</value>
- </data>
-</qtcreator>
diff --git a/cmake_modules/wolfgnssConfig.cmake b/cmake_modules/wolfgnssConfig.cmake
index 486e5af0e..43a786472 100644
--- a/cmake_modules/wolfgnssConfig.cmake
+++ b/cmake_modules/wolfgnssConfig.cmake
@@ -12,7 +12,7 @@ ENDIF(wolfgnss_INCLUDE_DIRS)
 FIND_LIBRARY(
     wolfgnss_LIBRARIES
     NAMES libwolfgnss.so
-    PATHS /usr/local/lib/iri-algorithms)
+    PATHS /usr/local/lib)
 IF(wolfgnss_LIBRARIES)
   MESSAGE("Found gnss lib: ${wolfgnss_LIBRARIES}")
 ELSE(wolfgnss_LIBRARIES)
@@ -66,18 +66,23 @@ set(wolfgnss_FOUND TRUE)
 
 # Now we gather all the required dependencies for Wolf Laser
 
-FIND_PACKAGE(laser_scan_utils REQUIRED)
-list(APPEND wolfgnss_INCLUDE_DIRS ${laser_scan_utils_INCLUDE_DIRS})
-list(APPEND wolfgnss_LIBRARIES ${laser_scan_utils_LIBRARY})
+FIND_PACKAGE(gnss_utils REQUIRED)
+list(APPEND wolfgnss_INCLUDE_DIRS ${gnss_utils_INCLUDE_DIRS})
+list(APPEND wolfgnss_LIBRARIES ${gnss_utils_LIBRARY})
 if(NOT wolf_FOUND)
-  FIND_PACKAGE(wolf REQUIRED)
+  FIND_PACKAGE(wolfcore REQUIRED)
 
   #We reverse in order to insert at the start
   list(REVERSE wolfgnss_INCLUDE_DIRS)
-  list(APPEND wolfgnss_INCLUDE_DIRS ${wolf_INCLUDE_DIRS})
+  list(APPEND wolfgnss_INCLUDE_DIRS ${wolfcore_INCLUDE_DIRS})
   list(REVERSE wolfgnss_INCLUDE_DIRS)
 
   list(REVERSE wolfgnss_LIBRARIES)
-  list(APPEND wolfgnss_LIBRARIES ${wolf_LIBRARIES})
+  list(APPEND wolfgnss_LIBRARIES ${wolfcore_LIBRARIES})
   list(REVERSE wolfgnss_LIBRARIES)
-endif()
\ No newline at end of file
+endif()
+
+# provide both INCLUDE_DIR and INCLUDE_DIRS
+SET(wolfgnss_INCLUDE_DIR ${wolfgnss_INCLUDE_DIRS})
+# provide both LIBRARY and LIBRARIES 
+SET(wolfgnss_LIBRARY ${wolfgnss_LIBRARIES})
\ No newline at end of file
diff --git a/codetemplates eclipse.xml b/codetemplates eclipse.xml
deleted file mode 100644
index 85f43146f..000000000
--- a/codetemplates eclipse.xml	
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?><templates><template autoinsert="true" context="org.eclipse.cdt.ui.text.codetemplates.constructorcomment_context" deleted="false" description="Comment for created constructors" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.constructorcomment" name="constructorcomment">/*
- *
- */</template><template autoinsert="true" context="org.eclipse.cdt.ui.text.codetemplates.destructorcomment_context" deleted="false" description="Comment for created destructors" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.destructorcomment" name="destructorcomment">/*
- *
- */</template><template autoinsert="false" context="org.eclipse.cdt.ui.text.codetemplates.filecomment_context" deleted="false" description="Comment for created C/C++ files" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.filecomment" name="filecomment">/**
- * \file ${file_name}
- *
- *  Created on: ${date}
- *      \author: ${user}
- */
-</template><template autoinsert="true" context="org.eclipse.cdt.ui.text.codetemplates.typecomment_context" deleted="false" description="Comment for created classes" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.typecomment" name="typecomment">/*
- *
- */</template><template autoinsert="true" context="org.eclipse.cdt.ui.text.codetemplates.fieldcomment_context" deleted="false" description="Comment for fields" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.fieldcomment" name="fieldcomment">/*
- *
- */</template><template autoinsert="true" context="org.eclipse.cdt.ui.text.codetemplates.methodcomment_context" deleted="false" description="Comment for methods" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.methodcomment" name="methodcomment">/*
- *
- */</template><template autoinsert="false" context="org.eclipse.cdt.core.cxxSource.contenttype_context" deleted="false" description="Default template for newly created C++ source files" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.cppsourcefile" name="Default C++ source template">${filecomment}
-${includes}
-
-${namespace_begin}
-
-${declarations}
-
-${namespace_end}</template><template autoinsert="false" context="org.eclipse.cdt.core.cxxSource.contenttype_context" deleted="false" description="Default template for newly created C++ test files" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.cpptestfile" name="Default C++ test template">${filecomment}
-
-#include <core/utils/utils_gtest.h>
-
-${includes}
-
-${namespace_begin}
-
-${declarations}
-
-TEST(TestGroup, DummyTestExample)
-{
-    // ${todo}: Automatically generated TEST stub 
-}
-
-int main(int argc, char **argv)
-{
-  testing::InitGoogleTest(&amp;argc, argv);
-  return RUN_ALL_TESTS();
-}
-
-${namespace_end}</template><template autoinsert="true" context="org.eclipse.cdt.core.cxxHeader.contenttype_context" deleted="false" description="Default template for newly created C++ header files" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.cppheaderfile" name="Default C++ header template">${filecomment}
-#ifndef ${include_guard_symbol}
-#define ${include_guard_symbol}
-
-${includes}
-
-${namespace_begin}
-
-${declarations}
-
-${namespace_end}
-
-#endif /* ${include_guard_symbol} */</template><template autoinsert="true" context="org.eclipse.cdt.core.cSource.contenttype_context" deleted="false" description="Default template for newly created C source files" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.csourcefile" name="Default C source template">${filecomment}
-${includes}
-
-${declarations}</template><template autoinsert="true" context="org.eclipse.cdt.core.cHeader.contenttype_context" deleted="false" description="Default template for newly created C header files" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.cheaderfile" name="Default C header template">${filecomment}
-#ifndef ${include_guard_symbol}
-#define ${include_guard_symbol}
-
-${includes}
-
-${declarations}
-
-#endif /* ${include_guard_symbol} */</template><template autoinsert="true" context="org.eclipse.cdt.ui.text.codetemplates.namespace_context" deleted="false" description="Beginning of namespace declaration" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.namespace_begin" name="namespace_begin">namespace ${namespace_name} {</template><template autoinsert="true" context="org.eclipse.cdt.ui.text.codetemplates.namespace_context" deleted="false" description="End of namespace declaration" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.namespace_end" name="namespace_end">} /* namespace ${namespace_name} */</template><template autoinsert="true" context="org.eclipse.cdt.ui.text.codetemplates.class_context" deleted="false" description="Code in created class definitions" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.class_body" name="class_body">${declarations}</template><template autoinsert="true" context="org.eclipse.cdt.ui.text.codetemplates.methodbody_context" deleted="false" description="Code in created method stubs" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.methodbody" name="methodbody">	// ${todo} Auto-generated method stub
-	${body_statement}</template><template autoinsert="true" context="org.eclipse.cdt.ui.text.codetemplates.constructorbody_context" deleted="false" description="Code in created constructor stubs" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.constructorbody" name="constructorbody">	// ${todo} Auto-generated constructor stub
-	${body_statement}</template><template autoinsert="true" context="org.eclipse.cdt.ui.text.codetemplates.destructorbody_context" deleted="false" description="Code in created destructor stubs" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.destructorbody" name="destructorbody">	${body_statement}
-	// ${todo} Auto-generated destructor stub</template><template autoinsert="true" context="org.eclipse.cdt.core.asmSource.contenttype_context" deleted="false" description="Default template for newly created assembly files" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.asmsourcefile" name="Default assembly template">${filecomment}
-</template><template autoinsert="true" context="org.eclipse.core.runtime.text.contenttype_context" deleted="false" description="Default template for newly created text files" enabled="true" id="org.eclipse.cdt.ui.text.codetemplates.textfile" name="Default text file template">${file_name}
- Created on: ${date}
-     Author: ${user}
-
-</template></templates>
\ No newline at end of file
diff --git a/include/gnss/capture/capture_gnss.h b/include/gnss/capture/capture_gnss.h
new file mode 100644
index 000000000..1a8980534
--- /dev/null
+++ b/include/gnss/capture/capture_gnss.h
@@ -0,0 +1,80 @@
+//--------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--------
+#ifndef CAPTURE_GNSS_H_
+#define CAPTURE_GNSS_H_
+
+//Wolf includes
+#include "gnss/feature/feature_gnss_fix.h"
+#include "core/capture/capture_base.h"
+
+//std includes
+#include "gnss_utils/snapshot.h"
+
+namespace wolf {
+
+WOLF_PTR_TYPEDEFS(CaptureGnss);
+
+//class CaptureGnss
+class CaptureGnss : public CaptureBase
+{
+    protected:
+        GnssUtils::SnapshotPtr snapshot_; ///< observation and navigation data
+
+    public:
+      CaptureGnss(const TimeStamp& _ts, SensorBasePtr _sensor_ptr, GnssUtils::SnapshotPtr _snapshot);
+      ~CaptureGnss() override;
+
+      GnssUtils::SnapshotPtr getSnapshot() const;
+      GnssUtils::ObservationsPtr getObservations() const;
+      GnssUtils::NavigationPtr getNavigation() const;
+      GnssUtils::Satellites& getSatellites();
+      const GnssUtils::Satellites& getSatellites() const;
+
+};
+
+inline GnssUtils::SnapshotPtr CaptureGnss::getSnapshot() const
+{
+  return snapshot_;
+}
+
+inline GnssUtils::ObservationsPtr CaptureGnss::getObservations() const
+{
+  return snapshot_->getObservations();
+}
+
+inline GnssUtils::NavigationPtr CaptureGnss::getNavigation() const
+{
+  return snapshot_->getNavigation();
+}
+
+inline GnssUtils::Satellites& CaptureGnss::getSatellites()
+{
+  return snapshot_->getSatellites();
+}
+
+inline const GnssUtils::Satellites& CaptureGnss::getSatellites() const
+{
+  return snapshot_->getSatellites();
+}
+
+} //namespace wolf
+#endif
diff --git a/include/gnss/capture/capture_gnss_fix.h b/include/gnss/capture/capture_gnss_fix.h
index 08af47ba2..c894d3e7f 100644
--- a/include/gnss/capture/capture_gnss_fix.h
+++ b/include/gnss/capture/capture_gnss_fix.h
@@ -1,3 +1,24 @@
+//--------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--------
 #ifndef CAPTURE_GNSS_FIX_H_
 #define CAPTURE_GNSS_FIX_H_
 
@@ -16,31 +37,45 @@ WOLF_PTR_TYPEDEFS(CaptureGnssFix);
 class CaptureGnssFix : public CaptureBase
 {
     protected:
-        Eigen::VectorXs data_; ///< Raw data.
-        Eigen::MatrixXs data_covariance_; ///< Noise of the capture.
+        Eigen::Vector3d position_ECEF_; ///< position in ECEF coordinates.
+        Eigen::Matrix3d covariance_ECEF_; ///< Noise of the fix in ECEF coordinates.
+        TimeStamp ts_GPST_; ///< Time stamp in GPS time
 
     public:
-        CaptureGnssFix(const TimeStamp& _ts, SensorBasePtr _sensor_ptr, const Eigen::VectorXs& _data, const Eigen::MatrixXs& _data_covariance);
-        virtual ~CaptureGnssFix();
-        const Eigen::VectorXs& getData() const;
-        const Eigen::MatrixXs& getDataCovariance() const;
-        void getDataAndCovariance(Eigen::VectorXs& data, Eigen::MatrixXs& data_cov) const;
+        CaptureGnssFix(const TimeStamp& _ts, SensorBasePtr _sensor_ptr, const Eigen::Vector3d& _position, const Eigen::Matrix3d& _covariance, bool _ecef_coordinates = true);
+        ~CaptureGnssFix() override;
+
+        const Eigen::Vector3d& getPositionEcef() const;
+        const Eigen::Matrix3d& getCovarianceEcef() const;
+        void getPositionAndCovarianceEcef(Eigen::Vector3d& position, Eigen::Matrix3d& cov) const;
+        const TimeStamp& getGpsTimeStamp() const;
+        void setGpsTimeStamp(const TimeStamp& _ts_GPST);
 };
 
-inline const Eigen::VectorXs& CaptureGnssFix::getData() const
+inline const Eigen::Vector3d& CaptureGnssFix::getPositionEcef() const
+{
+    return position_ECEF_;
+}
+
+inline const Eigen::Matrix3d& CaptureGnssFix::getCovarianceEcef() const
+{
+    return covariance_ECEF_;
+}
+
+inline void CaptureGnssFix::getPositionAndCovarianceEcef(Eigen::Vector3d& position, Eigen::Matrix3d& cov) const
 {
-    return data_;
+    position = position_ECEF_;
+    cov = covariance_ECEF_;
 }
 
-inline const Eigen::MatrixXs& CaptureGnssFix::getDataCovariance() const
+inline const wolf::TimeStamp& CaptureGnssFix::getGpsTimeStamp() const
 {
-    return data_covariance_;
+    return ts_GPST_;
 }
 
-inline void CaptureGnssFix::getDataAndCovariance(Eigen::VectorXs& data, Eigen::MatrixXs& data_cov) const
+inline void CaptureGnssFix::setGpsTimeStamp(const TimeStamp& _ts_GPST)
 {
-    data = data_;
-    data_cov = data_covariance_;
+    ts_GPST_ = _ts_GPST;
 }
 
 } //namespace wolf
diff --git a/include/gnss/capture/capture_gnss_single_diff.h b/include/gnss/capture/capture_gnss_single_diff.h
deleted file mode 100644
index 3423496bd..000000000
--- a/include/gnss/capture/capture_gnss_single_diff.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef CAPTURE_GNSS_SINGLE_DIFF_H_
-#define CAPTURE_GNSS_SINGLE_DIFF_H_
-
-//Wolf includes
-#include "gnss/feature/feature_gnss_single_diff.h"
-#include "core/capture/capture_base.h"
-
-//std includes
-//
-
-namespace wolf {
-    
-WOLF_PTR_TYPEDEFS(CaptureGnssSingleDiff);
-
-//class CaptureGnssSingleDiff
-class CaptureGnssSingleDiff : public CaptureBase
-{
-    protected:
-        Eigen::Vector3s data_; ///< Raw data.
-        Eigen::Matrix3s data_covariance_; ///< Noise of the capture.
-        FrameBasePtr    origin_frame_ptr_;
-
-    public:
-        CaptureGnssSingleDiff(const TimeStamp& _ts,
-                              SensorBasePtr _sensor_ptr,
-                              const Eigen::Vector3s& _data,
-                              const Eigen::Matrix3s& _data_covariance,
-                              const FrameBasePtr& _origin_frame_ptr) :
-            CaptureBase("GNSS SINGLE DIFFERENCE", _ts, _sensor_ptr),
-            data_(_data),
-            data_covariance_(_data_covariance),
-            origin_frame_ptr_(_origin_frame_ptr)
-        {};
-        virtual ~CaptureGnssSingleDiff(){};
-        const Eigen::Vector3s& getData() const;
-        const Eigen::Matrix3s& getDataCovariance() const;
-        void getDataAndCovariance(Eigen::Vector3s& data, Eigen::Matrix3s& data_cov) const;
-        FrameBasePtr getOriginFrame() const;
-};
-
-inline const Eigen::Vector3s& CaptureGnssSingleDiff::getData() const
-{
-    return data_;
-}
-
-inline const Eigen::Matrix3s& CaptureGnssSingleDiff::getDataCovariance() const
-{
-    return data_covariance_;
-}
-
-inline void CaptureGnssSingleDiff::getDataAndCovariance(Eigen::Vector3s& data, Eigen::Matrix3s& data_cov) const
-{
-    data = data_;
-    data_cov = data_covariance_;
-}
-
-inline FrameBasePtr CaptureGnssSingleDiff::getOriginFrame() const
-{
-    return origin_frame_ptr_;
-}
-
-} //namespace wolf
-#endif
diff --git a/include/gnss/capture/capture_gnss_tdcp.h b/include/gnss/capture/capture_gnss_tdcp.h
new file mode 100644
index 000000000..73af63d7c
--- /dev/null
+++ b/include/gnss/capture/capture_gnss_tdcp.h
@@ -0,0 +1,96 @@
+//--------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--------
+#ifndef CAPTURE_GNSS_TDCP_H_
+#define CAPTURE_GNSS_TDCP_H_
+
+//Wolf includes
+#include "core/capture/capture_base.h"
+#include "../feature/feature_gnss_tdcp.h"
+
+//std includes
+//
+
+namespace wolf {
+    
+WOLF_PTR_TYPEDEFS(CaptureGnssTdcp);
+
+class CaptureGnssTdcp : public CaptureBase
+{
+    protected:
+        Eigen::Vector3d data_; ///< Displacement in ECEF coordinates.
+        Eigen::Matrix3d data_covariance_; ///< Noise of the capture.
+        FrameBasePtr    origin_frame_ptr_;
+        TimeStamp ts_GPST_; ///< Time stamp in GPS time
+
+    public:
+        CaptureGnssTdcp(const TimeStamp& _ts,
+                              SensorBasePtr _sensor_ptr,
+                              const Eigen::Vector3d& _data,
+                              const Eigen::Matrix3d& _data_covariance,
+                              const FrameBasePtr& _origin_frame_ptr) :
+            CaptureBase("CaptureGnssTdcp", _ts, _sensor_ptr),
+            data_(_data),
+            data_covariance_(_data_covariance),
+            origin_frame_ptr_(_origin_frame_ptr)
+        {};
+        ~CaptureGnssTdcp() override{};
+        const Eigen::Vector3d& getData() const;
+        const Eigen::Matrix3d& getDataCovariance() const;
+        void getDataAndCovariance(Eigen::Vector3d& data, Eigen::Matrix3d& data_cov) const;
+        FrameBasePtr getOriginFrame() const;
+        const TimeStamp& getGpsTimeStamp() const;
+        void setGpsTimeStamp(const TimeStamp &_ts_GPST);
+};
+
+inline const Eigen::Vector3d& CaptureGnssTdcp::getData() const
+{
+    return data_;
+}
+
+inline const Eigen::Matrix3d& CaptureGnssTdcp::getDataCovariance() const
+{
+    return data_covariance_;
+}
+
+inline void CaptureGnssTdcp::getDataAndCovariance(Eigen::Vector3d& data, Eigen::Matrix3d& data_cov) const
+{
+    data = data_;
+    data_cov = data_covariance_;
+}
+
+inline FrameBasePtr CaptureGnssTdcp::getOriginFrame() const
+{
+    return origin_frame_ptr_;
+}
+
+inline const wolf::TimeStamp& CaptureGnssTdcp::getGpsTimeStamp() const
+{
+    return ts_GPST_;
+}
+
+inline void CaptureGnssTdcp::setGpsTimeStamp(const TimeStamp &_ts_GPST)
+{
+    ts_GPST_ = _ts_GPST;
+}
+
+} //namespace wolf
+#endif
diff --git a/include/gnss/factor/factor_gnss_fix_2D.h b/include/gnss/factor/factor_gnss_fix_2d.h
similarity index 69%
rename from include/gnss/factor/factor_gnss_fix_2D.h
rename to include/gnss/factor/factor_gnss_fix_2d.h
index 773a84d8b..26a8735be 100644
--- a/include/gnss/factor/factor_gnss_fix_2D.h
+++ b/include/gnss/factor/factor_gnss_fix_2d.h
@@ -1,6 +1,27 @@
-
-#ifndef FACTOR_GNSS_FIX_2D_H_
-#define FACTOR_GNSS_FIX_2D_H_
+//--------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--------
+
+#ifndef FACTOR_GNSS_FIX_2d_H_
+#define FACTOR_GNSS_FIX_2d_H_
 
 //Wolf includes
 #include "core/common/wolf.h"
@@ -10,37 +31,39 @@
 
 namespace wolf {
     
-WOLF_PTR_TYPEDEFS(FactorGnssFix2D);
+WOLF_PTR_TYPEDEFS(FactorGnssFix2d);
 
-class FactorGnssFix2D : public FactorAutodiff<FactorGnssFix2D, 3, 2, 1, 3, 3, 1, 1, 1>
+class FactorGnssFix2d : public FactorAutodiff<FactorGnssFix2d, 3, 2, 1, 3, 3, 1, 1, 1>
 {
     protected:
         SensorGnssPtr sensor_gnss_ptr_;
 
     public:
 
-        FactorGnssFix2D(FeatureBasePtr& _ftr_ptr, const SensorGnssPtr& _sensor_gnss_ptr, const ProcessorBasePtr& _processor_ptr, bool _apply_loss_function = false, FactorStatus _status = FAC_ACTIVE) :
-            FactorAutodiff<FactorGnssFix2D, 3, 2, 1, 3, 3, 1, 1, 1>("FactorGnssFix2D",
-                                                                     nullptr,
-                                                                     nullptr,
-                                                                     nullptr,
-                                                                     nullptr,
-                                                                     _processor_ptr,
-                                                                     _apply_loss_function,
-                                                                     _status,
-                                                                     _ftr_ptr->getFrame()->getP(),
-                                                                     _ftr_ptr->getFrame()->getO(),
-                                                                     _sensor_gnss_ptr->getStateBlock(0),
-                                                                     _sensor_gnss_ptr->getEnuMapTranslation(),
-                                                                     _sensor_gnss_ptr->getEnuMapRoll(),
-                                                                     _sensor_gnss_ptr->getEnuMapPitch(),
-                                                                     _sensor_gnss_ptr->getEnuMapYaw()),
+        FactorGnssFix2d(FeatureBasePtr& _ftr_ptr, const SensorGnssPtr& _sensor_gnss_ptr, const ProcessorBasePtr& _processor_ptr, bool _apply_loss_function, FactorStatus _status = FAC_ACTIVE) :
+            FactorAutodiff<FactorGnssFix2d, 3, 2, 1, 3, 3, 1, 1, 1>("FactorGnssFix2d",
+                                                                    TOP_ABS,
+                                                                    _ftr_ptr,
+                                                                    nullptr,
+                                                                    nullptr,
+                                                                    nullptr,
+                                                                    nullptr,
+                                                                    _processor_ptr,
+                                                                    _apply_loss_function,
+                                                                    _status,
+                                                                    _ftr_ptr->getFrame()->getP(),
+                                                                    _ftr_ptr->getFrame()->getO(),
+                                                                    _sensor_gnss_ptr->getP(),
+                                                                    _sensor_gnss_ptr->getEnuMapTranslation(),
+                                                                    _sensor_gnss_ptr->getEnuMapRoll(),
+                                                                    _sensor_gnss_ptr->getEnuMapPitch(),
+                                                                    _sensor_gnss_ptr->getEnuMapYaw()),
             sensor_gnss_ptr_(_sensor_gnss_ptr)
         {
-            WOLF_WARN_COND(!sensor_gnss_ptr_->isEnuDefined() && _status == FAC_ACTIVE, "Creating an active GNSS Fix 2D factor without initializing ENU");
+            WOLF_WARN_COND(!sensor_gnss_ptr_->isEnuDefined() && _status == FAC_ACTIVE, "Creating an active GNSS Fix 2d factor without initializing ENU");
         }
 
-        virtual ~FactorGnssFix2D() = default;
+        ~FactorGnssFix2d() override = default;
 
         template<typename T>
         bool operator ()(const T* const _x,
@@ -55,7 +78,7 @@ class FactorGnssFix2D : public FactorAutodiff<FactorGnssFix2D, 3, 2, 1, 3, 3, 1,
 };
 
 template<typename T>
-inline bool FactorGnssFix2D::operator ()(const T* const _x,
+inline bool FactorGnssFix2d::operator ()(const T* const _x,
                                          const T* const _o,
                                          const T* const _x_antena,
                                          const T* const _t_ENU_map,
@@ -79,19 +102,19 @@ inline bool FactorGnssFix2D::operator ()(const T* const _x,
     //std::cout << "getREnuEcef():\n" << sensor_gnss_ptr_->gettEnuEcef() << std::endl;
     //std::cout << "gettEnuEcef(): " << sensor_gnss_ptr_->gettEnuEcef().transpose() << std::endl;
 
-    // Transform ECEF 3D Fix Feature to 2D Fix in Map coordinates (removing z)
+    // Transform ECEF 3d Fix Feature to 2d Fix in Map coordinates (removing z)
     //std::cout << "R_map_ENU:\n\t" << R_map_ENU(0,0) << "\t" << R_map_ENU(0,1) << "\t" << R_map_ENU(0,2) << "\n\t" << R_map_ENU(1,0) << "\t" << R_map_ENU(1,1) << "\t" << R_map_ENU(1,2) << std::endl;
     //std::cout << "R_ENU_ECEF:\n\t" << R_ENU_ECEF(0,0) << "\t" << R_ENU_ECEF(0,1) << "\t" << R_ENU_ECEF(0,2) << "\n\t" << R_ENU_ECEF(1,0) << "\t" << R_ENU_ECEF(1,1) << "\t" << R_ENU_ECEF(1,2) << "\n\t" << R_ENU_ECEF(2,0) << "\t" << R_ENU_ECEF(2,1) << "\t" << R_ENU_ECEF(2,2) << std::endl;
     //std::cout << "t_ENU_ECEF:\n\t" << t_ENU_ECEF(0) << "\n\t" << t_ENU_ECEF(1) << "\n\t" << t_ENU_ECEF(2) << std::endl;
     Eigen::Matrix<T,2,1> fix_map = R_map_ENU * (R_ENU_ECEF * fix_ECEF + t_ENU_ECEF - t_ENU_map);
-    //std::cout << "fix 3D:\n\t" << getMeasurement()(0) << "\n\t" << getMeasurement()(1) << "\n\t" << getMeasurement()(2) << std::endl;
+    //std::cout << "fix 3d:\n\t" << getMeasurement()(0) << "\n\t" << getMeasurement()(1) << "\n\t" << getMeasurement()(2) << std::endl;
     //std::cout << "fix_map:\n\t" << fix_map[0] << "\n\t" << fix_map[1] << std::endl;
 
     // Antena position in Map coordinates
     Eigen::Matrix<T,2,1> antena_map = R_map_base * t_base_antena.head(2) + t_map_base;
     //std::cout << "antena_map:\n\t" << antena_map[0] << "\n\t" << antena_map[1] << std::endl;
 
-    // Compute residual rotating information matrix to 2D Map coordinates
+    // Compute residual rotating information matrix to 2d Map coordinates
     // Covariance & information are rotated by R*S*R' so for the squred root upper (or right) R*L*U*R'->U*R'
     // In this case R = R_map_ENU * R_ENU_ECEF, then R' = R_ENU_ECEF' * R_map_ENU'
     residuals_ECEF = (getMeasurementSquareRootInformationUpper().cast<T>() * R_ENU_ECEF.transpose() * R_map_ENU.transpose()) * (antena_map - fix_map);
diff --git a/include/gnss/factor/factor_gnss_fix_3D.h b/include/gnss/factor/factor_gnss_fix_3d.h
similarity index 64%
rename from include/gnss/factor/factor_gnss_fix_3D.h
rename to include/gnss/factor/factor_gnss_fix_3d.h
index c620ee58a..650a1055f 100644
--- a/include/gnss/factor/factor_gnss_fix_3D.h
+++ b/include/gnss/factor/factor_gnss_fix_3d.h
@@ -1,6 +1,27 @@
-
-#ifndef FACTOR_GNSS_FIX_3D_H_
-#define FACTOR_GNSS_FIX_3D_H_
+//--------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--------
+
+#ifndef FACTOR_GNSS_FIX_3d_H_
+#define FACTOR_GNSS_FIX_3d_H_
 
 //Wolf includes
 #include "core/common/wolf.h"
@@ -9,37 +30,39 @@
 
 namespace wolf {
     
-WOLF_PTR_TYPEDEFS(FactorGnssFix3D);
+WOLF_PTR_TYPEDEFS(FactorGnssFix3d);
 
-class FactorGnssFix3D : public FactorAutodiff<FactorGnssFix3D, 3, 3, 4, 3, 3, 1, 1, 1>
+class FactorGnssFix3d : public FactorAutodiff<FactorGnssFix3d, 3, 3, 4, 3, 3, 1, 1, 1>
 {
     protected:
         SensorGnssPtr sensor_gnss_ptr_;
 
     public:
 
-        FactorGnssFix3D(FeatureBasePtr& _ftr_ptr, const SensorGnssPtr& _sensor_gnss_ptr, const ProcessorBasePtr& _processor_ptr, bool _apply_loss_function = false, FactorStatus _status = FAC_ACTIVE) :
-            FactorAutodiff<FactorGnssFix3D, 3, 3, 4, 3, 3, 1, 1, 1>("FactorGnssFix3D",
-                                                                     nullptr,
-                                                                     nullptr,
-                                                                     nullptr,
-                                                                     nullptr,
-                                                                     _processor_ptr,
-                                                                     _apply_loss_function,
-                                                                     _status,
-                                                                     _ftr_ptr->getFrame()->getP(),
-                                                                     _ftr_ptr->getFrame()->getO(),
-                                                                     _sensor_gnss_ptr->getStateBlock(0),
-                                                                     _sensor_gnss_ptr->getEnuMapTranslation(),
-                                                                     _sensor_gnss_ptr->getEnuMapRoll(),
-                                                                     _sensor_gnss_ptr->getEnuMapPitch(),
-                                                                     _sensor_gnss_ptr->getEnuMapYaw()),
+        FactorGnssFix3d(FeatureBasePtr& _ftr_ptr, const SensorGnssPtr& _sensor_gnss_ptr, const ProcessorBasePtr& _processor_ptr, bool _apply_loss_function, FactorStatus _status = FAC_ACTIVE) :
+            FactorAutodiff<FactorGnssFix3d, 3, 3, 4, 3, 3, 1, 1, 1>("FactorGnssFix3d",
+                                                                    TOP_ABS,
+                                                                    _ftr_ptr,
+                                                                    nullptr,
+                                                                    nullptr,
+                                                                    nullptr,
+                                                                    nullptr,
+                                                                    _processor_ptr,
+                                                                    _apply_loss_function,
+                                                                    _status,
+                                                                    _ftr_ptr->getFrame()->getP(),
+                                                                    _ftr_ptr->getFrame()->getO(),
+                                                                    _sensor_gnss_ptr->getP(),
+                                                                    _sensor_gnss_ptr->getEnuMapTranslation(),
+                                                                    _sensor_gnss_ptr->getEnuMapRoll(),
+                                                                    _sensor_gnss_ptr->getEnuMapPitch(),
+                                                                    _sensor_gnss_ptr->getEnuMapYaw()),
             sensor_gnss_ptr_(_sensor_gnss_ptr)
         {
-            WOLF_WARN_COND(!sensor_gnss_ptr_->isEnuDefined() && _status == FAC_ACTIVE, "Creating an ACTIVE GNSS Fix 3D factor without initializing ENU");
+            WOLF_WARN_COND(!sensor_gnss_ptr_->isEnuDefined() && _status == FAC_ACTIVE, "Creating an ACTIVE GNSS Fix 3d factor without initializing ENU");
         }
 
-        virtual ~FactorGnssFix3D() = default;
+        ~FactorGnssFix3d() override = default;
 
         template<typename T>
         bool operator ()(const T* const _x,
@@ -54,7 +77,7 @@ class FactorGnssFix3D : public FactorAutodiff<FactorGnssFix3D, 3, 3, 4, 3, 3, 1,
 };
 
 template<typename T>
-inline bool FactorGnssFix3D::operator ()(const T* const _x,
+inline bool FactorGnssFix3d::operator ()(const T* const _x,
                                          const T* const _o,
                                          const T* const _x_antena,
                                          const T* const _t_ENU_map,
@@ -74,7 +97,7 @@ inline bool FactorGnssFix3D::operator ()(const T* const _x,
     Eigen::Map<const Eigen::Matrix<T,3,1> > t_ENU_map(_t_ENU_map);
     Eigen::Matrix<T,3,1> fix_ECEF = getMeasurement().cast<T>();
 
-    // Transform ECEF 3D Fix Feature to Map coordinates
+    // Transform ECEF 3d Fix Feature to Map coordinates
     Eigen::Matrix<T,3,1> fix_map = R_map_ENU * (R_ENU_ECEF * fix_ECEF + t_ENU_ECEF - t_ENU_map);
 
     // Antena position in Map coordinates
diff --git a/include/gnss/factor/factor_gnss_pseudo_range.h b/include/gnss/factor/factor_gnss_pseudo_range.h
new file mode 100644
index 000000000..ac1825f5a
--- /dev/null
+++ b/include/gnss/factor/factor_gnss_pseudo_range.h
@@ -0,0 +1,190 @@
+//--------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--------
+#ifndef FACTOR_GNSS_PSEUDO_RANGE_H_
+#define FACTOR_GNSS_PSEUDO_RANGE_H_
+
+//Wolf includes
+#include "core/common/wolf.h"
+#include "core/factor/factor_autodiff.h"
+#include "core/frame/frame_base.h"
+#include "gnss/sensor/sensor_gnss.h"
+#include "gnss/capture/capture_gnss.h"
+#include "gnss/feature/feature_gnss_satellite.h"
+
+namespace wolf {
+
+WOLF_PTR_TYPEDEFS(FactorGnssPseudoRange);
+
+class FactorGnssPseudoRange : public FactorAutodiff<FactorGnssPseudoRange, 1, 3, 4, 1, 1, 3, 3, 1, 1, 1>
+{
+    protected:
+        SensorGnssPtr sensor_gnss_ptr_;
+        Eigen::Vector3d satellite_ENU_, satellite_ECEF_;
+        mutable double sagnac_correction_;
+        mutable bool sagnac_set_;
+        bool not_GPS_;
+
+    public:
+
+        FactorGnssPseudoRange(FeatureGnssSatellitePtr& _ftr_ptr,
+                              const SensorGnssPtr& _sensor_gnss_ptr,
+                              const ProcessorBasePtr& _processor_ptr,
+                              bool _apply_loss_function,
+                              FactorStatus _status = FAC_ACTIVE) :
+            FactorAutodiff<FactorGnssPseudoRange, 1, 3, 4, 1, 1, 3, 3, 1, 1, 1>("FactorGnssPseudoRange",
+                                                                                TOP_ABS,
+                                                                                _ftr_ptr,
+                                                                                nullptr,
+                                                                                nullptr,
+                                                                                nullptr,
+                                                                                nullptr,
+                                                                                _processor_ptr,
+                                                                                _apply_loss_function,
+                                                                                _status,
+                                                                                _ftr_ptr->getFrame()->getP(),
+                                                                                _ftr_ptr->getFrame()->getO(),
+                                                                                _ftr_ptr->getCapture()->getStateBlock('T'),
+                                                                                (_ftr_ptr->getSatellite().sys == SYS_GLO ?
+                                                                                     _ftr_ptr->getCapture()->getStateBlock('G') :
+                                                                                     (_ftr_ptr->getSatellite().sys == SYS_GAL ?
+                                                                                             _ftr_ptr->getCapture()->getStateBlock('E') :
+                                                                                             _ftr_ptr->getCapture()->getStateBlock('M'))),//in case GPS, M is set but anyway not used
+                                                                                _sensor_gnss_ptr->getP(),
+                                                                                _sensor_gnss_ptr->getEnuMapTranslation(),
+                                                                                _sensor_gnss_ptr->getEnuMapRoll(),
+                                                                                _sensor_gnss_ptr->getEnuMapPitch(),
+                                                                                _sensor_gnss_ptr->getEnuMapYaw()),
+            sensor_gnss_ptr_(_sensor_gnss_ptr),
+            satellite_ENU_(sensor_gnss_ptr_->getREnuEcef() * _ftr_ptr->getSatellite().pos + sensor_gnss_ptr_->gettEnuEcef()),
+            satellite_ECEF_(_ftr_ptr->getSatellite().pos),
+            sagnac_correction_(0),
+            sagnac_set_(false),
+            not_GPS_(_ftr_ptr->getSatellite().sys != SYS_GPS)
+        {
+            //WOLF_INFO("FactorPseudoRange: sat ", _ftr_ptr->getSatellite().sat,"\n\tpos = ", _ftr_ptr->getSatellite().pos.transpose(), "\n\tprange = ", _ftr_ptr->getMeasurement());
+            WOLF_WARN_COND(!sensor_gnss_ptr_->isEnuDefined() && _status == FAC_ACTIVE, "Creating FactorGnssPseudoRange without initializing ENU");
+        }
+
+        ~FactorGnssPseudoRange() override = default;
+
+        template<typename T>
+        bool operator ()(const T* const _x,
+                         const T* const _o,
+                         const T* const _clock_bias,
+                         const T* const _clock_bias_constellation,
+                         const T* const _x_antena,
+                         const T* const _t_ENU_map,
+                         const T* const _roll_ENU_map,
+                         const T* const _pitch_ENU_map,
+                         const T* const _yaw_ENU_map,
+                         T* _residual) const;
+
+        template<typename T>
+        void initSagnac(const Eigen::Matrix<T,3,1>& antena_ENU) const;
+
+};
+
+template<typename T>
+inline bool FactorGnssPseudoRange::operator ()(const T* const _x,
+                                               const T* const _o,
+                                               const T* const _clock_bias,
+                                               const T* const _clock_bias_constellation,
+                                               const T* const _x_antena,
+                                               const T* const _t_ENU_map,
+                                               const T* const _roll_ENU_map,
+                                               const T* const _pitch_ENU_map,
+                                               const T* const _yaw_ENU_map,
+                                               T* _residual) const
+{
+    Eigen::Map<const Eigen::Matrix<T,3,1> > t_map_base(_x);
+    Eigen::Map<const Eigen::Quaternion<T> > q_map_base(_o);
+    Eigen::Map<const Eigen::Matrix<T,3,1> > t_base_antena(_x_antena);
+    Eigen::Map<const Eigen::Matrix<T,3,1> > t_ENU_map(_t_ENU_map);
+
+    /* NOT EFFICIENT IMPLE;ENTATION
+
+    Eigen::Matrix<T,3,3> R_ENU_map = sensor_gnss_ptr_->computeREnuMap(_roll_ENU_map[0], _pitch_ENU_map[0], _yaw_ENU_map[0]);
+
+    // Antenna position in ENU coordinates
+    Eigen::Matrix<T,3,1> antena_ENU = t_ENU_map + R_ENU_map * (t_map_base + q_map_base * t_base_antena);
+    */
+
+    /* SAGNAC EFFECT CORRECTION
+    * It should be recomputed with the new antenna position in ECEF coordinates,
+    * but it is multiplied by a factor of 1e-14 (earth rotation / CLIGHT).
+    * We use instead a precomputed sagnac effect taking the first value of antenna position
+    */
+    if (!sagnac_set_)
+    {
+        Eigen::Matrix<T,3,1> antena_ENU = t_ENU_map + sensor_gnss_ptr_->computeREnuMap(_roll_ENU_map[0], _pitch_ENU_map[0], _yaw_ENU_map[0]) * (t_map_base + q_map_base * t_base_antena);
+        initSagnac(antena_ENU);
+    }
+
+    // Expected pseudo-range
+    T exp = (t_ENU_map + sensor_gnss_ptr_->computeREnuMap(_roll_ENU_map[0], _pitch_ENU_map[0], _yaw_ENU_map[0]) * (t_map_base + q_map_base * t_base_antena) - satellite_ENU_.cast<T>()).norm() +      // norm
+             sagnac_correction_ +                                // sagnac correction (precomputed)
+             _clock_bias[0] +                                    // receiver clock bias (w.r.t. GPS clock)
+             (not_GPS_ ? _clock_bias_constellation[0] : T(0));   // interconstellation clock bias
+    /*
+    T exp = (antena_ENU-satellite_ENU_.cast<T>()).norm() +      // norm
+            sagnac_correction_ +                                // sagnac correction (precomputed)
+            _clock_bias[0] +                                    // receiver clock bias (w.r.t. GPS clock)
+            (not_GPS_ ? _clock_bias_constellation[0] : T(0));   // interconstellation clock bias
+    */
+
+    //std::cout << "sat " << std::static_pointer_cast<FeatureGnssSatellite>(getFeature())->getSatellite().sat << std::endl;
+    //std::cout << "\tsys " << std::static_pointer_cast<FeatureGnssSatellite>(getFeature())->getSatellite().sys << std::endl;
+    //std::cout << std::setprecision(13) <<"\tsat_pos= " <<  satellite_ENU_.transpose() << "\n";
+    //std::cout << "\tx = " << antena_ENU(0) << " "<<  antena_ENU(1) << " "<<  antena_ENU(2) << "\n";
+    //std::cout << "\tprange = " << std::static_pointer_cast<FeatureGnssSatellite>(getFeature())->getPseudoRange().p << "\n";
+    //std::cout << "\tiono_corr = " << std::static_pointer_cast<FeatureGnssSatellite>(getFeature())->getPseudoRange().iono_corr << "\n";
+    //std::cout << "\ttropo_corr = " << std::static_pointer_cast<FeatureGnssSatellite>(getFeature())->getPseudoRange().tropo_corr << "\n";
+    //std::cout << "\tsat_clock_corr = " << std::static_pointer_cast<FeatureGnssSatellite>(getFeature())->getPseudoRange().sat_clock_corr << "\n";
+    //std::cout << "\tcorrected prange = " << getMeasurement() << "\n";
+    //std::cout << "\tclock_bias = " <<  _clock_bias[0]  << " " << _clock_bias_constellation[0] << "\n";
+    //std::cout << "\tnorm = " <<  (antena_ENU-satellite_ENU_.cast<T>()).norm() + sagnac_correction_ << "\n";
+    //std::cout << "\texp_wo = " <<  (antena_ENU-satellite_ENU_.cast<T>()).norm() + sagnac_correction_ + _clock_bias[0] << "\n";
+    //std::cout << "\texp = " <<  exp << "\n";
+    //std::cout << "\terror = " << exp - getMeasurement()(0) << "\n";
+
+    // Residual
+    _residual[0] = (exp - getMeasurement()(0)) * getMeasurementSquareRootInformationUpper()(0,0);
+
+    return true;
+}
+
+template<typename T>
+void FactorGnssPseudoRange::initSagnac(const Eigen::Matrix<T,3,1>& antena_ENU) const
+{
+}
+
+template<>
+void FactorGnssPseudoRange::initSagnac<double>(const Eigen::Matrix<double,3,1>& antena_ENU) const
+{
+    Eigen::Vector3d antena_ECEF = sensor_gnss_ptr_->getREnuEcef().transpose() * (antena_ENU - sensor_gnss_ptr_->gettEnuEcef());
+    sagnac_correction_ = GnssUtils::computeSagnacCorrection(antena_ECEF, satellite_ECEF_);
+    sagnac_set_ = true;
+}
+
+} // namespace wolf
+
+#endif
diff --git a/include/gnss/factor/factor_gnss_single_diff_2d.h b/include/gnss/factor/factor_gnss_single_diff_2d.h
new file mode 100644
index 000000000..f85495b05
--- /dev/null
+++ b/include/gnss/factor/factor_gnss_single_diff_2d.h
@@ -0,0 +1,126 @@
+//--------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--------
+
+#ifndef FACTOR_GNSS_SINGLE_DIFF_2d_H_
+#define FACTOR_GNSS_SINGLE_DIFF_2d_H_
+
+//Wolf includes
+#include "core/common/wolf.h"
+#include "core/factor/factor_autodiff.h"
+#include "core/frame/frame_base.h"
+#include "gnss/sensor/sensor_gnss.h"
+
+namespace wolf {
+    
+WOLF_PTR_TYPEDEFS(FactorGnssSingleDiff2d);
+
+class FactorGnssSingleDiff2d : public FactorAutodiff<FactorGnssSingleDiff2d, 3, 2, 1, 2, 1, 3, 1, 1, 1>
+{
+    protected:
+        SensorGnssPtr sensor_gnss_ptr_;
+
+    public:
+
+        FactorGnssSingleDiff2d(FeatureBasePtr& _ftr_ptr, const FrameBasePtr& _frame_other_ptr, const SensorGnssPtr& _sensor_gnss_ptr, const ProcessorBasePtr& _processor_ptr, bool _apply_loss_function, FactorStatus _status = FAC_ACTIVE) :
+            FactorAutodiff<FactorGnssSingleDiff2d, 3, 2, 1, 2, 1, 3, 1, 1, 1>("GNSS SINGLE DIFFERENCES 2d",
+                                                                              TOP_GEOM,
+                                                                              _ftr_ptr,
+                                                                              _frame_other_ptr,
+                                                                              nullptr,
+                                                                              nullptr,
+                                                                              nullptr,
+                                                                              _processor_ptr,
+                                                                              _apply_loss_function,
+                                                                              _status,
+                                                                              _frame_other_ptr->getP(),
+                                                                              _frame_other_ptr->getO(),
+                                                                              _ftr_ptr->getFrame()->getP(),
+                                                                              _ftr_ptr->getFrame()->getO(),
+                                                                              _sensor_gnss_ptr->getP(),
+                                                                              _sensor_gnss_ptr->getEnuMapRoll(),
+                                                                              _sensor_gnss_ptr->getEnuMapPitch(),
+                                                                              _sensor_gnss_ptr->getEnuMapYaw()),
+            sensor_gnss_ptr_(_sensor_gnss_ptr)
+        {
+            WOLF_WARN_COND(!sensor_gnss_ptr_->isEnuDefined(), "Creating a GNSS SingleDiff 2d factor without initializing ENU");
+        }
+
+        virtual ~FactorGnssSingleDiff2d() = default;
+
+        template<typename T>
+        bool operator ()(const T* const _x1,
+                         const T* const _o1,
+                         const T* const _x2,
+                         const T* const _o2,
+                         const T* const _x_antena,
+                         const T* const _roll_ENU_MAP,
+                         const T* const _pitch_ENU_MAP,
+                         const T* const _yaw_ENU_MAP,
+                         T* _residuals) const;
+
+};
+
+template<typename T>
+inline bool FactorGnssSingleDiff2d::operator ()(const T* const _x1,
+                                                const T* const _o1,
+                                                const T* const _x2,
+                                                const T* const _o2,
+                                                const T* const _x_antena,
+                                                const T* const _roll_ENU_MAP,
+                                                const T* const _pitch_ENU_MAP,
+                                                const T* const _yaw_ENU_MAP,
+                                                T* _residuals) const
+{
+    Eigen::Map<const Eigen::Matrix<T,2,1> > t_MAP_BASE1(_x1);
+    Eigen::Matrix<T,2,2> R_MAP_BASE1 = Eigen::Rotation2D<T>(_o1[0]).matrix();
+    Eigen::Map<const Eigen::Matrix<T,2,1> > t_MAP_BASE2(_x2);
+    Eigen::Matrix<T,2,2> R_MAP_BASE2 = Eigen::Rotation2D<T>(_o2[0]).matrix();
+    Eigen::Map<const Eigen::Matrix<T,3,1> > t_BASE_ANTENA(_x_antena);
+    Eigen::Map<Eigen::Matrix<T,3,1> > residuals_ECEF(_residuals);
+
+    Eigen::Matrix<T,3,3> R_ENU_ECEF = sensor_gnss_ptr_->getREnuEcef().cast<T>();
+    Eigen::Matrix<T,2,3> R_MAP_ENU = sensor_gnss_ptr_->computeREnuMap(_roll_ENU_MAP[0], _pitch_ENU_MAP[0], _yaw_ENU_MAP[0]).transpose().topRows(2);
+
+    // Transform ECEF 3d SingleDiff Feature to 2d SingleDiff in Map coordinates (removing z)
+    Eigen::Matrix<T,2,1> measured_diff_2d_MAP = R_MAP_ENU * (R_ENU_ECEF * getMeasurement().cast<T>());
+
+    // Substraction of expected antena positions in Map coordinates
+    Eigen::Matrix<T,2,1> expected_diff_2d_MAP = (R_MAP_BASE2 * t_BASE_ANTENA.head(2) + t_MAP_BASE2) - (R_MAP_BASE1 * t_BASE_ANTENA.head(2) + t_MAP_BASE1);
+
+    // Compute residual rotating information matrix to 2d Map coordinates
+    // Covariance & information are rotated by R*S*R' so for the squred root upper (or right) R*L*U*R'->U*R'
+    // In this case R = R_2dMAP_ENU * R_ENU_ECEF, then R' = R_ENU_ECEF' * R_2dMAP_ENU'
+    residuals_ECEF = (getMeasurementSquareRootInformationUpper().cast<T>() * R_ENU_ECEF.transpose() * R_MAP_ENU.transpose()) * (expected_diff_2d_MAP - measured_diff_2d_MAP);
+
+    //std::cout << "frame1: " << _x1[0] << " " << _x1[1] << " " << _o1[0] << std::endl;
+    //std::cout << "frame2: " << _x2[0] << " " << _x2[1] << " " << _o2[0] << std::endl;
+    //std::cout << "antena: " << _x_antena[0] << " " << _x_antena[1] << " " << _x_antena[2] << std::endl;
+    //std::cout << "RPY: " << _roll[0] << " " << _pitch[1] << " " << _yaw[2] << std::endl;
+    //std::cout << "measured_diff_2d: " << measured_diff_2d[0] << " " << measured_diff_2d[1] << std::endl;
+    //std::cout << "expected_diff_2d: " << expected_diff_2d[0] << " " << expected_diff_2d[1] << std::endl;
+
+    return true;
+}
+
+} // namespace wolf
+
+#endif
diff --git a/include/gnss/factor/factor_gnss_tdcp.h b/include/gnss/factor/factor_gnss_tdcp.h
new file mode 100644
index 000000000..fb19510fb
--- /dev/null
+++ b/include/gnss/factor/factor_gnss_tdcp.h
@@ -0,0 +1,213 @@
+//--------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--------
+#ifndef FACTOR_GNSS_TDCP_H_
+#define FACTOR_GNSS_TDCP_H_
+
+//Wolf includes
+#include "core/common/wolf.h"
+#include "core/factor/factor_autodiff.h"
+#include "core/frame/frame_base.h"
+#include "gnss/sensor/sensor_gnss.h"
+#include "gnss/capture/capture_gnss.h"
+#include "gnss/feature/feature_gnss_satellite.h"
+
+namespace wolf {
+
+WOLF_PTR_TYPEDEFS(FactorGnssTdcp);
+
+class FactorGnssTdcp : public FactorAutodiff<FactorGnssTdcp, 1, 3, 4, 1, 3, 4, 1, 3, 3, 1, 1, 1>
+{
+    protected:
+        SensorGnssPtr sensor_gnss_ptr_;
+        mutable double d_pseudo_range_;
+        mutable bool sagnac_set_;
+        double std_dev_;
+        Eigen::Vector3d satellite_ENU_k_, satellite_ENU_r_;
+
+    public:
+
+        FactorGnssTdcp(const double& _std_dev,
+                       FeatureGnssSatellitePtr& _ftr_r,
+                       FeatureGnssSatellitePtr& _ftr_k,
+                       const SensorGnssPtr& _sensor_gnss_ptr,
+                       const ProcessorBasePtr& _processor_ptr,
+                       bool _apply_loss_function,
+                       FactorStatus _status = FAC_ACTIVE) :
+            FactorAutodiff<FactorGnssTdcp, 1, 3, 4, 1, 3, 4, 1, 3, 3, 1, 1, 1>("FactorGnssTdcp",
+                                                                               TOP_GEOM,
+                                                                               _ftr_k,
+                                                                               nullptr,
+                                                                               _ftr_r->getCapture(),
+                                                                               _ftr_r,
+                                                                               nullptr,
+                                                                               _processor_ptr,
+                                                                               _apply_loss_function,
+                                                                               _status,
+                                                                               _ftr_r->getFrame()->getP(),
+                                                                               _ftr_r->getFrame()->getO(),
+                                                                               _ftr_r->getCapture()->getStateBlock('T'),
+                                                                               _ftr_k->getFrame()->getP(),
+                                                                               _ftr_k->getFrame()->getO(),
+                                                                               _ftr_k->getCapture()->getStateBlock('T'),
+                                                                               _sensor_gnss_ptr->getP(),
+                                                                               _sensor_gnss_ptr->getEnuMapTranslation(),
+                                                                               _sensor_gnss_ptr->getEnuMapRoll(),
+                                                                               _sensor_gnss_ptr->getEnuMapPitch(),
+                                                                               _sensor_gnss_ptr->getEnuMapYaw()),
+            sensor_gnss_ptr_(_sensor_gnss_ptr),
+            sagnac_set_(false),
+            std_dev_(_std_dev),
+            satellite_ENU_k_(sensor_gnss_ptr_->getREnuEcef() * _ftr_k->getSatellite().pos + sensor_gnss_ptr_->gettEnuEcef()),
+            satellite_ENU_r_(sensor_gnss_ptr_->getREnuEcef() * _ftr_r->getSatellite().pos + sensor_gnss_ptr_->gettEnuEcef())
+        {
+            assert(_ftr_r != _ftr_k);
+            assert(_ftr_r->getCapture()->getStateBlock('T') != nullptr);
+            assert(_ftr_k->getCapture()->getStateBlock('T') != nullptr);
+
+            WOLF_WARN_COND(!sensor_gnss_ptr_->isEnuDefined() && _status == FAC_ACTIVE, "Creating an FactorGnssTdcp without initializing ENU");
+
+            d_pseudo_range_ = _ftr_k->getRange().L_corrected - _ftr_r->getRange().L_corrected;
+        }
+
+        ~FactorGnssTdcp() override = default;
+
+        template<typename T>
+        bool operator ()(const T* const _x_r,
+                         const T* const _o_r,
+                         const T* const _clock_bias_r,
+                         const T* const _x_k,
+                         const T* const _o_k,
+                         const T* const _clock_bias_k,
+                         const T* const _x_antena,
+                         const T* const _t_ENU_map,
+                         const T* const _roll_ENU_map,
+                         const T* const _pitch_ENU_map,
+                         const T* const _yaw_ENU_map,
+                         T* _residual) const;
+
+        template<typename T>
+        void initSagnac(const Eigen::Matrix<T,3,1>& antena_r_ENU, const Eigen::Matrix<T,3,1>& antena_k_ENU) const;
+
+};
+
+template<typename T>
+inline bool FactorGnssTdcp::operator ()(const T* const _x_r,
+                                        const T* const _o_r,
+                                        const T* const _clock_bias_r,
+                                        const T* const _x_k,
+                                        const T* const _o_k,
+                                        const T* const _clock_bias_k,
+                                        const T* const _x_antena,
+                                        const T* const _t_ENU_map,
+                                        const T* const _roll_ENU_map,
+                                        const T* const _pitch_ENU_map,
+                                        const T* const _yaw_ENU_map,
+                                        T* _residual) const
+{
+    Eigen::Map<const Eigen::Matrix<T,3,1> > t_map_base_r(_x_r);
+    Eigen::Map<const Eigen::Quaternion<T> > q_map_base_r(_o_r);
+    Eigen::Map<const Eigen::Matrix<T,3,1> > t_map_base_k(_x_k);
+    Eigen::Map<const Eigen::Quaternion<T> > q_map_base_k(_o_k);
+    Eigen::Map<const Eigen::Matrix<T,3,1> > t_base_antena(_x_antena);
+    Eigen::Map<const Eigen::Matrix<T,3,1> > t_ENU_map(_t_ENU_map);
+
+    Eigen::Matrix<T,3,3> R_ENU_map = sensor_gnss_ptr_->computeREnuMap(_roll_ENU_map[0], _pitch_ENU_map[0], _yaw_ENU_map[0]);
+
+    /* INEFFICIENT IMPLEMENTATION
+    // Antenna position in ECEF coordinates
+    Eigen::Matrix<T,3,1> antena_r_ENU = t_ENU_map + R_ENU_map * (t_map_base_r + q_map_base_r * t_base_antena);
+    Eigen::Matrix<T,3,1> antena_k_ENU = t_ENU_map + R_ENU_map * (t_map_base_k + q_map_base_k * t_base_antena);
+
+    // Expected tdcp
+    T exp = (antena_k_ENU-satellite_ENU_k_.cast<T>()).norm()
+          + _clock_bias_k[0]
+          - (antena_r_ENU-satellite_ENU_r_.cast<T>()).norm()
+          - _clock_bias_r[0] ;
+    */
+    // Expected tdcp
+    //T exp = (t_ENU_map + R_ENU_map * (t_map_base_k + q_map_base_k * t_base_antena)-satellite_ENU_k_.cast<T>()).norm()
+    //      + _clock_bias_k[0]
+    //      - (t_ENU_map + R_ENU_map * (t_map_base_r + q_map_base_r * t_base_antena)-satellite_ENU_r_.cast<T>()).norm()
+    //      - _clock_bias_r[0];
+
+    //std::cout << "sat_r " << std::static_pointer_cast<FeatureGnssSatellite>(getFeatureOther())->getSatellite().sat << std::endl;
+    //std::cout << "sat_k " << std::static_pointer_cast<FeatureGnssSatellite>(getFeature())->getSatellite().sat << std::endl;
+    //std::cout << std::setprecision(13) <<"\tsat_r_pos= " <<  satellite_ENU_r_.transpose() << "\n";
+    //std::cout << "\tsat_k_pos= " <<  satellite_ENU_k_.transpose() << "\n";
+    //std::cout << "\tantena_r_ENU = " << antena_r_ENU(0) << " "<<  antena_r_ENU(1) << " "<<  antena_r_ENU(2) << "\n";
+    //std::cout << "\tantena_k_ENU = " << antena_k_ENU(0) << " "<<  antena_k_ENU(1) << " "<<  antena_k_ENU(2) << "\n";
+    //std::cout << "\tdiff_range = " << d_pseudo_range_ << "\n";
+    //std::cout << "\tclock_bias_r = " <<  _clock_bias_r[0]  << "\n";
+    //std::cout << "\tclock_bias_k = " <<  _clock_bias_k[0]  << "\n";
+    //std::cout << "\tnorm_r = " <<  (antena_r_ENU-satellite_ENU_r_.cast<T>()).norm() << "\n";
+    //std::cout << "\tnorm_k = " <<  (antena_k_ENU-satellite_ENU_k_.cast<T>()).norm() << "\n";
+    //std::cout << "\tnorm_r (t corrected) = " <<  (antena_r_ENU-satellite_ENU_r_.cast<T>()).norm() + _clock_bias_r[0] << "\n";
+    //std::cout << "\tnorm_k (t corrected) = " <<  (antena_k_ENU-satellite_ENU_k_.cast<T>()).norm() + _clock_bias_k[0] << "\n";
+    //std::cout << "\texp = " <<  exp << "\n";
+    //std::cout << "\terror = " << exp - d_pseudo_range_ << "\n";
+
+    /* SAGNAC EFFECT CORRECTION
+    * It should be recomputed with the new antenna position in ECEF coordinates,
+    * but it is multiplied by a factor of 1e-14 (earth rotation / CLIGHT).
+    * We use instead a precomputed sagnac effect taking the first values of antenna position
+    */
+    if (not sagnac_set_)
+    {
+        Eigen::Matrix<T,3,1> antena_r_ENU = t_ENU_map + sensor_gnss_ptr_->computeREnuMap(_roll_ENU_map[0], _pitch_ENU_map[0], _yaw_ENU_map[0]) * (t_map_base_r + q_map_base_r * t_base_antena);
+        Eigen::Matrix<T,3,1> antena_k_ENU = t_ENU_map + sensor_gnss_ptr_->computeREnuMap(_roll_ENU_map[0], _pitch_ENU_map[0], _yaw_ENU_map[0]) * (t_map_base_k + q_map_base_k * t_base_antena);
+        initSagnac(antena_r_ENU, antena_k_ENU);
+    }
+
+    // Residual
+    _residual[0] = ((t_ENU_map + R_ENU_map * (t_map_base_k + q_map_base_k * t_base_antena)-satellite_ENU_k_.cast<T>()).norm()
+                      + _clock_bias_k[0]
+                      - (t_ENU_map + R_ENU_map * (t_map_base_r + q_map_base_r * t_base_antena)-satellite_ENU_r_.cast<T>()).norm()
+                      - _clock_bias_r[0]
+                      - d_pseudo_range_) / std_dev_;
+
+    return true;
+}
+
+template<typename T>
+void FactorGnssTdcp::initSagnac(const Eigen::Matrix<T,3,1>& antena_r_ENU, const Eigen::Matrix<T,3,1>& antena_k_ENU) const
+{
+}
+
+template<>
+void FactorGnssTdcp::initSagnac<double>(const Eigen::Matrix<double,3,1>& antena_r_ENU, const Eigen::Matrix<double,3,1>& antena_k_ENU) const
+{
+    Eigen::Vector3d antena_r_ECEF = sensor_gnss_ptr_->getREnuEcef().transpose() * (antena_r_ENU - sensor_gnss_ptr_->gettEnuEcef());
+    Eigen::Vector3d sat_r_ECEF = sensor_gnss_ptr_->getREnuEcef().transpose() * (satellite_ENU_r_ - sensor_gnss_ptr_->gettEnuEcef());
+    double sagnac_corr_r = GnssUtils::computeSagnacCorrection(antena_r_ECEF, sat_r_ECEF);
+
+    Eigen::Vector3d antena_k_ECEF = sensor_gnss_ptr_->getREnuEcef().transpose() * (antena_k_ENU - sensor_gnss_ptr_->gettEnuEcef());
+    Eigen::Vector3d sat_k_ECEF = sensor_gnss_ptr_->getREnuEcef().transpose() * (satellite_ENU_k_ - sensor_gnss_ptr_->gettEnuEcef());
+    double sagnac_corr_k = GnssUtils::computeSagnacCorrection(antena_k_ECEF, sat_k_ECEF);
+
+    d_pseudo_range_ += -sagnac_corr_k + sagnac_corr_r;
+
+    sagnac_set_ = true;
+}
+
+} // namespace wolf
+
+#endif
diff --git a/include/gnss/factor/factor_gnss_single_diff_2D.h b/include/gnss/factor/factor_gnss_tdcp_2d.h
similarity index 70%
rename from include/gnss/factor/factor_gnss_single_diff_2D.h
rename to include/gnss/factor/factor_gnss_tdcp_2d.h
index 3cadfcb24..049106a4d 100644
--- a/include/gnss/factor/factor_gnss_single_diff_2D.h
+++ b/include/gnss/factor/factor_gnss_tdcp_2d.h
@@ -1,6 +1,27 @@
-
-#ifndef FACTOR_GNSS_SINGLE_DIFF_2D_H_
-#define FACTOR_GNSS_SINGLE_DIFF_2D_H_
+//--------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--------
+
+#ifndef FACTOR_GNSS_TDCP_2D_H_
+#define FACTOR_GNSS_TDCP_2D_H_
 
 //Wolf includes
 #include "core/common/wolf.h"
@@ -10,38 +31,40 @@
 
 namespace wolf {
     
-WOLF_PTR_TYPEDEFS(FactorGnssSingleDiff2D);
+WOLF_PTR_TYPEDEFS(FactorGnssTdcp2d);
 
-class FactorGnssSingleDiff2D : public FactorAutodiff<FactorGnssSingleDiff2D, 3, 2, 1, 2, 1, 3, 1, 1, 1>
+class FactorGnssTdcp2d : public FactorAutodiff<FactorGnssTdcp2d, 3, 2, 1, 2, 1, 3, 1, 1, 1>
 {
     protected:
         SensorGnssPtr sensor_gnss_ptr_;
 
     public:
 
-        FactorGnssSingleDiff2D(FeatureBasePtr& _ftr_ptr, const FrameBasePtr& _frame_other_ptr, const SensorGnssPtr& _sensor_gnss_ptr, const ProcessorBasePtr& _processor_ptr, bool _apply_loss_function = false, FactorStatus _status = FAC_ACTIVE) :
-            FactorAutodiff<FactorGnssSingleDiff2D, 3, 2, 1, 2, 1, 3, 1, 1, 1>("GNSS SINGLE DIFFERENCES 2D",
-                                                                              _frame_other_ptr,
-                                                                              nullptr,
-                                                                              nullptr,
-                                                                              nullptr,
-                                                                              _processor_ptr,
-                                                                              _apply_loss_function,
-                                                                              _status,
-                                                                              _frame_other_ptr->getP(),
-                                                                              _frame_other_ptr->getO(),
-                                                                              _ftr_ptr->getFrame()->getP(),
-                                                                              _ftr_ptr->getFrame()->getO(),
-                                                                              _sensor_gnss_ptr->getStateBlock(0),
-                                                                              _sensor_gnss_ptr->getEnuMapRoll(),
-                                                                              _sensor_gnss_ptr->getEnuMapPitch(),
-                                                                              _sensor_gnss_ptr->getEnuMapYaw()),
+        FactorGnssTdcp2d(const FeatureBasePtr& _ftr_ptr, const FrameBasePtr& _frame_other_ptr, const SensorGnssPtr& _sensor_gnss_ptr, const ProcessorBasePtr& _processor_ptr, bool _apply_loss_function = false, FactorStatus _status = FAC_ACTIVE) :
+            FactorAutodiff<FactorGnssTdcp2d, 3, 2, 1, 2, 1, 3, 1, 1, 1>("FactorGnssTdcp2d",
+                                                                        TOP_GEOM,
+                                                                        _ftr_ptr,
+                                                                        _frame_other_ptr,
+                                                                        nullptr,
+                                                                        nullptr,
+                                                                        nullptr,
+                                                                        _processor_ptr,
+                                                                        _apply_loss_function,
+                                                                        _status,
+                                                                        _frame_other_ptr->getP(),
+                                                                        _frame_other_ptr->getO(),
+                                                                        _ftr_ptr->getFrame()->getP(),
+                                                                        _ftr_ptr->getFrame()->getO(),
+                                                                        _sensor_gnss_ptr->getP(),
+                                                                        _sensor_gnss_ptr->getEnuMapRoll(),
+                                                                        _sensor_gnss_ptr->getEnuMapPitch(),
+                                                                        _sensor_gnss_ptr->getEnuMapYaw()),
             sensor_gnss_ptr_(_sensor_gnss_ptr)
         {
             WOLF_WARN_COND(!sensor_gnss_ptr_->isEnuDefined(), "Creating a GNSS SingleDiff 2D factor without initializing ENU");
         }
 
-        virtual ~FactorGnssSingleDiff2D() = default;
+        ~FactorGnssTdcp2d() override = default;
 
         template<typename T>
         bool operator ()(const T* const _x1,
@@ -57,7 +80,7 @@ class FactorGnssSingleDiff2D : public FactorAutodiff<FactorGnssSingleDiff2D, 3,
 };
 
 template<typename T>
-inline bool FactorGnssSingleDiff2D::operator ()(const T* const _x1,
+inline bool FactorGnssTdcp2d::operator ()(const T* const _x1,
                                                 const T* const _o1,
                                                 const T* const _x2,
                                                 const T* const _o2,
diff --git a/include/gnss/factor/factor_gnss_tdcp_3d.h b/include/gnss/factor/factor_gnss_tdcp_3d.h
new file mode 100644
index 000000000..f55c668ec
--- /dev/null
+++ b/include/gnss/factor/factor_gnss_tdcp_3d.h
@@ -0,0 +1,127 @@
+//--------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--------
+
+#ifndef FACTOR_GNSS_TDCP_3D_H_
+#define FACTOR_GNSS_TDCP_3D_H_
+
+//Wolf includes
+#include "core/common/wolf.h"
+#include "core/factor/factor_autodiff.h"
+#include "core/frame/frame_base.h"
+#include "gnss/sensor/sensor_gnss.h"
+
+namespace wolf {
+    
+WOLF_PTR_TYPEDEFS(FactorGnssTdcp3d);
+
+class FactorGnssTdcp3d : public FactorAutodiff<FactorGnssTdcp3d, 3, 3, 4, 3, 4, 3, 1, 1, 1>
+{
+    protected:
+        SensorGnssPtr sensor_gnss_ptr_;
+
+    public:
+
+        FactorGnssTdcp3d(const FeatureBasePtr& _ftr_ptr,
+                         const FrameBasePtr& _frame_other_ptr,
+                         const SensorGnssPtr& _sensor_gnss_ptr,
+                         const ProcessorBasePtr& _processor_ptr,
+                         bool _apply_loss_function,
+                         FactorStatus _status = FAC_ACTIVE) :
+            FactorAutodiff<FactorGnssTdcp3d, 3, 3, 4, 3, 4, 3, 1, 1, 1>("FactorGnssTdcp3d",
+                                                                        TOP_GEOM,
+                                                                        _ftr_ptr,
+                                                                        _frame_other_ptr,
+                                                                        nullptr,
+                                                                        nullptr,
+                                                                        nullptr,
+                                                                        _processor_ptr,
+                                                                        _apply_loss_function,
+                                                                        _status,
+                                                                        _frame_other_ptr->getP(),
+                                                                        _frame_other_ptr->getO(),
+                                                                        _ftr_ptr->getFrame()->getP(),
+                                                                        _ftr_ptr->getFrame()->getO(),
+                                                                        _sensor_gnss_ptr->getP(),
+                                                                        _sensor_gnss_ptr->getEnuMapRoll(),
+                                                                        _sensor_gnss_ptr->getEnuMapPitch(),
+                                                                        _sensor_gnss_ptr->getEnuMapYaw()),
+            sensor_gnss_ptr_(_sensor_gnss_ptr)
+        {
+            assert(_ftr_ptr->getMeasurement().size() == 3 && "FactorGnssTdcp3d uses 3d measurements. For FeatureGnssTdcp with also delta clock, use FactorGnssTdcpBatch instead");
+            WOLF_WARN_COND(!sensor_gnss_ptr_->isEnuDefined(), "Creating a GNSS SingleDiff 3D factor without initializing ENU");
+        }
+
+        ~FactorGnssTdcp3d() override = default;
+
+        template<typename T>
+        bool operator ()(const T* const _x1,
+                         const T* const _o1,
+                         const T* const _x2,
+                         const T* const _o2,
+                         const T* const _x_antena,
+                         const T* const _roll_ENU_MAP,
+                         const T* const _pitch_ENU_MAP,
+                         const T* const _yaw_ENU_MAP,
+                         T* _residuals) const;
+
+};
+
+template<typename T>
+inline bool FactorGnssTdcp3d::operator ()(const T* const _x1,
+                                          const T* const _o1,
+                                          const T* const _x2,
+                                          const T* const _o2,
+                                          const T* const _x_antena,
+                                          const T* const _roll_ENU_MAP,
+                                          const T* const _pitch_ENU_MAP,
+                                          const T* const _yaw_ENU_MAP,
+                                          T* _residuals) const
+{
+    Eigen::Map<const Eigen::Matrix<T,3,1>> t_MAP_BASE1(_x1);
+    Eigen::Map<const Eigen::Quaternion<T>> q_MAP_BASE1(_o1);
+    Eigen::Map<const Eigen::Matrix<T,3,1>> t_MAP_BASE2(_x2);
+    Eigen::Map<const Eigen::Quaternion<T>> q_MAP_BASE2(_o2);
+    Eigen::Map<const Eigen::Matrix<T,3,1>> t_BASE_ANTENA(_x_antena);
+    Eigen::Map<Eigen::Matrix<T,3,1> > residuals_ECEF(_residuals);
+
+    Eigen::Matrix<T,3,3> R_ECEF_ENU = sensor_gnss_ptr_->getREnuEcef().transpose().cast<T>();
+    Eigen::Matrix<T,3,3> R_ENU_MAP = sensor_gnss_ptr_->computeREnuMap(_roll_ENU_MAP[0], _pitch_ENU_MAP[0], _yaw_ENU_MAP[0]);
+
+    // Expected displacement
+    Eigen::Matrix<T,3,1> expected_ECEF = R_ECEF_ENU * R_ENU_MAP * ((q_MAP_BASE2 * t_BASE_ANTENA + t_MAP_BASE2) - (q_MAP_BASE1 * t_BASE_ANTENA + t_MAP_BASE1));
+
+    // Compute residual
+    residuals_ECEF = getMeasurementSquareRootInformationUpper().cast<T>() * (expected_ECEF - getMeasurement().cast<T>());
+
+    //std::cout << "frame1: " << _x1[0] << " " << _x1[1] << " " << _x1[2] << " " << _o1[0] << " " << _o1[1] << " " << _o1[2] << " " << _o1[3] << std::endl;
+    //std::cout << "frame2: " << _x2[0] << " " << _x2[1] << " " << _x2[2] << " " << _o2[0] << " " << _o2[1] << " " << _o2[2] << " " << _o2[3] << std::endl;
+    //std::cout << "antena: " << _x_antena[0] << " " << _x_antena[1] << " " << _x_antena[2] << std::endl;
+    //std::cout << "RPY: " << _roll[0] << " " << _pitch[1] << " " << _yaw[2] << std::endl;
+    //std::cout << "expected_ECEF: " << expected_ECEF[0] << " " << expected_ECEF[1] << " " << expected_ECEF[2] << std::endl;
+    //std::cout << "getMeasurement(): " << getMeasurement().transpose << std::endl;
+
+    return true;
+}
+
+} // namespace wolf
+
+#endif
diff --git a/include/gnss/factor/factor_gnss_tdcp_batch.h b/include/gnss/factor/factor_gnss_tdcp_batch.h
new file mode 100644
index 000000000..1c0e9ecff
--- /dev/null
+++ b/include/gnss/factor/factor_gnss_tdcp_batch.h
@@ -0,0 +1,139 @@
+//--------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--------
+
+#ifndef FACTOR_GNSS_TDCP_BATCH_H_
+#define FACTOR_GNSS_TDCP_BATCH_H_
+
+//Wolf includes
+#include "core/common/wolf.h"
+#include "core/factor/factor_autodiff.h"
+#include "gnss/sensor/sensor_gnss.h"
+
+namespace wolf {
+    
+WOLF_PTR_TYPEDEFS(FactorGnssTdcpBatch);
+
+class FactorGnssTdcpBatch : public FactorAutodiff<FactorGnssTdcpBatch, 4, 3, 4, 1, 3, 4, 1, 3, 1, 1, 1>
+{
+    protected:
+        SensorGnssPtr sensor_gnss_ptr_;
+
+    public:
+
+        FactorGnssTdcpBatch(const FeatureBasePtr& _ftr_ptr,
+                            const CaptureBasePtr& _capture_other_ptr,
+                            const SensorGnssPtr& _sensor_gnss_ptr,
+                            const ProcessorBasePtr& _processor_ptr,
+                            bool _apply_loss_function = false,
+                            FactorStatus _status = FAC_ACTIVE) :
+            FactorAutodiff<FactorGnssTdcpBatch, 4, 3, 4, 1, 3, 4, 1, 3, 1, 1, 1>("FactorGnssTdcpBatch",
+                                                                                 TOP_GEOM,
+                                                                                 _ftr_ptr,
+                                                                                 _capture_other_ptr->getFrame(),
+                                                                                 _capture_other_ptr,
+                                                                                 nullptr,
+                                                                                 nullptr,
+                                                                                 _processor_ptr,
+                                                                                 _apply_loss_function,
+                                                                                 _status,
+                                                                                 _capture_other_ptr->getFrame()->getP(),
+                                                                                 _capture_other_ptr->getFrame()->getO(),
+                                                                                 _capture_other_ptr->getStateBlock('T'),
+                                                                                 _ftr_ptr->getFrame()->getP(),
+                                                                                 _ftr_ptr->getFrame()->getO(),
+                                                                                 _ftr_ptr->getCapture()->getStateBlock('T'),
+                                                                                 _sensor_gnss_ptr->getP(),
+                                                                                 _sensor_gnss_ptr->getEnuMapRoll(),
+                                                                                 _sensor_gnss_ptr->getEnuMapPitch(),
+                                                                                 _sensor_gnss_ptr->getEnuMapYaw()),
+            sensor_gnss_ptr_(_sensor_gnss_ptr)
+        {
+            assert(_ftr_ptr->getMeasurement().size() == 4 && "FactorGnssTdcpBatch uses 4d measurements (pos.displacement, delta clock). For FeatureGnssTdcp with only displacement, use FactorGnssTdcp3d instead");
+            WOLF_WARN_COND(!sensor_gnss_ptr_->isEnuDefined(), "Creating a GNSS SingleDiff 3D factor without initializing ENU");
+        }
+
+        ~FactorGnssTdcpBatch() override = default;
+
+        template<typename T>
+        bool operator ()(const T* const _x1,
+                         const T* const _o1,
+                         const T* const _t1,
+                         const T* const _x2,
+                         const T* const _o2,
+                         const T* const _t2,
+                         const T* const _x_antena,
+                         const T* const _roll_ENU_MAP,
+                         const T* const _pitch_ENU_MAP,
+                         const T* const _yaw_ENU_MAP,
+                         T* _residuals) const;
+
+};
+
+template<typename T>
+inline bool FactorGnssTdcpBatch::operator ()(const T* const _x1,
+                                             const T* const _o1,
+                                             const T* const _t1,
+                                             const T* const _x2,
+                                             const T* const _o2,
+                                             const T* const _t2,
+                                             const T* const _x_antena,
+                                             const T* const _roll_ENU_MAP,
+                                             const T* const _pitch_ENU_MAP,
+                                             const T* const _yaw_ENU_MAP,
+                                             T* _residuals) const
+{
+    Eigen::Map<const Eigen::Matrix<T,3,1>> t_MAP_BASE1(_x1);
+    Eigen::Map<const Eigen::Quaternion<T>> q_MAP_BASE1(_o1);
+    Eigen::Map<const Eigen::Matrix<T,3,1>> t_MAP_BASE2(_x2);
+    Eigen::Map<const Eigen::Quaternion<T>> q_MAP_BASE2(_o2);
+    Eigen::Map<const Eigen::Matrix<T,3,1>> t_BASE_ANTENA(_x_antena);
+    Eigen::Map<Eigen::Matrix<T,3,1> > disp_residuals(_residuals);
+    Eigen::Map<Eigen::Matrix<T,4,1> > residuals(_residuals);
+
+    Eigen::Matrix<T,3,3> R_ECEF_ENU = sensor_gnss_ptr_->getREnuEcef().transpose().cast<T>();
+    Eigen::Matrix<T,3,3> R_ENU_MAP = sensor_gnss_ptr_->computeREnuMap(_roll_ENU_MAP[0], _pitch_ENU_MAP[0], _yaw_ENU_MAP[0]);
+
+    // Expected d
+    Eigen::Matrix<T,4,1> exp;
+
+    // Expected displacement in ECEF
+    exp.head(3) = R_ECEF_ENU * R_ENU_MAP * ((q_MAP_BASE2 * t_BASE_ANTENA + t_MAP_BASE2) - (q_MAP_BASE1 * t_BASE_ANTENA + t_MAP_BASE1));
+
+    // clock error
+    exp(3) = *_t2 - *_t1;
+
+    // Compute residual
+    residuals = getMeasurementSquareRootInformationUpper().cast<T>() * (exp - getMeasurement().cast<T>());
+
+    //std::cout << "frame1: " << _x1[0] << " " << _x1[1] << " " << _x1[2] << " " << _o1[0] << " " << _o1[1] << " " << _o1[2] << " " << _o1[3] << std::endl;
+    //std::cout << "frame2: " << _x2[0] << " " << _x2[1] << " " << _x2[2] << " " << _o2[0] << " " << _o2[1] << " " << _o2[2] << " " << _o2[3] << std::endl;
+    //std::cout << "antena: " << _x_antena[0] << " " << _x_antena[1] << " " << _x_antena[2] << std::endl;
+    //std::cout << "RPY: " << _roll[0] << " " << _pitch[1] << " " << _yaw[2] << std::endl;
+    //std::cout << "expected_ECEF: " << expected_ECEF[0] << " " << expected_ECEF[1] << " " << expected_ECEF[2] << std::endl;
+    //std::cout << "getMeasurement(): " << getMeasurement().transpose << std::endl;
+
+    return true;
+}
+
+} // namespace wolf
+
+#endif
diff --git a/include/gnss/feature/feature_gnss_fix.h b/include/gnss/feature/feature_gnss_fix.h
index f33fb8557..60f8debf6 100644
--- a/include/gnss/feature/feature_gnss_fix.h
+++ b/include/gnss/feature/feature_gnss_fix.h
@@ -1,3 +1,24 @@
+//--------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--------
 #ifndef FEATURE_GNSS_FIX_H_
 #define FEATURE_GNSS_FIX_H_
 
@@ -5,11 +26,13 @@
 #include "core/feature/feature_base.h"
 
 //std includes
+#include "gnss_utils/gnss_utils.h"
+#include "gnss_utils/utils/rcv_position.h"
 
 namespace wolf {
 
 WOLF_PTR_TYPEDEFS(FeatureGnssFix);
-    
+
 //class FeatureGnssFix
 class FeatureGnssFix : public FeatureBase
 {
@@ -21,11 +44,20 @@ class FeatureGnssFix : public FeatureBase
          * \param _meas_covariance the noise of the measurement
          *
          */
-        FeatureGnssFix(const Eigen::Vector3s& _measurement, const Eigen::Matrix3s& _meas_covariance) :
-            FeatureBase("FeatureGnssFix", _measurement, _meas_covariance)
-        {};
+        FeatureGnssFix(const GnssUtils::ComputePosOutput& _gnss_fix_output);
+
+        ~FeatureGnssFix() override{};
+
+    private:
+      GnssUtils::ComputePosOutput gnss_fix_output_;
 
-        virtual ~FeatureGnssFix(){};
+};
+
+inline FeatureGnssFix::FeatureGnssFix(const GnssUtils::ComputePosOutput& _gnss_fix_output) :
+        FeatureBase("FeatureGnssFix", _gnss_fix_output.pos, _gnss_fix_output.pos_covar),
+        gnss_fix_output_(_gnss_fix_output)
+{
+    //
 };
 
 } // namespace wolf
diff --git a/include/gnss/feature/feature_gnss_satellite.h b/include/gnss/feature/feature_gnss_satellite.h
new file mode 100644
index 000000000..ff98d7177
--- /dev/null
+++ b/include/gnss/feature/feature_gnss_satellite.h
@@ -0,0 +1,101 @@
+//--------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--------
+#ifndef FEATURE_GNSS_SATELLITE_H_
+#define FEATURE_GNSS_SATELLITE_H_
+
+//Wolf includes
+#include "core/feature/feature_base.h"
+
+//std includes
+#include "gnss_utils/range.h"
+#include "gnss_utils/utils/satellite.h"
+
+namespace wolf {
+
+WOLF_PTR_TYPEDEFS(FeatureGnssSatellite);
+WOLF_LIST_TYPEDEFS(FeatureGnssSatellite);
+
+//class FeatureGnssSatellite
+class FeatureGnssSatellite : public FeatureBase
+{
+    public:
+
+        /** \brief Constructor
+         *
+         * \param _obs_sat satellite observation in rtklib format
+         *
+         * This constructor will take the pseudorange as measurement with 1 m² of variance
+         *
+         */
+        FeatureGnssSatellite(const obsd_t& _obs_sat, const GnssUtils::Satellite& _sat, const GnssUtils::Range& _range);
+
+        ~FeatureGnssSatellite() override{};
+
+        const obsd_t& getObservation() const;
+        int satNumber() const;
+        const GnssUtils::Satellite& getSatellite() const;
+        const GnssUtils::Range& getRange() const;
+
+    private:
+      obsd_t obs_sat_;
+      GnssUtils::Satellite sat_;
+      GnssUtils::Range range_;
+};
+
+
+} // namespace wolf
+
+// IMPLEMENTATION
+namespace wolf {
+
+inline FeatureGnssSatellite::FeatureGnssSatellite(const obsd_t& _obs_sat, const GnssUtils::Satellite& _sat, const GnssUtils::Range& _range) :
+    FeatureBase("FeatureGnssPseudoRange", Eigen::Vector1d(_range.P_corrected), Eigen::Matrix1d(_range.P_var)),
+    obs_sat_(_obs_sat),
+    sat_(_sat),
+    range_(_range)
+{
+    //
+}
+
+inline const obsd_t& FeatureGnssSatellite::getObservation() const
+{
+    return obs_sat_;
+}
+
+inline int FeatureGnssSatellite::satNumber() const
+{
+    return obs_sat_.sat;
+}
+
+inline const GnssUtils::Satellite& FeatureGnssSatellite::getSatellite() const
+{
+    return sat_;
+}
+
+inline const GnssUtils::Range& FeatureGnssSatellite::getRange() const
+{
+    return range_;
+}
+
+} // namespace wolf
+
+#endif
diff --git a/include/gnss/feature/feature_gnss_single_diff.h b/include/gnss/feature/feature_gnss_single_diff.h
deleted file mode 100644
index 903a062ce..000000000
--- a/include/gnss/feature/feature_gnss_single_diff.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef FEATURE_GNSS_SINGLE_DIFF_H_
-#define FEATURE_GNSS_SINGLE_DIFF_H_
-
-//Wolf includes
-#include "core/feature/feature_base.h"
-
-//std includes
-
-namespace wolf {
-
-WOLF_PTR_TYPEDEFS(FeatureGnssSinleDiff);
-    
-//class FeatureGnssSingleDiff
-class FeatureGnssSingleDiff : public FeatureBase
-{
-    public:
-
-        /** \brief Constructor from capture pointer and measure
-         *
-         * \param _measurement the measurement
-         * \param _meas_covariance the noise of the measurement
-         *
-         */
-        FeatureGnssSingleDiff(const Eigen::Vector3s& _measurement, const Eigen::Matrix3s& _meas_covariance) :
-            FeatureBase("GNSS SINGLE DIFFERENCE", _measurement, _meas_covariance)
-        {};
-
-        virtual ~FeatureGnssSingleDiff(){};
-};
-
-} // namespace wolf
-
-#endif
diff --git a/include/gnss/feature/feature_gnss_tdcp.h b/include/gnss/feature/feature_gnss_tdcp.h
new file mode 100644
index 000000000..1af89dda3
--- /dev/null
+++ b/include/gnss/feature/feature_gnss_tdcp.h
@@ -0,0 +1,56 @@
+//--------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--------
+#ifndef FEATURE_GNSS_TDCP_H_
+#define FEATURE_GNSS_TDCP_H_
+
+//Wolf includes
+#include "core/feature/feature_base.h"
+
+//std includes
+
+namespace wolf {
+
+WOLF_PTR_TYPEDEFS(FeatureGnssSinleDiff);
+    
+class FeatureGnssTdcp : public FeatureBase
+{
+    public:
+
+        /** \brief Constructor from capture pointer and measure
+         *
+         * \param _measurement the measurement
+         * \param _meas_covariance the noise of the measurement
+         *
+         */
+        FeatureGnssTdcp(const Eigen::Vector4d& _measurement, const Eigen::Matrix4d& _meas_covariance) :
+            FeatureBase("FeatureGnssTdcp", _measurement, _meas_covariance)
+        {};
+        FeatureGnssTdcp(const Eigen::Vector3d& _measurement, const Eigen::Matrix3d& _meas_covariance) :
+            FeatureBase("FeatureGnssTdcp", _measurement, _meas_covariance)
+        {};
+
+        ~FeatureGnssTdcp() override{};
+};
+
+} // namespace wolf
+
+#endif
diff --git a/include/gnss/processor/processor_gnss_fix.h b/include/gnss/processor/processor_gnss_fix.h
index ebeeefab5..1fa78ddd3 100644
--- a/include/gnss/processor/processor_gnss_fix.h
+++ b/include/gnss/processor/processor_gnss_fix.h
@@ -1,39 +1,122 @@
+//--------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--------
 #ifndef WOLF_PROCESSOR_GNSS_FIX_H
 #define WOLF_PROCESSOR_GNSS_FIX_H
 
 // Wolf includes
 #include "core/processor/processor_base.h"
-#include "gnss/capture/capture_gnss_fix.h"
+#include "gnss/capture/capture_gnss.h"
 #include "gnss/sensor/sensor_gnss.h"
 
 // Std includes
+#include "gnss_utils/gnss_utils.h"
+#include "gnss_utils/utils/rcv_position.h"
 
 
 namespace wolf {
-    
+
 WOLF_PTR_TYPEDEFS(ProcessorGnssFix);
-WOLF_STRUCT_PTR_TYPEDEFS(ProcessorParamsGnssFix);
-    
+WOLF_STRUCT_PTR_TYPEDEFS(ParamsProcessorGnssFix);
 
-struct ProcessorParamsGnssFix : public ProcessorParamsBase
+struct ParamsProcessorGnssFix : public ParamsProcessorBase
 {
-    Scalar time_th;
-    Scalar dist_traveled;
-    Scalar enu_map_init_dist_min;
-    ProcessorParamsGnssFix() = default;
-    ProcessorParamsGnssFix(std::string _unique_name, const ParamsServer& _server):
-        ProcessorParamsBase(_unique_name, _server)
+    bool fix_from_raw, init_enu_map, remove_outliers;
+    GnssUtils::Options compute_pos_opt;
+    double max_time_span;
+    double dist_traveled;
+    double enu_map_init_dist_min, enu_map_init_dist_max;
+    double enu_map_fix_dist;
+    double outlier_residual_th;
+
+    ParamsProcessorGnssFix() = default;
+    ParamsProcessorGnssFix(std::string _unique_name, const ParamsServer& _server):
+        ParamsProcessorBase(_unique_name, _server)
     {
-        time_th                 = _server.getParam<Scalar>(_unique_name + "/time_th");
-        dist_traveled           = _server.getParam<Scalar>(_unique_name + "/dist_traveled");
-        enu_map_init_dist_min   = _server.getParam<Scalar>(_unique_name + "/enu_map_init_dist_min");
+        max_time_span               = _server.getParam<double>  (prefix + _unique_name + "/keyframe_vote/max_time_span");
+        dist_traveled               = _server.getParam<double>  (prefix + _unique_name + "/keyframe_vote/dist_traveled");
+        init_enu_map                = _server.getParam<bool>    (prefix + _unique_name + "/init_enu_map");
+        if (init_enu_map)
+        {
+            enu_map_init_dist_min   = _server.getParam<double>  (prefix + _unique_name + "/enu_map_init_dist_min");
+            enu_map_init_dist_max   = _server.getParam<double>  (prefix + _unique_name + "/enu_map_init_dist_max");
+        }
+        enu_map_fix_dist            = _server.getParam<double>  (prefix + _unique_name + "/enu_map_fix_dist");
+        fix_from_raw                = _server.getParam<bool>    (prefix + _unique_name + "/fix_from_raw");
+        remove_outliers             = _server.getParam<bool>    (prefix + _unique_name + "/remove_outliers");
+        outlier_residual_th         = _server.getParam<double>  (prefix + _unique_name + "/outlier_residual_th");
+
+        // COMPUTE POS PARAMS (only if compute fix from yaw)
+        if (fix_from_raw)
+        {
+            // GNSS OPTIONS (see gnss_utils.h)
+            compute_pos_opt.sateph     =        _server.getParam<int>   (prefix + _unique_name + "/gnss/sateph");  // satellite ephemeris/clock (0:broadcast ephemeris,1:precise ephemeris,2:broadcast + SBAS,3:ephemeris option: broadcast + SSR_APC,4:broadcast + SSR_COM,5: QZSS LEX ephemeris
+            compute_pos_opt.ionoopt    =        _server.getParam<int>   (prefix + _unique_name + "/gnss/ionoopt"); // ionosphere option (0:correction off,1:broadcast mode,2:SBAS model,3:L1/L2 or L1/L5,4:estimation,5:IONEX TEC model,6:QZSS broadcast,7:QZSS LEX ionosphere,8:SLANT TEC mode)
+            compute_pos_opt.tropopt    =        _server.getParam<int>   (prefix + _unique_name + "/gnss/tropopt"); // troposphere option: (0:correction off,1:Saastamoinen model,2:SBAS model,3:troposphere option: ZTD estimation,4:ZTD+grad estimation,5:ZTD correction,6:ZTD+grad correction)
+            compute_pos_opt.sbascorr   =        _server.getParam<int>   (prefix + _unique_name + "/gnss/sbascorr");// SBAS option (1:long term correction,2:fast correction,4:ionosphere correction,8:ranging)
+            compute_pos_opt.raim       =        _server.getParam<bool>  (prefix + _unique_name + "/gnss/raim");    // RAIM enabled
+            compute_pos_opt.elmin      =        _server.getParam<double>(prefix + _unique_name + "/gnss/elmin");   // min elevation (rad)
+            compute_pos_opt.maxgdop    =        _server.getParam<double>(prefix + _unique_name + "/gnss/maxgdop"); // maxgdop: reject threshold of gdop
+
+            compute_pos_opt.GPS    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/GPS");   /* navigation system */
+            compute_pos_opt.SBS    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/SBS");
+            compute_pos_opt.GLO    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/GLO");
+            compute_pos_opt.GAL    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/GAL");
+            compute_pos_opt.QZS    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/QZS");
+            compute_pos_opt.CMP    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/CMP");
+            compute_pos_opt.IRN    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/IRN");
+            compute_pos_opt.LEO    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/LEO");
+        }
     }
-    std::string print() const
+
+    std::string print() const override
     {
-        return "\n" + ProcessorParamsBase::print()                              + "\n"
-            + "time_th: "               + std::to_string(time_th)               + "\n"
-            + "dist_traveled: "         + std::to_string(dist_traveled)         + "\n"
-            + "enu_map_init_dist_min: " + std::to_string(enu_map_init_dist_min) + "\n";
+        return "\n"                             + ParamsProcessorBase::print()              + "\n" +
+               "max_time_span: "                + std::to_string(max_time_span)             + "\n" +
+               "dist_traveled: "                + std::to_string(dist_traveled)             + "\n" +
+               "fix_from_raw: "                 + std::to_string(fix_from_raw)              + "\n" +
+               "init_enu_map: "                 + std::to_string(init_enu_map)              + "\n" +
+               (init_enu_map ?
+                       "enu_map_init_dist_min: "+ std::to_string(enu_map_init_dist_min)     + "\n" : "") +
+               "enu_map_fix_dist: "             + std::to_string(enu_map_fix_dist)          + "\n" +
+               "remove_outliers: "              + std::to_string(remove_outliers)           + "\n" +
+               "outlier_residual_th: "          + std::to_string(outlier_residual_th)       + "\n" +
+               "keyframe_vote/max_time_span: "  + std::to_string(max_time_span)             + "\n" +
+               (fix_from_raw ?
+                       "gnss/sateph: "                   + std::to_string(compute_pos_opt.sateph)    + "\n" +
+                       "gnss/ionoopt: "                  + std::to_string(compute_pos_opt.ionoopt)   + "\n" +
+                       "gnss/tropopt: "                  + std::to_string(compute_pos_opt.tropopt)   + "\n" +
+                       "gnss/sbascorr: "                 + std::to_string(compute_pos_opt.sbascorr)  + "\n" +
+                       "gnss/raim: "                     + std::to_string(compute_pos_opt.raim)      + "\n" +
+                       "gnss/elmin: "                    + std::to_string(compute_pos_opt.elmin*R2D) + "\n" +
+                       "gnss/maxgdop: "                  + std::to_string(compute_pos_opt.maxgdop)   + "\n" +
+                       "gnss/constellations/GPS: "       + std::to_string(compute_pos_opt.GPS)       + "\n" +
+                       "gnss/constellations/SBS: "       + std::to_string(compute_pos_opt.SBS)       + "\n" +
+                       "gnss/constellations/GLO: "       + std::to_string(compute_pos_opt.GLO)       + "\n" +
+                       "gnss/constellations/GAL: "       + std::to_string(compute_pos_opt.GAL)       + "\n" +
+                       "gnss/constellations/QZS: "       + std::to_string(compute_pos_opt.QZS)       + "\n" +
+                       "gnss/constellations/CMP: "       + std::to_string(compute_pos_opt.CMP)       + "\n" +
+                       "gnss/constellations/IRN: "       + std::to_string(compute_pos_opt.IRN)       + "\n" +
+                       "gnss/constellations/LEO: "       + std::to_string(compute_pos_opt.LEO)       + "\n" :
+                       "");
     }
 };
 
@@ -42,14 +125,21 @@ class ProcessorGnssFix : public ProcessorBase
 {
     protected:
         SensorGnssPtr sensor_gnss_;
-        ProcessorParamsGnssFixPtr params_gnss_;
-        CaptureGnssFixPtr first_capture_, incoming_capture_;
+        ParamsProcessorGnssFixPtr params_gnss_;
+        CaptureBasePtr last_KF_capture_, incoming_capture_;
+        FeatureGnssFixPtr last_KF_feature_;
         FrameBasePtr last_KF_;
+        GnssUtils::ComputePosOutput incoming_pos_out_;
+        Eigen::Vector3d first_pos_;
+        VectorComposite first_frame_state_;
+
 
     public:
-        ProcessorGnssFix(ProcessorParamsGnssFixPtr _params);
-        virtual ~ProcessorGnssFix();
-        virtual void configure(SensorBasePtr _sensor) override;
+        ProcessorGnssFix(ParamsProcessorGnssFixPtr _params);
+        ~ProcessorGnssFix() override;
+        void configure(SensorBasePtr _sensor) override;
+
+        WOLF_PROCESSOR_CREATE(ProcessorGnssFix, ParamsProcessorGnssFix);
 
     protected:
         /** \brief process an incoming capture
@@ -57,46 +147,43 @@ class ProcessorGnssFix : public ProcessorBase
          * Each derived processor should implement this function. It will be called if:
          *  - A new capture arrived and triggerInCapture() returned true.
          */
-        virtual void processCapture(CaptureBasePtr) override;
+        void processCapture(CaptureBasePtr) override;
 
         /** \brief process an incoming key-frame
          *
          * The ProcessorTracker only processes incoming captures (it is not called).
          */
-        virtual void processKeyFrame(FrameBasePtr _keyframe_ptr, const Scalar& _time_tolerance) override {};
+        void processKeyFrame(FrameBasePtr _keyframe_ptr) override {};
 
         /** \brief trigger in capture
          *
          * Returns true if processCapture() should be called after the provided capture arrived.
          */
-        virtual bool triggerInCapture(CaptureBasePtr) const override;
+        bool triggerInCapture(CaptureBasePtr) const override;
 
         /** \brief trigger in key-frame
          *
          * The ProcessorTracker only processes incoming captures, then it returns false.
          */
-        virtual bool triggerInKeyFrame(FrameBasePtr _keyframe_ptr, const Scalar& _time_tolerance) const override {return false;}
+        bool triggerInKeyFrame(FrameBasePtr _keyframe_ptr) const override {return false;}
 
         /** \brief store key frame
         *
         * Returns true if the key frame should be stored
         */
-        virtual bool storeKeyFrame(FrameBasePtr);
+        bool storeKeyFrame(FrameBasePtr) override;
 
         /** \brief store capture
         *
         * Returns true if the capture should be stored
         */
-        virtual bool storeCapture(CaptureBasePtr);
+        bool storeCapture(CaptureBasePtr) override;
 
-        virtual bool voteForKeyFrame() const override;
+        bool voteForKeyFrame() const override;
 
     private:
         FactorBasePtr emplaceFactor(FeatureBasePtr _ftr_ptr);
-        bool rejectOutlier(FactorBasePtr ctr_ptr);
-
-    public:
-        static ProcessorBasePtr create(const std::string& _unique_name, const ProcessorParamsBasePtr _params);
+        bool detectOutlier(FactorBasePtr ctr_ptr);
 
 };
 
diff --git a/include/gnss/processor/processor_gnss_single_diff.h b/include/gnss/processor/processor_gnss_single_diff.h
deleted file mode 100644
index 12f7943c6..000000000
--- a/include/gnss/processor/processor_gnss_single_diff.h
+++ /dev/null
@@ -1,116 +0,0 @@
-#ifndef WOLF_PROCESSOR_GNSS_SINGLE_DIFF_H
-#define WOLF_PROCESSOR_GNSS_SINGLE_DIFF_H
-
-// Wolf includes
-#include "core/processor/processor_base.h"
-#include "gnss/capture/capture_gnss_single_diff.h"
-#include "gnss/sensor/sensor_gnss.h"
-
-// Std includes
-
-
-namespace wolf {
-    
-WOLF_PTR_TYPEDEFS(ProcessorGnssSingleDiff);
-WOLF_STRUCT_PTR_TYPEDEFS(ProcessorParamsGnssSingleDiff);
-
-
-struct ProcessorParamsGnssSingleDiff : public ProcessorParamsBase
-{
-    Scalar time_th;
-    Scalar dist_traveled;
-    Scalar enu_map_init_dist_min;
-    ProcessorParamsGnssSingleDiff() = default;
-    ProcessorParamsGnssSingleDiff(std::string _unique_name, const ParamsServer& _server):
-        ProcessorParamsBase(_unique_name, _server)
-    {
-        time_th                 = _server.getParam<Scalar>(_unique_name + "/time_th");
-        dist_traveled           = _server.getParam<Scalar>(_unique_name + "/dist_traveled");
-        enu_map_init_dist_min   = _server.getParam<Scalar>(_unique_name + "/enu_map_init_dist_min");
-    }
-    std::string print() const
-    {
-        return "\n" + ProcessorParamsBase::print()                              + "\n"
-            + "time_th: "               + std::to_string(time_th)               + "\n"
-            + "dist_traveled: "         + std::to_string(dist_traveled)         + "\n"
-            + "enu_map_init_dist_min: " + std::to_string(enu_map_init_dist_min) + "\n";
-    }
-};
-    
-//class
-class ProcessorGnssSingleDiff : public ProcessorBase
-{
-    protected:
-        SensorGnssPtr sensor_gnss_;
-        ProcessorParamsGnssSingleDiffPtr params_gnss_;
-        CaptureGnssSingleDiffPtr incoming_capture_;
-        FrameBasePtr last_KF_;
-
-    public:
-        ProcessorGnssSingleDiff(ProcessorParamsGnssSingleDiffPtr _params_gnss);
-        virtual ~ProcessorGnssSingleDiff();
-        virtual void configure(SensorBasePtr _sensor) override;
-        FrameBasePtr getLastKF();
-
-    protected:
-        /** \brief process an incoming capture
-         *
-         * Each derived processor should implement this function. It will be called if:
-         *  - A new capture arrived and triggerInCapture() returned true.
-         */
-        virtual void processCapture(CaptureBasePtr) override;
-
-        /** \brief process an incoming key-frame
-         *
-         * The ProcessorTracker only processes incoming captures (it is not called).
-         */
-        virtual void processKeyFrame(FrameBasePtr _keyframe, const Scalar& _time_tolerance) override {};
-
-        /** \brief store key frame
-        *
-        * Returns true if the key frame should be stored
-        */
-        virtual bool storeKeyFrame(FrameBasePtr) override;
-
-        /** \brief store capture
-        *
-        * Returns true if the capture should be stored
-        */
-        virtual bool storeCapture(CaptureBasePtr) override;
-
-        /** \brief trigger in capture
-         *
-         * Returns true if processCapture() should be called after the provided capture arrived.
-         */
-        virtual bool triggerInCapture(CaptureBasePtr) const override;
-
-        /** \brief trigger in key-frame
-         *
-         * The ProcessorTracker only processes incoming captures, then it returns false.
-         */
-        virtual bool triggerInKeyFrame(FrameBasePtr _keyframe, const Scalar& _time_tolerance) const override {return false;}
-
-        virtual bool voteForKeyFrame() const override;
-
-
-    private:
-        FactorBasePtr emplaceFactor(FeatureBasePtr _ftr);
-
-    public:
-        static ProcessorBasePtr create(const std::string& _unique_name, const ProcessorParamsBasePtr _params);
-
-};
-
-inline bool ProcessorGnssSingleDiff::triggerInCapture(CaptureBasePtr) const
-{
-    return true;
-}
-
-inline wolf::FrameBasePtr ProcessorGnssSingleDiff::getLastKF()
-{
-    return last_KF_;
-}
-
-} // namespace wolf
-
-#endif //WOLF_PROCESSOR_GNSS_SINGLE_DIFF_H
diff --git a/include/gnss/processor/processor_gnss_tdcp.h b/include/gnss/processor/processor_gnss_tdcp.h
new file mode 100644
index 000000000..0f1bbf1d0
--- /dev/null
+++ b/include/gnss/processor/processor_gnss_tdcp.h
@@ -0,0 +1,154 @@
+//--------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--------
+#ifndef WOLF_PROCESSOR_GNSS_TDCP_H
+#define WOLF_PROCESSOR_GNSS_TDCP_H
+
+// Wolf includes
+#include "core/common/wolf.h"
+#include "core/processor/processor_base.h"
+
+#include "gnss/capture/capture_gnss.h"
+#include "gnss/sensor/sensor_gnss.h"
+#include "gnss/processor/processor_gnss_fix.h"
+
+// GNSS Utils includes
+#include "gnss_utils/tdcp.h"
+
+// Std includes
+
+
+namespace wolf {
+    
+WOLF_PTR_TYPEDEFS(ProcessorGnssTdcp);
+WOLF_STRUCT_PTR_TYPEDEFS(ParamsProcessorGnssTdcp);
+
+
+struct ParamsProcessorGnssTdcp : public ParamsProcessorGnssFix
+{
+    struct GnssUtils::TdcpOptions tdcp;
+
+    ParamsProcessorGnssTdcp() = default;
+    ParamsProcessorGnssTdcp(std::string _unique_name, const ParamsServer& _server):
+        ParamsProcessorGnssFix(_unique_name, _server)
+    {
+        tdcp.min_common_sats    = _server.getParam<double>(prefix + _unique_name + "/tdcp/min_common_sats");
+        tdcp.raim_n             = _server.getParam<double>(prefix + _unique_name + "/tdcp/raim_n");
+        tdcp.max_residual_ci    = _server.getParam<double>(prefix + _unique_name + "/tdcp/max_residual_ci");
+        tdcp.residual_opt       = _server.getParam<int>   (prefix + _unique_name + "/tdcp/residual_opt");
+        tdcp.sigma_atm     = _server.getParam<double>(prefix + _unique_name + "/tdcp/sigma_atm");
+        tdcp.sigma_carrier = _server.getParam<double>(prefix + _unique_name + "/tdcp/sigma_carrier");
+        tdcp.time_window   = _server.getParam<double>(prefix + _unique_name + "/tdcp/time_window");
+
+        // hardcoded params
+        compute_pos_opt.carrier_opt.corr_tropo = false;
+        compute_pos_opt.carrier_opt.corr_iono = false;
+        compute_pos_opt.carrier_opt.corr_clock = false;
+        tdcp.relinearize_jacobian = false;
+        tdcp.max_iterations = 1;
+        tdcp.use_old_nav = false;
+        tdcp.multi_freq = false;
+    }
+    std::string print() const override
+    {
+        return "\n" + ParamsProcessorBase::print()                               + "\n"
+            + "tdcp/min_common_sats: "  + std::to_string(tdcp.min_common_sats)   + "\n"
+            + "tdcp/raim_n: "           + std::to_string(tdcp.raim_n)            + "\n"
+            + "tdcp/max_residual_ci: "  + std::to_string(tdcp.max_residual_ci)   + "\n"
+            + "tdcp/residual_opt: "     + std::to_string(tdcp.residual_opt)      + "\n"
+            + "tdcp/sigma_atm: "        + std::to_string(tdcp.sigma_atm)         + "\n"
+            + "tdcp/sigma_carrier: "    + std::to_string(tdcp.sigma_carrier)     + "\n"
+            + "tdcp/time_window: "      + std::to_string(tdcp.time_window)       + "\n";
+    }
+};
+    
+//class
+class ProcessorGnssTdcp : public ProcessorBase
+{
+    protected:
+        SensorGnssPtr sensor_gnss_;
+        ParamsProcessorGnssTdcpPtr params_tdcp_;
+        CaptureGnssPtr incoming_capture_;
+        FrameBasePtr last_KF_;
+
+    public:
+        ProcessorGnssTdcp(ParamsProcessorGnssTdcpPtr _params_gnss);
+        ~ProcessorGnssTdcp() override;
+        void configure(SensorBasePtr _sensor) override;
+
+        WOLF_PROCESSOR_CREATE(ProcessorGnssTdcp, ParamsProcessorGnssTdcp);
+
+        FrameBasePtr getLastKF();
+
+    protected:
+        /** \brief process an incoming capture
+         *
+         * Each derived processor should implement this function. It will be called if:
+         *  - A new capture arrived and triggerInCapture() returned true.
+         */
+        void processCapture(CaptureBasePtr) override;
+
+        /** \brief process an incoming key-frame
+         *
+         * The ProcessorTracker only processes incoming captures (it is not called).
+         */
+        void processKeyFrame(FrameBasePtr _keyframe) override;
+
+        /** \brief store key frame
+        *
+        * Returns true if the key frame should be stored
+        */
+        bool storeKeyFrame(FrameBasePtr) override {return false;};
+
+        /** \brief store capture
+        *
+        * Returns true if the capture should be stored
+        */
+        bool storeCapture(CaptureBasePtr) override {return false;};
+
+        /** \brief trigger in capture
+         *
+         * Returns true if processCapture() should be called after the provided capture arrived.
+         */
+        bool triggerInCapture(CaptureBasePtr) const override {return true;};
+
+        /** \brief trigger in key-frame
+         *
+         * The ProcessorTracker only processes incoming captures, then it returns false.
+         */
+        bool triggerInKeyFrame(FrameBasePtr _keyframe) const override {return true;};
+
+        bool voteForKeyFrame() const override;
+
+
+    private:
+        FactorBasePtr emplaceFactor(FeatureBasePtr _ftr, FrameBasePtr _frm_ref);
+
+};
+
+inline wolf::FrameBasePtr ProcessorGnssTdcp::getLastKF()
+{
+    return last_KF_;
+}
+
+} // namespace wolf
+
+#endif //WOLF_PROCESSOR_GNSS_TDCP_H
diff --git a/include/gnss/processor/processor_tracker_gnss.h b/include/gnss/processor/processor_tracker_gnss.h
new file mode 100644
index 000000000..319ce9deb
--- /dev/null
+++ b/include/gnss/processor/processor_tracker_gnss.h
@@ -0,0 +1,322 @@
+//--------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--------
+#ifndef PROCESSOR_TRACKER_GNSS_H_
+#define PROCESSOR_TRACKER_GNSS_H_
+
+#include "gnss/internal/config.h"
+#include "core/common/wolf.h"
+#include "core/processor/processor_tracker_feature.h"
+#include "gnss/sensor/sensor_gnss.h"
+#include "gnss/feature/feature_gnss_satellite.h"
+#include "gnss_utils/gnss_utils.h"
+#include "gnss_utils/tdcp.h"
+
+namespace wolf
+{
+    
+WOLF_STRUCT_PTR_TYPEDEFS(ParamsProcessorTrackerGnss);
+
+struct ParamsProcessorTrackerGnss : public ParamsProcessorTrackerFeature
+{
+    GnssUtils::Options gnss_opt;
+    GnssUtils::Options fix_opt{GnssUtils::default_options};
+    GnssUtils::TdcpOptions tdcp_params;
+    double max_time_span;
+    bool remove_outliers, remove_outliers_tdcp, remove_outliers_with_fix;
+    double outlier_residual_th;
+    bool init_frames, pseudo_ranges, fix;
+    double enu_map_fix_dist;
+    int min_sbas_sats;
+    bool tdcp_enabled;
+    std::string tdcp_structure;
+
+    ParamsProcessorTrackerGnss() = default;
+    ParamsProcessorTrackerGnss(std::string _unique_name, const ParamsServer& _server):
+        ParamsProcessorTrackerFeature(_unique_name, _server)
+    {
+        remove_outliers             = _server.getParam<bool>    (prefix + _unique_name + "/remove_outliers");
+        remove_outliers_with_fix    = _server.getParam<bool>    (prefix + _unique_name + "/remove_outliers_with_fix");
+        outlier_residual_th         = _server.getParam<double>  (prefix + _unique_name + "/outlier_residual_th");
+        init_frames                 = _server.getParam<bool>    (prefix + _unique_name + "/init_frames");
+        max_time_span               = _server.getParam<double>  (prefix + _unique_name + "/keyframe_vote/max_time_span");
+        enu_map_fix_dist            = _server.getParam<double>  (prefix + _unique_name + "/enu_map_fix_dist");
+        fix                         = _server.getParam<bool>    (prefix + _unique_name + "/fix");
+        pseudo_ranges               = _server.getParam<bool>    (prefix + _unique_name + "/pseudo_ranges");
+        min_sbas_sats               = _server.getParam<int>     (prefix + _unique_name + "/gnss/min_sbas_sats");
+
+        // GNSS OPTIONS (see rtklib.h)
+        gnss_opt.sateph     =        _server.getParam<int>   (prefix + _unique_name + "/gnss/sateph");  // satellite ephemeris option: EPHOPT_BRDC(0):broadcast ephemeris, EPHOPT_PREC(1): precise ephemeris, EPHOPT_SBAS(2): broadcast + SBAS, EPHOPT_SSRAPC(3): broadcast + SSR_APC, EPHOPT_SSRCOM(4): broadcast + SSR_COM, EPHOPT_LEX(5): QZSS LEX ephemeris, EPHOPT_SBAS2(6):broadcast + SBAS(sats with SBAS corr and sats with BRDC eph), EPHOPT_SBAS3(7):broadcast + SBAS(EPHOPT_SBAS if possible, otherwise EPHOPT_SBAS2), EPHOPT_SBAS4(8):broadcast + SBAS(EPHOPT_SBAS if possible, otherwise EPHOPT_BRDC)
+        gnss_opt.ionoopt    =        _server.getParam<int>   (prefix + _unique_name + "/gnss/ionoopt"); // ionosphere option: IONOOPT_OFF(0):correction off, IONOOPT_BRDC(1):broadcast mode, IONOOPT_SBAS(2):SBAS model, IONOOPT_IFLC(3):L1/L2 or L1/L5, IONOOPT_EST(4):estimation, IONOOPT_TEC(5):IONEX TEC model, IONOOPT_QZS(6):QZSS broadcast, IONOOPT_LEX(7):QZSS LEX ionosphere, IONOOPT_STEC(8):SLANT TEC mode, IONOOPT_SBAS2(9):broadcast + SBAS(sats with SBAS corr and sats with BRDC eph), IONOOPT_SBAS3(10):broadcast + SBAS(IONOOPT_SBAS if possible, otherwise IONOOPT_SBAS2), IONOOPT_SBAS4(8):broadcast + SBAS(IONOOPT_SBAS if possible, otherwise IONOOPT_BRDC)
+        gnss_opt.tropopt    =        _server.getParam<int>   (prefix + _unique_name + "/gnss/tropopt"); // troposphere option: (0:correction off,1:Saastamoinen model,2:SBAS model,3:troposphere option: ZTD estimation,4:ZTD+grad estimation,5:ZTD correction,6:ZTD+grad correction)
+        gnss_opt.sbascorr   =        _server.getParam<int>   (prefix + _unique_name + "/gnss/sbascorr");// SBAS option (1:long term correction,2:fast correction,4:ionosphere correction,8:ranging)
+        gnss_opt.raim       =        _server.getParam<int>   (prefix + _unique_name + "/gnss/raim");    // RAIM enabled
+        gnss_opt.elmin      =        _server.getParam<double>(prefix + _unique_name + "/gnss/elmin");   // min elevation (rad)
+        gnss_opt.maxgdop    =        _server.getParam<double>(prefix + _unique_name + "/gnss/maxgdop"); // maxgdop: reject threshold of gdop
+
+        gnss_opt.GPS    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/GPS");   /* navigation system */
+        gnss_opt.SBS    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/SBS");
+        gnss_opt.GLO    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/GLO");
+        gnss_opt.GAL    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/GAL");
+        gnss_opt.QZS    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/QZS");
+        gnss_opt.CMP    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/CMP");
+        gnss_opt.IRN    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/IRN");
+        gnss_opt.LEO    = _server.getParam<bool>(prefix + _unique_name + "/gnss/constellations/LEO");
+
+        // TDCP
+        tdcp_enabled = _server.getParam<bool>(prefix + _unique_name + "/gnss/tdcp/enabled");
+        if (tdcp_enabled)
+        {
+            tdcp_structure                      = _server.getParam<std::string>(prefix + _unique_name + "/gnss/tdcp/structure");
+            remove_outliers_tdcp                = _server.getParam<bool>  (prefix + _unique_name + "/gnss/tdcp/remove_outliers");
+            tdcp_params.batch                   = _server.getParam<bool>  (prefix + _unique_name + "/gnss/tdcp/batch");
+            gnss_opt.carrier_opt.corr_iono      = _server.getParam<bool>  (prefix + _unique_name + "/gnss/tdcp/corr_iono");
+            gnss_opt.carrier_opt.corr_tropo     = _server.getParam<bool>  (prefix + _unique_name + "/gnss/tdcp/corr_tropo");
+            gnss_opt.carrier_opt.corr_clock     = _server.getParam<bool>  (prefix + _unique_name + "/gnss/tdcp/corr_clock");
+            tdcp_params.loss_function           = _server.getParam<bool>  (prefix + _unique_name + "/gnss/tdcp/loss_function");
+            tdcp_params.time_window             = _server.getParam<double>(prefix + _unique_name + "/gnss/tdcp/time_window");
+            tdcp_params.sigma_atm               = _server.getParam<double>(prefix + _unique_name + "/gnss/tdcp/sigma_atm");
+            tdcp_params.sigma_carrier           = _server.getParam<double>(prefix + _unique_name + "/gnss/tdcp/sigma_carrier");
+            tdcp_params.multi_freq              = _server.getParam<bool>  (prefix + _unique_name + "/gnss/tdcp/multi_freq");
+            if (tdcp_params.batch)
+            {
+                tdcp_params.min_common_sats         = _server.getParam<int>       (prefix + _unique_name + "/gnss/tdcp/min_common_sats");
+                tdcp_params.raim_n                  = _server.getParam<int>       (prefix + _unique_name + "/gnss/tdcp/raim_n");
+                tdcp_params.max_residual_ci         = _server.getParam<double>    (prefix + _unique_name + "/gnss/tdcp/max_residual_ci");
+                tdcp_params.relinearize_jacobian    = _server.getParam<bool>      (prefix + _unique_name + "/gnss/tdcp/relinearize_jacobian");
+                tdcp_params.max_iterations          = _server.getParam<int>       (prefix + _unique_name + "/gnss/tdcp/max_iterations");
+                tdcp_params.residual_opt            = _server.getParam<int>       (prefix + _unique_name + "/gnss/tdcp/residual_opt");
+            }
+
+            if (tdcp_structure != "all-all" and tdcp_structure != "consecutive" and tdcp_structure != "first-all")
+                throw std::runtime_error("unknown value for '/gnss/tdcp/structure', should be 'all-all', 'consecutive' or 'first-all'");
+        }
+
+        // COMPUTE FIX OPTIONS (RAIM)
+        fix_opt.raim = gnss_opt.raim;
+        fix_opt.sateph = 6; //EPHOPT_SBAS2;
+        fix_opt.ionoopt = 9;//IONOPT_SBAS2;
+        fix_opt.tropopt = 2;
+        fix_opt.sbascorr = 15;
+        fix_opt.elmin = gnss_opt.elmin;
+        fix_opt.maxgdop = gnss_opt.maxgdop;
+        // same constellations
+        fix_opt.GPS = gnss_opt.GPS;
+        fix_opt.SBS = gnss_opt.SBS;
+        fix_opt.GLO = gnss_opt.GLO;
+        fix_opt.GAL = gnss_opt.GAL;
+        fix_opt.QZS = gnss_opt.QZS;
+        fix_opt.CMP = gnss_opt.CMP;
+        fix_opt.IRN = gnss_opt.IRN;
+        fix_opt.LEO = gnss_opt.LEO;
+    }
+
+    std::string print() const override
+    {
+        return "\n"                             + ParamsProcessorBase::print()                  + "\n"
+            + "remove_outliers: "               + std::to_string(remove_outliers)               + "\n"
+            + "outlier_residual_th: "           + std::to_string(outlier_residual_th)           + "\n"
+            + "init_frames: "                   + std::to_string(init_frames)                   + "\n"
+            + "enu_map_fix_dist: "              + std::to_string(enu_map_fix_dist)              + "\n"
+            + "keyframe_vote/max_time_span: "   + std::to_string(max_time_span)                 + "\n"
+            + "gnss/sateph: "                   + std::to_string(gnss_opt.sateph)               + "\n"
+            + "gnss/ionoopt: "                  + std::to_string(gnss_opt.ionoopt)              + "\n"
+            + "gnss/tropopt: "                  + std::to_string(gnss_opt.tropopt)              + "\n"
+            + "gnss/sbascorr: "                 + std::to_string(gnss_opt.sbascorr)             + "\n"
+            + "gnss/raim: "                     + std::to_string(gnss_opt.raim)                 + "\n"
+            + "gnss/elmin: "                    + std::to_string(gnss_opt.elmin*R2D)            + "\n"
+            + "gnss/maxgdop: "                  + std::to_string(gnss_opt.maxgdop)              + "\n"
+            + "gnss/constellations/GPS: "       + std::to_string(gnss_opt.GPS)                  + "\n"
+            + "gnss/constellations/SBS: "       + std::to_string(gnss_opt.SBS)                  + "\n"
+            + "gnss/constellations/GLO: "       + std::to_string(gnss_opt.GLO)                  + "\n"
+            + "gnss/constellations/GAL: "       + std::to_string(gnss_opt.GAL)                  + "\n"
+            + "gnss/constellations/QZS: "       + std::to_string(gnss_opt.QZS)                  + "\n"
+            + "gnss/constellations/CMP: "       + std::to_string(gnss_opt.CMP)                  + "\n"
+            + "gnss/constellations/IRN: "       + std::to_string(gnss_opt.IRN)                  + "\n"
+            + "gnss/constellations/LEO: "       + std::to_string(gnss_opt.LEO)                  + "\n"
+            + "gnss/tdcp/enabled: "             + std::to_string(tdcp_enabled)                  + "\n"
+            + "gnss/tdcp/structure: "           + tdcp_structure                                + "\n"
+            + "gnss/tdcp/batch: "               + std::to_string(tdcp_params.batch)             + "\n"
+            + "gnss/tdcp/corr_iono: "           + std::to_string(gnss_opt.carrier_opt.corr_iono)+ "\n"
+            + "gnss/tdcp/corr_tropo: "          + std::to_string(gnss_opt.carrier_opt.corr_tropo)+ "\n"
+            + "gnss/tdcp/corr_clock: "          + std::to_string(gnss_opt.carrier_opt.corr_clock)+ "\n"
+            + "gnss/tdcp/loss_function: "       + std::to_string(tdcp_params.loss_function)     + "\n"
+            + "gnss/tdcp/time_window: "         + std::to_string(tdcp_params.time_window)       + "\n"
+            + "gnss/tdcp/sigma_atm: "           + std::to_string(tdcp_params.sigma_atm)         + "\n"
+            + "gnss/tdcp/sigma_carrier: "       + std::to_string(tdcp_params.sigma_carrier)     + "\n"
+            + "gnss/tdcp/multi_freq: "          + std::to_string(tdcp_params.multi_freq)        + "\n";
+    }
+};
+
+
+WOLF_PTR_TYPEDEFS(ProcessorTrackerGnss);
+    
+//Class
+class ProcessorTrackerGnss : public ProcessorTrackerFeature
+{
+
+    public:
+        ProcessorTrackerGnss(ParamsProcessorTrackerGnssPtr _params_tracker_gnss);
+        ~ProcessorTrackerGnss() override;
+
+        // Factory method for high level API
+        WOLF_PROCESSOR_CREATE(ProcessorTrackerGnss, ParamsProcessorTrackerGnss);
+
+        void configure(SensorBasePtr _sensor) override;
+
+        unsigned int getNTrackedSats() const;
+
+        unsigned int getNUntrackedSats() const;
+
+    protected:
+
+        ParamsProcessorTrackerGnssPtr params_tracker_gnss_;
+        SensorGnssPtr sensor_gnss_;
+        std::map<int, FeatureGnssSatellitePtr> untracked_incoming_features_, untracked_last_features_;
+        GnssUtils::ComputePosOutput fix_incoming_, fix_last_;
+        unsigned int outliers_pseudorange_, outliers_tdcp_, inliers_pseudorange_, inliers_tdcp_;
+        std::map<int, unsigned int> sat_outliers_;
+        Eigen::Vector3d first_pos_;
+
+        /** \brief Track provided features in \b _capture
+         * \param _features_in input list of features in \b last to track
+         * \param _capture the capture in which the _features_in should be searched
+         * \param _features_out returned list of features found in \b _capture
+         * \param _feature_correspondences returned map of correspondences: _feature_correspondences[feature_out_ptr] = feature_in_ptr
+         *
+         * IMPORTANT: The features in _features_out should be emplaced. Don't use `make_shared`, use `FeatureBase::emplace` instead.
+         * Then, they will be already linked to the _capture.
+         *
+         * \return the number of features tracked
+         */
+        unsigned int trackFeatures(const FeatureBasePtrList& _features_in,
+                                           const CaptureBasePtr& _capture,
+                                           FeatureBasePtrList& _features_out,
+                                           FeatureMatchMap& _feature_correspondences) override;
+
+        /** \brief Correct the drift in incoming feature by re-comparing against the corresponding feature in origin.
+         * \param _last_feature input feature in last capture tracked
+         * \param _incoming_feature input/output feature in incoming capture to be corrected
+         * \return false if the the process discards the correspondence with origin's feature
+         */
+        bool correctFeatureDrift(const FeatureBasePtr _origin_feature, const FeatureBasePtr _last_feature, FeatureBasePtr _incoming_feature) override;
+
+        /** \brief Vote for KeyFrame generation
+         *
+         * If a KeyFrame criterion is validated, this function returns true,
+         * meaning that it wants to create a KeyFrame at the \b last Capture.
+         *
+         * WARNING! This function only votes! It does not create KeyFrames!
+         */
+        bool voteForKeyFrame() const override;
+
+        /** \brief Detect new Features
+         * \param _max_features maximum number of features detected (-1: unlimited. 0: none)
+         * \param _capture The capture in which the new features should be detected.
+         * \param _features_out The list of detected Features in _capture.
+         * \return The number of detected Features.
+         *
+         * This function detects Features that do not correspond to known Features/Landmarks in the system.
+         *
+         * IMPORTANT: The features in _features_out should be emplaced. Don't use `make_shared`, use `FeatureBase::emplace` instead.
+         * Then, they will be already linked to the _capture.
+         *
+         * The function is called in ProcessorTrackerFeature::processNew() to set the member new_features_last_,
+         * the list of newly detected features of the capture last_ptr_.
+         */
+        unsigned int detectNewFeatures(const int& _max_new_features,
+                                               const CaptureBasePtr& _capture,
+                                               FeatureBasePtrList& _features_out) override;
+
+        /** \brief OVERWRITTEN Emplaces tdcp and pseudorange factors
+         */
+        void establishFactors() override;
+
+        /** \brief Emplaces a new factor
+         * \param _feature_ptr pointer to the parent Feature
+         * \param _feature_other_ptr pointer to the other feature constrained.
+         *
+         * This function emplaces a factor of the appropriate type for the derived processor.
+         */
+        FactorBasePtr emplaceFactor(FeatureBasePtr _feature_ptr, FeatureBasePtr _feature_other_ptr) override{return nullptr;};
+
+        /** Pre-process incoming Capture
+         *
+         * This is called by process() just after assigning incoming_ptr_ to a valid Capture.
+         *
+         * Overload this function to prepare stuff on derived classes.
+         *
+         * Typical uses of prePrecess() are:
+         *   - casting base types to derived types
+         *   - initializing counters, flags, or any derived variables
+         *   - initializing algorithms needed for processing the derived data
+         */
+        void preProcess() override;
+
+        /** Post-process
+         *
+         * This is called by process() after finishing the processing algorithm.
+         *
+         * Overload this function to post-process stuff on derived classes.
+         *
+         * Typical uses of postPrecess() are:
+         *   - resetting and/or clearing variables and/or algorithms at the end of processing
+         *   - drawing / printing / logging the results of the processing
+         */
+        void postProcess() override;
+
+        void advanceDerived() override;
+        void resetDerived() override;
+
+        void removeOutliers(FactorBasePtrList fac_list, CaptureBasePtr cap);
+};
+
+inline ProcessorTrackerGnss::~ProcessorTrackerGnss()
+{
+    //
+}
+
+inline void ProcessorTrackerGnss::configure(SensorBasePtr _sensor)
+{
+    sensor_gnss_ = std::dynamic_pointer_cast<SensorGnss>(_sensor);
+    assert(sensor_gnss_ != nullptr && "configured a processor tracker gnss with a wrong sensor");
+}
+
+inline bool ProcessorTrackerGnss::correctFeatureDrift(const FeatureBasePtr _origin_feature,
+                                                      const FeatureBasePtr _last_feature,
+                                                      FeatureBasePtr _incoming_feature)
+{
+    return true;
+}
+
+inline unsigned int ProcessorTrackerGnss::getNTrackedSats() const
+{
+    return known_features_last_.size();
+}
+
+inline unsigned int ProcessorTrackerGnss::getNUntrackedSats() const
+{
+    return untracked_last_features_.size();
+}
+
+} // namespace wolf
+
+#endif /* PROCESSOR_TRACKER_GNSS_H_ */
diff --git a/include/gnss/sensor/sensor_gnss.h b/include/gnss/sensor/sensor_gnss.h
index 66c410926..03fc7a2d4 100644
--- a/include/gnss/sensor/sensor_gnss.h
+++ b/include/gnss/sensor/sensor_gnss.h
@@ -1,3 +1,24 @@
+//--------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--------
 #ifndef SENSOR_GPS_H_
 #define SENSOR_GPS_H_
 
@@ -8,86 +29,123 @@
 
 namespace wolf {
 
-WOLF_STRUCT_PTR_TYPEDEFS(IntrinsicsGnss);
+static const char CLOCK_BIAS_KEY = 'T';
+static const char CLOCK_BIAS_GPS_GLO_KEY = 'G';
+static const char CLOCK_BIAS_GPS_GAL_KEY = 'E';
+static const char CLOCK_BIAS_GPS_CMP_KEY = 'M';
+
+WOLF_STRUCT_PTR_TYPEDEFS(ParamsSensorGnss);
 WOLF_PTR_TYPEDEFS(SensorGnss);
 
-struct IntrinsicsGnss : public IntrinsicsBase
-{
-        // add GNSS parameters here
-        bool extrinsics_fixed_ = true;
-        bool roll_fixed_ =true;
-        bool pitch_fixed_=true;
-        bool yaw_fixed_  =false;
-        bool translation_fixed_ = false;
-        virtual ~IntrinsicsGnss() = default;
-        IntrinsicsGnss() = default;
-        IntrinsicsGnss(std::string _unique_name, const ParamsServer& _server):
-            IntrinsicsBase(_unique_name, _server)
+struct ParamsSensorGnss : public ParamsSensorBase
+{
+        // extrinsics and intrinsics
+        bool extrinsics_fixed = true;
+        bool clock_bias_GPS_GLO_dynamic = false;
+        bool clock_bias_GPS_GAL_dynamic = false;
+        bool clock_bias_GPS_CMP_dynamic = false;
+
+        // ENU
+        std::string ENU_mode = "auto";
+        bool roll_fixed = true;
+        bool pitch_fixed = true;
+        bool yaw_fixed = true;
+        bool translation_fixed = true;
+        Eigen::Vector3d ENU_latlonalt = Eigen::Vector3d::Zero();
+
+
+        ~ParamsSensorGnss() override = default;
+        ParamsSensorGnss() = default;
+        ParamsSensorGnss(std::string _unique_name, const ParamsServer& _server):
+            ParamsSensorBase(_unique_name, _server)
         {
-            extrinsics_fixed_   = _server.getParam<bool>(_unique_name + "/extrinsics_fixed");
-            roll_fixed_         = _server.getParam<bool>(_unique_name + "/roll_fixed");
-            pitch_fixed_        = _server.getParam<bool>(_unique_name + "/pitch_fixed");
-            yaw_fixed_          = _server.getParam<bool>(_unique_name + "/yaw_fixed");
-            translation_fixed_  = _server.getParam<bool>(_unique_name + "/translation_fixed");
+            extrinsics_fixed            = _server.getParam<bool>(prefix + _unique_name + "/extrinsics_fixed");
+            clock_bias_GPS_GLO_dynamic  = _server.getParam<bool>(prefix + _unique_name + "/clock_bias_GPS_GLO_dynamic");
+            clock_bias_GPS_GAL_dynamic  = _server.getParam<bool>(prefix + _unique_name + "/clock_bias_GPS_GAL_dynamic");
+            clock_bias_GPS_CMP_dynamic  = _server.getParam<bool>(prefix + _unique_name + "/clock_bias_GPS_CMP_dynamic");
+            ENU_mode                    = _server.getParam<std::string>(prefix + _unique_name + "/ENU/mode");
+            if (ENU_mode == "manual" or ENU_mode == "auto")
+            {
+                roll_fixed         = _server.getParam<bool>(prefix + _unique_name + "/ENU/roll_fixed");
+                pitch_fixed        = _server.getParam<bool>(prefix + _unique_name + "/ENU/pitch_fixed");
+                yaw_fixed          = _server.getParam<bool>(prefix + _unique_name + "/ENU/yaw_fixed");
+                translation_fixed  = _server.getParam<bool>(prefix + _unique_name + "/ENU/translation_fixed");
+                if (ENU_mode == "manual")
+                    ENU_latlonalt      = _server.getParam<Eigen::Vector3d>(prefix + _unique_name + "/ENU_latlonalt");
+            }
+            else if (ENU_mode != "ECEF")
+            {
+                std::runtime_error("bad 'ENU_mode' value. Should be 'manual', 'auto' or 'ECEF'");
+            }
         }
-        std::string print() const
+        std::string print() const override
         {
-            return "\n" + IntrinsicsBase::print()                           + "\n"
-            + "extrinsics_fixed: "  + std::to_string(extrinsics_fixed_)     + "\n"
-            + "roll_fixed: "        + std::to_string(roll_fixed_)           + "\n"
-            + "pitch_fixed: "       + std::to_string(pitch_fixed_)          + "\n"
-            + "yaw_fixed: "         + std::to_string(yaw_fixed_)            + "\n"
-            + "translation_fixed: " + std::to_string(translation_fixed_)    + "\n";
+            return "\n" + ParamsSensorBase::print()                                             + "\n"
+            + "extrinsics_fixed: "              + std::to_string(extrinsics_fixed)              + "\n"
+            + "clock_bias_GPS_GLO_dynamic: "    + std::to_string(clock_bias_GPS_GLO_dynamic)    + "\n"
+            + "clock_bias_GPS_GAL_dynamic: "    + std::to_string(clock_bias_GPS_GAL_dynamic)    + "\n"
+            + "clock_bias_GPS_CMP_dynamic: "    + std::to_string(clock_bias_GPS_CMP_dynamic)    + "\n"
+            + "ENU_mode: "                      + ENU_mode                                      + "\n"
+            + "roll_fixed: "                    + std::to_string(roll_fixed)                    + "\n"
+            + "pitch_fixed: "                   + std::to_string(pitch_fixed )                  + "\n"
+            + "yaw_fixed: "                     + std::to_string(yaw_fixed)                     + "\n"
+            + "translation_fixed: "             + std::to_string(translation_fixed)             + "\n"
+            + "ENU_latlonalt: to_string not implemented yet!"                                   + "\n";
         }
 };
 
 class SensorGnss : public SensorBase
 {
     protected:
-        IntrinsicsGnssPtr params_;
-        bool ENU_defined_, ENU_MAP_initialized_;
-        Eigen::Matrix3s R_ENU_ECEF_;
-        Eigen::Vector3s t_ENU_ECEF_;
+        ParamsSensorGnssPtr params_;
+        bool ENU_defined_, t_ENU_MAP_initialized_, R_ENU_MAP_initialized_;
+        Eigen::Matrix3d R_ENU_ECEF_;
+        Eigen::Vector3d t_ENU_ECEF_;
 
     public:
-        SensorGnss(StateBlockPtr _p_ptr, StateBlockPtr _bias_ptr, IntrinsicsGnssPtr params);
-        SensorGnss(StateBlockPtr _p_ptr, StateBlockPtr _bias_ptr, IntrinsicsGnssPtr params, StateBlockPtr _t_ENU_MAP_ptr, StateBlockPtr _roll_ENU_MAP_ptr, StateBlockPtr _pitch_ENU_MAP_ptr, StateBlockPtr _yaw_ENU_MAP_ptr);
+        SensorGnss(const Eigen::VectorXd& _extrinsics,
+                   const ParamsSensorGnssPtr& _ParamsSensor);
+        WOLF_SENSOR_CREATE(SensorGnss, ParamsSensorGnss, 3);
 
-        virtual ~SensorGnss();
+        ~SensorGnss() override;
 
         // Gets
         StateBlockPtr getEnuMapTranslation() const;
         StateBlockPtr getEnuMapRoll() const;
         StateBlockPtr getEnuMapPitch() const;
         StateBlockPtr getEnuMapYaw() const;
-        const Eigen::Matrix3s& getREnuEcef() const;
-        const Eigen::Vector3s& gettEnuEcef() const;
-        Eigen::Matrix3s getREnuMap() const;
-        Eigen::Vector3s gettEnuMap() const;
+        const Eigen::Matrix3d& getREnuEcef() const;
+        const Eigen::Vector3d& gettEnuEcef() const;
+        Eigen::Matrix3d getREnuMap() const;
+        Eigen::Vector3d gettEnuMap() const;
         bool isEnuDefined() const;
         bool isEnuMapInitialized() const;
+        bool isEnuMapTranslationInitialized() const;
+        bool isEnuMapRotationInitialized() const;
+        bool isEnuModeEcef() const;
+        bool isEnuModeAuto() const;
+        bool isEnuMapFixed() const;
 
         // Sets
-        void setEnuMapTranslationState(const Eigen::Vector3s& t_ENU_MAP);
-        void setEnuMapRollState(const Scalar& roll_ENU_MAP);
-        void setEnuMapPitchState(const Scalar& pitch_ENU_MAP);
-        void setEnuMapYawState(const Scalar& yaw_ENU_MAP);
-        void setEcefEnu(const Eigen::Vector3s& _t_ECEF_ENU);
-        void setEnuEcef(const Eigen::Matrix3s& _R_ENU_ECEF, const Eigen::Vector3s& _t_ENU_ECEF);
+        void setEnuMapTranslationState(const Eigen::Vector3d& t_ENU_MAP);
+        void setEnuMapRollState(const double& roll_ENU_MAP);
+        void setEnuMapPitchState(const double& pitch_ENU_MAP);
+        void setEnuMapYawState(const double& yaw_ENU_MAP);
+        void setEcefEnu(const Eigen::Vector3d& _ENU, bool _ECEF_coordinates=true);
+        void setEnuEcef(const Eigen::Matrix3d& _R_ENU_ECEF, const Eigen::Vector3d& _t_ENU_ECEF);
+        void setEnuMapInitialized(const bool& _init);
+        void setEnuMapTranslationInitialized(const bool& _init);
+        void setEnuMapRotationInitialized(const bool& _init);
+        void fixEnuMap();
 
         // compute
         template<typename T>
         Eigen::Matrix<T,3,3> computeREnuMap(const T& _r,const T& _p,const T& _y) const;
-        void computeEnuEcef(const Eigen::Vector3s& _t_ECEF_ENU, Eigen::Matrix3s& R_ENU_ECEF, Eigen::Vector3s& t_ENU_ECEF) const;
-        void initializeEnuMap(const Eigen::VectorXs& _pose_MAP_frameENU, const Eigen::Vector3s& _t_ECEF_antenaENU,
-                              const Eigen::VectorXs& _pose_MAP_frame2, const Eigen::Vector3s& _t_ECEF_antena2);
-        void initializeEnuMapYaw(const Eigen::VectorXs& _pose_MAP_frame1, const Eigen::VectorXs& _pose_MAP_frame2,
-                                 const Eigen::Vector3s& _v_ECEF_antena1_antena2);
-
-    public:
-        static SensorBasePtr create(const std::string& _unique_name, const Eigen::VectorXs& _extrinsics_p, const IntrinsicsBasePtr _intrinsics);
-
-
+        void initializeEnuMap(const Eigen::VectorXd& _pose_MAP_frameENU, const Eigen::Vector3d& _t_ECEF_antenaENU,
+                              const Eigen::VectorXd& _pose_MAP_frame2, const Eigen::Vector3d& _t_ECEF_antena2);
+        void initializeEnuMapYaw(const Eigen::VectorXd& _pose_MAP_frame1, const Eigen::VectorXd& _pose_MAP_frame2,
+                                 const Eigen::Vector3d& _v_ECEF_antena1_antena2);
+        Eigen::Vector3d computeFrameAntennaPosEcef(const FrameBasePtr&) const;
 };
 
 inline bool SensorGnss::isEnuDefined() const
@@ -97,52 +155,109 @@ inline bool SensorGnss::isEnuDefined() const
 
 inline bool SensorGnss::isEnuMapInitialized() const
 {
-    return ENU_MAP_initialized_;
+    return t_ENU_MAP_initialized_ && R_ENU_MAP_initialized_;
+}
+
+inline bool SensorGnss::isEnuMapTranslationInitialized() const
+{
+    return t_ENU_MAP_initialized_;
+}
+
+inline bool SensorGnss::isEnuMapRotationInitialized() const
+{
+    return R_ENU_MAP_initialized_;
+}
+
+inline bool SensorGnss::isEnuModeEcef() const
+{
+    return params_->ENU_mode == "ECEF";
+}
+
+inline bool SensorGnss::isEnuModeAuto() const
+{
+    return params_->ENU_mode == "auto";
 }
 
 inline StateBlockPtr SensorGnss::getEnuMapTranslation() const
 {
-    return getStateBlockPtrStatic(3);
+    return getStateBlock('t');
 }
 
 inline StateBlockPtr SensorGnss::getEnuMapRoll() const
 {
-    return getStateBlockPtrStatic(4);
+    return getStateBlock('r');
 }
 
 inline StateBlockPtr SensorGnss::getEnuMapPitch() const
 {
-    return getStateBlockPtrStatic(5);
+    return getStateBlock('p');
 }
 
 inline StateBlockPtr SensorGnss::getEnuMapYaw() const
 {
-    return getStateBlockPtrStatic(6);
+    return getStateBlock('y');
 }
 
-inline const Eigen::Matrix3s& SensorGnss::getREnuEcef() const
+inline const Eigen::Matrix3d& SensorGnss::getREnuEcef() const
 {
     return R_ENU_ECEF_;
 }
 
-inline const Eigen::Vector3s& SensorGnss::gettEnuEcef() const
+inline const Eigen::Vector3d& SensorGnss::gettEnuEcef() const
 {
     return t_ENU_ECEF_;
 }
 
+inline Eigen::Vector3d SensorGnss::gettEnuMap() const
+{
+    return getEnuMapTranslation()->getState();
+}
+
 template<typename T>
 inline Eigen::Matrix<T,3,3> SensorGnss::computeREnuMap(const T& _r,const T& _p,const T& _y) const
 {
-    Eigen::Matrix<T,3,3> R_ENU_MAP = (Eigen::AngleAxis<T>(_r, Eigen::Matrix<T,3,1>::UnitX())
-                                    * Eigen::AngleAxis<T>(_p, Eigen::Matrix<T,3,1>::UnitY())
-                                    * Eigen::AngleAxis<T>(_y, Eigen::Matrix<T,3,1>::UnitZ())).toRotationMatrix();
+    return Eigen::Matrix<T,3,3>(Eigen::AngleAxis<T>(_r, Eigen::Matrix<T,3,1>::UnitX()) *
+                                Eigen::AngleAxis<T>(_p, Eigen::Matrix<T,3,1>::UnitY()) *
+                                Eigen::AngleAxis<T>(_y, Eigen::Matrix<T,3,1>::UnitZ()));
+}
+
+inline Eigen::Matrix3d SensorGnss::getREnuMap() const
+{
+    return computeREnuMap(getEnuMapRoll() ->getState()(0),
+                          getEnuMapPitch()->getState()(0),
+                          getEnuMapYaw()  ->getState()(0));
+}
+
+inline void SensorGnss::setEnuMapInitialized(const bool& _init)
+{
+    t_ENU_MAP_initialized_ = _init;
+    R_ENU_MAP_initialized_ = _init;
+}
+
+inline void SensorGnss::setEnuMapTranslationInitialized(const bool& _init)
+{
+    t_ENU_MAP_initialized_ = _init;
+}
+
+inline void SensorGnss::setEnuMapRotationInitialized(const bool& _init)
+{
+    R_ENU_MAP_initialized_ = _init;
+}
 
-    return R_ENU_MAP;
+inline bool SensorGnss::isEnuMapFixed() const
+{
+    return getEnuMapTranslation()->isFixed() and
+           getEnuMapRoll()->isFixed() and
+           getEnuMapPitch()->isFixed() and
+           getEnuMapYaw()->isFixed();
+}
 
-    // TODO: store R's when T=Scalar
-    //Eigen::Matrix<T,3,3> R_roll_T, R_pitch_T, R_yaw_T;
-    //if (!getRollState()->isFixed())
-    //    *R_roll_ptr_ = Eigen::AngleAxis<Scalar>(0.25*M_PI, Eigen::Vector3s::UnitX());
+inline void SensorGnss::fixEnuMap()
+{
+    getEnuMapTranslation()->fix();
+    getEnuMapRoll()->fix();
+    getEnuMapPitch()->fix();
+    getEnuMapYaw()->fix();
 }
 
 } // namespace wolf
diff --git a/include/gnss/tree_manager/tree_manager_sliding_window_tdcp.h b/include/gnss/tree_manager/tree_manager_sliding_window_tdcp.h
new file mode 100644
index 000000000..2124f1506
--- /dev/null
+++ b/include/gnss/tree_manager/tree_manager_sliding_window_tdcp.h
@@ -0,0 +1,64 @@
+//--------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--------
+#ifndef INCLUDE_TREE_MANAGER_SLIDING_WINDOW_TDCP_H_
+#define INCLUDE_TREE_MANAGER_SLIDING_WINDOW_TDCP_H_
+
+#include "core/tree_manager/tree_manager_sliding_window.h"
+
+namespace wolf
+{
+
+WOLF_STRUCT_PTR_TYPEDEFS(ParamsTreeManagerSlidingWindowTdcp)
+WOLF_PTR_TYPEDEFS(TreeManagerSlidingWindowTdcp)
+
+struct ParamsTreeManagerSlidingWindowTdcp : public ParamsTreeManagerSlidingWindow
+{
+        ParamsTreeManagerSlidingWindowTdcp() = default;
+        ParamsTreeManagerSlidingWindowTdcp(std::string _unique_name, const wolf::ParamsServer & _server) :
+            ParamsTreeManagerSlidingWindow(_unique_name, _server)
+        {
+        }
+        std::string print() const override
+        {
+            return "\n" + ParamsTreeManagerSlidingWindow::print();
+        }
+};
+
+class TreeManagerSlidingWindowTdcp : public TreeManagerSlidingWindow
+{
+    public:
+        TreeManagerSlidingWindowTdcp(ParamsTreeManagerSlidingWindowTdcpPtr _params);
+        WOLF_TREE_MANAGER_CREATE(TreeManagerSlidingWindowTdcp, ParamsTreeManagerSlidingWindowTdcp)
+
+        ~TreeManagerSlidingWindowTdcp() override{}
+
+        void keyFrameCallback(FrameBasePtr _key_frame) override;
+
+    protected:
+        ParamsTreeManagerSlidingWindowTdcpPtr params_sw_sb_;
+        SensorBasePtr sensor_imu_;
+        Eigen::Matrix6d cov_bias_;
+};
+
+} /* namespace wolf */
+
+#endif /* INCLUDE_TREE_MANAGER_SLIDING_WINDOW_H_ */
diff --git a/internal/config.h.in b/internal/config.h.in
index b9008e196..7014ca320 100644
--- a/internal/config.h.in
+++ b/internal/config.h.in
@@ -24,8 +24,8 @@
 //            which will be added to the include path for compilation,
 //            and installed with the public wolf headers.
 
-#ifndef WOLF_INTERNAL_${UPPERNAME}_CONFIG_H_
-#define WOLF_INTERNAL_${UPPERNAME}_CONFIG_H_
+#ifndef WOLF_INTERNAL_${UPPER_NAME}_CONFIG_H_
+#define WOLF_INTERNAL_${UPPER_NAME}_CONFIG_H_
 
 #cmakedefine _WOLF_DEBUG
 
diff --git a/license_header_2022.txt b/license_header_2022.txt
new file mode 100644
index 000000000..0c987025f
--- /dev/null
+++ b/license_header_2022.txt
@@ -0,0 +1,17 @@
+// 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/>.
diff --git a/package.xml b/package.xml
index e94a5f327..fc94ad080 100644
--- a/package.xml
+++ b/package.xml
@@ -20,8 +20,6 @@
 
   <!-- wolf does not REQUIRE the following, they are added here for the dependency tree -->
   <build_depend>Ceres</build_depend>
-  <build_depend>laser_scan_utils</build_depend>
-  <build_depend>raw_gps_utils</build_depend>
   <build_depend>OpenCV</build_depend>
   <build_depend>YAMLCPP</build_depend>
 
diff --git a/src/capture/capture_gnss.cpp b/src/capture/capture_gnss.cpp
new file mode 100644
index 000000000..299f515a9
--- /dev/null
+++ b/src/capture/capture_gnss.cpp
@@ -0,0 +1,53 @@
+//--------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 "gnss/capture/capture_gnss.h"
+
+
+namespace wolf {
+
+CaptureGnss::CaptureGnss(const TimeStamp& _ts, SensorBasePtr _sensor_ptr, GnssUtils::SnapshotPtr _snapshot) :
+	CaptureBase("CaptureGnss", _ts, _sensor_ptr),
+	snapshot_(_snapshot)
+{
+    // Clock bias
+    assert(_sensor_ptr->getStateBlock('T') != nullptr and _sensor_ptr->isStateBlockDynamic('T'));
+    addStateBlock('T', std::make_shared<StateBlock>(1,true), nullptr);
+
+    // interconstellation clock bias
+    assert(_sensor_ptr->getStateBlock('G') != nullptr);
+    if(_sensor_ptr->isStateBlockDynamic('G'))
+        addStateBlock('G', std::make_shared<StateBlock>(1,true), nullptr);
+    assert(_sensor_ptr->getStateBlock('E') != nullptr);
+    if(_sensor_ptr->isStateBlockDynamic('E'))
+        addStateBlock('E', std::make_shared<StateBlock>(1,true), nullptr);
+    assert(_sensor_ptr->getStateBlock('M') != nullptr);
+    if(_sensor_ptr->isStateBlockDynamic('M'))
+        addStateBlock('M', std::make_shared<StateBlock>(1,true), nullptr);
+
+}
+
+CaptureGnss::~CaptureGnss()
+{
+	//std::cout << "Destroying GPS fix capture...\n";
+}
+
+} //namespace wolf
diff --git a/src/capture/capture_gnss_fix.cpp b/src/capture/capture_gnss_fix.cpp
index d7f78a9a1..5ddd2bb8a 100644
--- a/src/capture/capture_gnss_fix.cpp
+++ b/src/capture/capture_gnss_fix.cpp
@@ -1,12 +1,33 @@
+//--------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 "gnss/capture/capture_gnss_fix.h"
 
 
 namespace wolf {
 
-CaptureGnssFix::CaptureGnssFix(const TimeStamp& _ts, SensorBasePtr _sensor_ptr, const Eigen::VectorXs& _data, const Eigen::MatrixXs& _data_covariance) :
+CaptureGnssFix::CaptureGnssFix(const TimeStamp& _ts, SensorBasePtr _sensor_ptr, const Eigen::Vector3d& _position, const Eigen::Matrix3d& _covariance, bool _ecef_coordinates) :
 	CaptureBase("CaptureGnssFix", _ts, _sensor_ptr),
-	data_(_data),
-	data_covariance_(_data_covariance)
+	position_ECEF_(_ecef_coordinates ? _position : GnssUtils::latLonAltToEcef(_position)),
+	covariance_ECEF_(_ecef_coordinates ?_covariance : GnssUtils::enuToEcefCov(_position, _covariance))
 {
     //
 }
diff --git a/src/processor/processor_gnss_fix.cpp b/src/processor/processor_gnss_fix.cpp
index 26b58a4e6..31f95fd15 100644
--- a/src/processor/processor_gnss_fix.cpp
+++ b/src/processor/processor_gnss_fix.cpp
@@ -1,17 +1,37 @@
-#include "gnss/factor/factor_gnss_fix_2D.h"
-#include "gnss/factor/factor_gnss_fix_3D.h"
+//--------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 "gnss/capture/capture_gnss.h"
+#include "gnss/capture/capture_gnss_fix.h"
+#include "gnss/factor/factor_gnss_fix_2d.h"
+#include "gnss/factor/factor_gnss_fix_3d.h"
 #include "gnss/feature/feature_gnss_fix.h"
 #include "gnss/processor/processor_gnss_fix.h"
-#include "gnss/capture/capture_gnss_fix.h"
 
 namespace wolf
 {
 
-ProcessorGnssFix::ProcessorGnssFix(ProcessorParamsGnssFixPtr _params_gnss) :
-        ProcessorBase("ProcessorGnssFix", _params_gnss),
-
-        params_gnss_(_params_gnss),
-        first_capture_(nullptr)
+ProcessorGnssFix::ProcessorGnssFix(ParamsProcessorGnssFixPtr _params_gnss) :
+        ProcessorBase("ProcessorGnssFix", 0, _params_gnss),
+        params_gnss_(_params_gnss)
 {
     //
 }
@@ -24,137 +44,235 @@ ProcessorGnssFix::~ProcessorGnssFix()
 void ProcessorGnssFix::processCapture(CaptureBasePtr _capture)
 {
     // TODO: keep captures in a buffer and deal with KFpacks
-    //WOLF_DEBUG("ProcessorGnssFix::process()");
-    incoming_capture_ = std::static_pointer_cast<CaptureGnssFix>(_capture);
+    WOLF_DEBUG("PR ", getName()," process() capture type: ", _capture->getType());
+    incoming_capture_ = _capture;
+
+    bool KF_created = false;
+
+    // Check type of capture: GNSS raw
+    bool israw = false;
+    if (std::dynamic_pointer_cast<CaptureGnss>(_capture)!=nullptr)
+        israw = true;
+    // if not raw nor Fix, not processable
+    else if (std::dynamic_pointer_cast<CaptureGnssFix>(_capture)==nullptr)
+        return;
+
+    // Only process raw if fix_from_raw
+    if (israw and !params_gnss_->fix_from_raw)
+        return;
+
+    // Only process fix if not fix_from_raw
+    if (!israw and params_gnss_->fix_from_raw)
+        return;
 
     FrameBasePtr new_frame = nullptr;
     FactorBasePtr new_fac = nullptr;
 
+    // emplace features
+    if (israw)
+    {
+        auto raw_capture = std::static_pointer_cast<CaptureGnss>(incoming_capture_);
+        incoming_pos_out_ = GnssUtils::computePos(*(raw_capture->getObservations()), *(raw_capture->getNavigation()), params_gnss_->compute_pos_opt);
+        if (!incoming_pos_out_.success) // error
+        {
+            WOLF_DEBUG("computePos failed with msg: ", incoming_pos_out_.msg);
+            return;
+        }
+    }
+    else
+    {
+        WOLF_DEBUG("ProcessorGnssFix: creating feature from incoming navsatfix. Type:", incoming_capture_->getType());
+        auto fix_capture = std::static_pointer_cast<CaptureGnssFix>(incoming_capture_);
+        incoming_pos_out_.time = fix_capture->getGpsTimeStamp().getSeconds();           // time_t time;
+        incoming_pos_out_.sec =  1e-9 * fix_capture->getGpsTimeStamp().getSeconds();    // double sec;
+        incoming_pos_out_.pos = fix_capture->getPositionEcef();                         // Eigen::Vector3d pos;        // position (m)
+        incoming_pos_out_.vel = Eigen::Vector3d::Zero();                                // Eigen::Vector3d vel;        // velocity (m/s)
+        incoming_pos_out_.pos_covar = fix_capture->getCovarianceEcef();                 // Eigen::Matrix3d pos_covar;  // position covariance (m^2)
+        incoming_pos_out_.type = 0;                                                     // int type;                   // coordinates used (0:xyz-ecef,1:enu-baseline)
+    }
+    auto incoming_feature = FeatureBase::emplace<FeatureGnssFix>(incoming_capture_, incoming_pos_out_);
+
     // ALREADY CREATED KF
-    PackKeyFramePtr KF_pack = buffer_pack_kf_.selectPack( incoming_capture_, params_gnss_->time_tolerance);
-    if (KF_pack && KF_pack->key_frame != last_KF_)
+    FrameBasePtr keyframe = buffer_frame_.select( incoming_capture_->getTimeStamp(), params_gnss_->time_tolerance);
+    if (keyframe and last_KF_capture_ and keyframe == last_KF_capture_->getFrame())
+        keyframe = nullptr;
+    if (keyframe)
     {
-        WOLF_DEBUG( "PR ", getName()," - capture ", incoming_capture_->id(), " appended to existing KF " , KF_pack->key_frame->id() , " TS: ", KF_pack->key_frame->getTimeStamp());
-        new_frame = KF_pack->key_frame;
+        WOLF_DEBUG("PR ", getName()," - capture ", incoming_capture_->id(), " appended to existing KF " , keyframe->id() , " TS: ", keyframe->getTimeStamp());
+        new_frame = keyframe;
     }
     // MAKE KF
-    else if (voteForKeyFrame() && permittedKeyFrame())
+    else if (permittedKeyFrame() && voteForKeyFrame())
     {
-        new_frame = getProblem()->emplaceFrame(KEY, incoming_capture_->getTimeStamp());
-        getProblem()->keyFrameCallback(new_frame, shared_from_this(), params_gnss_->time_tolerance);
-        WOLF_DEBUG( "PR ", getName()," - capture ", incoming_capture_->id(), " appended to new KF " , new_frame->id() , " TS: ", new_frame->getTimeStamp());
+        WOLF_DEBUG("PR ", getName()," emplacing KF TS = ", incoming_capture_->getTimeStamp());
+        new_frame = getProblem()->emplaceFrame(incoming_capture_->getTimeStamp());
+        KF_created = true;
     }
+    // OTHERWISE return
+    else
+        return;
 
     // ESTABLISH FACTOR
-    if (new_frame)
-    {
-        // LINK CAPTURE
-        incoming_capture_->link(new_frame); // Add incoming Capture to the new Frame
-
-        // EMPLACE FEATURES
-        WOLF_DEBUG( "PR ", getName()," - emplacing the feature...");
-        auto ftr = FeatureBase::emplace<FeatureGnssFix>(incoming_capture_, incoming_capture_->getData(),incoming_capture_->getDataCovariance());
+    // link capture
+    incoming_capture_->link(new_frame);
+    // emplace factor
+    new_fac = emplaceFactor(incoming_feature);
 
-        // EMPLACE FACTOR
-        new_fac = emplaceFactor(ftr);
+    // outlier rejection (only can be evaluated if ENU defined and ENU-MAP well initialized: fixed)
+    if (params_gnss_->remove_outliers and
+        sensor_gnss_->isEnuDefined() and
+        sensor_gnss_->isEnuMapFixed() and
+        detectOutlier(new_fac))
+    {
+        new_frame->remove();
+        return;
+    }
 
-        // outlier rejection
-        if (sensor_gnss_->isEnuDefined() && sensor_gnss_->isEnuMapInitialized())
-            if (rejectOutlier(new_fac))
-                new_fac = nullptr;
+    // store last KF
+    last_KF_capture_ = incoming_capture_;
+    last_KF_feature_ = incoming_feature;
 
-        // store last KF
-        if (new_fac)
-            last_KF_= new_frame;
+    // Define ENU (if not defined)
+    if (!sensor_gnss_->isEnuDefined())
+    {
+        WOLF_DEBUG("Defining ecef enu");
+        sensor_gnss_->setEcefEnu(incoming_feature->getMeasurement());
     }
 
-    // SET ECEF_ENU IF:
-    //      1 - not initialized
-    //      2 - factor established
-    if (!sensor_gnss_->isEnuDefined() && new_fac != nullptr)
+    // Store the first capture that established a factor (for initializing ENU-MAP)
+    if (first_frame_state_.empty() and
+        not sensor_gnss_->isEnuMapFixed())
     {
-        WOLF_DEBUG("setting ecef enu");
-        sensor_gnss_->setEcefEnu(incoming_capture_->getData());
-
-        // Store the first capture that established a factor
-        first_capture_ = incoming_capture_;
+        first_frame_state_ = incoming_capture_->getFrame()->getState();
+        first_pos_ = incoming_feature->getMeasurement();
     }
-
-    // INITIALIZE ENU_MAP IF 4 NECESSARY CONDITIONS:
-    //      1 - ENU-ECEF defined
-    //      2 - not initialized
-    //      3 - current capture in key-frame with factor
-    //      4 - two factors established (first and current) in frames separated enough ( > enu_map_init_dist_min)
-    if ( sensor_gnss_->isEnuDefined() &&
-        !sensor_gnss_->isEnuMapInitialized() &&
-         new_fac != nullptr &&
-         first_capture_ != nullptr && first_capture_->getFrame() != nullptr && incoming_capture_->getFrame() != nullptr &&
-         (first_capture_->getFrame()->getState()-incoming_capture_->getFrame()->getState()).norm() > params_gnss_->enu_map_init_dist_min)
+    // Initialize ENU-MAP if: ENU defined and ENU-MAP not initialized (and not fixed) and far enough
+    if (params_gnss_->init_enu_map and
+        not first_frame_state_.empty() and
+        sensor_gnss_->isEnuDefined() and
+        not sensor_gnss_->isEnuMapInitialized() and
+        not sensor_gnss_->isEnuMapFixed() and
+        (first_pos_-incoming_pos_out_.pos).norm() > params_gnss_->enu_map_init_dist_min)
     {
-        WOLF_DEBUG("initializing enu map");
-        sensor_gnss_->initializeEnuMap(first_capture_->getFrame()->getState(),      first_capture_->getData(),
-                                       incoming_capture_->getFrame()->getState(),   incoming_capture_->getData());
+        assert(first_frame_state_.count('P') and first_frame_state_.count('O') and incoming_capture_->getFrame() != nullptr);
+
+        sensor_gnss_->initializeEnuMap(first_frame_state_.vector("PO"),
+                                       first_pos_,
+                                       incoming_capture_->getFrame()->getState().vector("PO"),
+                                       incoming_feature->getMeasurement());
+        // Set as not-initialized if factors not separated enough ( < enu_map_init_dist_max)
+        if ((first_pos_ - incoming_feature->getMeasurement()).norm() < params_gnss_->enu_map_init_dist_max)
+            sensor_gnss_->setEnuMapInitialized(false);
     }
+
+    // Fix ENU-MAP if separated enough from first pos ( > enu_map_fix_dist )
+    if ((first_pos_ - incoming_feature->getMeasurement()).norm() > params_gnss_->enu_map_fix_dist)
+        sensor_gnss_->fixEnuMap();
+
+    // Notify if KF created
+    if (KF_created)
+        getProblem()->keyFrameCallback(new_frame, shared_from_this());
 }
 
 FactorBasePtr ProcessorGnssFix::emplaceFactor(FeatureBasePtr _ftr)
 {
-    // CREATE CONSTRAINT --------------------
     //WOLF_DEBUG("creating the factor...");
-    // 2D
+    // 2d
     if (getProblem()->getDim() == 2)
-        return FactorBase::emplace<FactorGnssFix2D>(_ftr, _ftr, sensor_gnss_, shared_from_this(), false, FAC_ACTIVE);
-    // 3D
+        return FactorBase::emplace<FactorGnssFix2d>(_ftr, _ftr, sensor_gnss_, shared_from_this(), params_->apply_loss_function, FAC_ACTIVE);
+    // 3d
     else
-        return FactorBase::emplace<FactorGnssFix3D>(_ftr, _ftr, sensor_gnss_, shared_from_this(), false, FAC_ACTIVE);
+        return FactorBase::emplace<FactorGnssFix3d>(_ftr, _ftr, sensor_gnss_, shared_from_this(), params_->apply_loss_function, FAC_ACTIVE);
 }
 
-bool ProcessorGnssFix::rejectOutlier(FactorBasePtr fac)
+bool ProcessorGnssFix::voteForKeyFrame() const
 {
-    // Cast feature
-    auto gnss_ftr = std::static_pointer_cast<FeatureGnssFix>(fac->getFeature());
-    // copy states
-    Eigen::VectorXs x(gnss_ftr->getCapture()->getFrame()->getP()->getState());
-    Eigen::VectorXs o(gnss_ftr->getCapture()->getFrame()->getP()->getState());
-    Eigen::VectorXs x_antena(sensor_gnss_->getStateBlock(0)->getState());
-    Eigen::VectorXs t_ENU_map(sensor_gnss_->getEnuMapTranslation()->getState());
-    Eigen::VectorXs roll_ENU_map(sensor_gnss_->getEnuMapRoll()->getState());
-    Eigen::VectorXs pitch_ENU_map(sensor_gnss_->getEnuMapPitch()->getState());
-    Eigen::VectorXs yaw_ENU_map(sensor_gnss_->getEnuMapYaw()->getState());
-    // create Scalar* array of a copy of the state
-    Scalar* parameters[7] = {x.data(), o.data(), x_antena.data(), t_ENU_map.data(), roll_ENU_map.data(),
-                             pitch_ENU_map.data(), yaw_ENU_map.data()};
-    // create residuals pointer
-    Eigen::VectorXs residuals(3);
-    // evaluate the factor in this state
-    fac->evaluate(parameters, residuals.data(), nullptr);
-    // discard if residual too high evaluated at the current estimation
-    if (residuals.norm() > 1e3)
+    //WOLF_DEBUG("voteForKeyFrame...");
+    // Null lastKF
+    if (not last_KF_capture_)
     {
-        WOLF_WARN("Discarding GNSS FIX Factor, considered OUTLIER");
-        WOLF_TRACE("Feature: ", fac->getMeasurement().transpose(),"\nError: ",(fac->getMeasurementSquareRootInformationUpper().inverse()*residuals).transpose());
-        fac->remove();
+        WOLF_DEBUG("KF because of null lastKF");
         return true;
     }
-    return false;
-}
 
-bool ProcessorGnssFix::voteForKeyFrame() const
-{
     // Depending on time since the last KF with gnssfix capture
-    if (!last_KF_ || (incoming_capture_->getTimeStamp() - last_KF_->getTimeStamp()) > params_gnss_->time_th)
+    if (not last_KF_capture_ or
+        (incoming_capture_->getTimeStamp() - last_KF_capture_->getTimeStamp()) > params_gnss_->max_time_span)
+    {
+        WOLF_DEBUG("KF because of time since last KF");
         return true;
+    }
 
-    // Distance criterion
-    if ((incoming_capture_->getFrame()->getP()->getState() - last_KF_->getP()->getState()).norm() > params_gnss_->dist_traveled)
+    // ENU not defined
+    if (not sensor_gnss_->isEnuDefined())
+    {
+        WOLF_DEBUG("KF because of enu not defined");
         return true;
+    }
 
-    // TODO: more alternatives?
+    // ENU-MAP not initialized and can be initialized
+    if (params_gnss_->init_enu_map and
+        not first_frame_state_.empty() and
+        sensor_gnss_->isEnuDefined() and
+        not sensor_gnss_->isEnuMapInitialized() and
+        not sensor_gnss_->isEnuMapFixed() and
+        (first_pos_-incoming_pos_out_.pos).norm() > params_gnss_->enu_map_init_dist_min and
+        (first_pos_-incoming_pos_out_.pos).norm() < params_gnss_->enu_map_init_dist_max)
+    {
+        WOLF_DEBUG("KF because of enu map not initialized");
+        return true;
+    }
+
+    // Distance criterion (ENU defined and ENU-MAP initialized)
+    if (last_KF_capture_ != nullptr and
+        (incoming_pos_out_.pos - last_KF_feature_->getMeasurement()).norm() > params_gnss_->dist_traveled)
+    {
+        WOLF_DEBUG("KF because of distance criterion: ", (incoming_pos_out_.pos - last_KF_feature_->getMeasurement()).norm());
+        return true;
+    }
 
     // otherwise
     return false;
 }
 
+bool ProcessorGnssFix::detectOutlier(FactorBasePtr fac)
+{
+    WOLF_DEBUG( "PR ", getName()," rejectOutlier...");
+
+    // Cast feature
+    auto gnss_ftr = std::static_pointer_cast<FeatureGnssFix>(fac->getFeature());
+
+    // copy states
+    Eigen::VectorXd x(gnss_ftr->getCapture()->getFrame()->getP()->getState());
+    Eigen::VectorXd o(gnss_ftr->getCapture()->getFrame()->getO()->getState());
+    Eigen::VectorXd x_antena(sensor_gnss_->getP()->getState());
+    Eigen::VectorXd t_ENU_map(sensor_gnss_->getEnuMapTranslation()->getState());
+    Eigen::VectorXd roll_ENU_map(sensor_gnss_->getEnuMapRoll()->getState());
+    Eigen::VectorXd pitch_ENU_map(sensor_gnss_->getEnuMapPitch()->getState());
+    Eigen::VectorXd yaw_ENU_map(sensor_gnss_->getEnuMapYaw()->getState());
+
+    // create double* array of a copy of the state
+    double* parameters[7] = {x.data(), o.data(), x_antena.data(), t_ENU_map.data(), roll_ENU_map.data(),
+                             pitch_ENU_map.data(), yaw_ENU_map.data()};
+    // residual
+    Eigen::Vector3d residual;
+
+    // evaluate the factor in this state
+    fac->evaluate(parameters, residual.data(), nullptr);
+
+    // evaluate if residual too high at the current estimation
+    if (residual.norm() > params_gnss_->outlier_residual_th)
+    {
+        WOLF_WARN("Discarding GNSS FIX Factor, considered OUTLIER");
+        WOLF_TRACE("Feature: ", fac->getMeasurement().transpose(),
+                   "\n\tError: ",(fac->getMeasurementSquareRootInformationUpper().inverse()*residual).transpose(),
+                   "\n\tResidual: ",residual.transpose(),
+                   "\n\tResidual norm: ",residual.norm(), "(max: ", params_gnss_->outlier_residual_th, ")");
+        return true;
+    }
+    return false;
+}
+
 bool ProcessorGnssFix::storeKeyFrame(FrameBasePtr _frame_ptr)
 {
     return true;
@@ -168,19 +286,11 @@ void ProcessorGnssFix::configure(SensorBasePtr _sensor)
     sensor_gnss_ = std::static_pointer_cast<SensorGnss>(_sensor);
 };
 
-ProcessorBasePtr ProcessorGnssFix::create(const std::string& _unique_name, const ProcessorParamsBasePtr _params)
-{
-    auto prc = std::make_shared<ProcessorGnssFix>(std::static_pointer_cast<ProcessorParamsGnssFix>(_params));
-    prc->setName(_unique_name);
-    return prc;
-}
-
-
 } // namespace wolf
 
-
-// Register in the SensorFactory
-#include "core/processor/processor_factory.h"
+// Register in the FactoryProcessor
+#include "core/processor/factory_processor.h"
 namespace wolf {
-WOLF_REGISTER_PROCESSOR("ProcessorGnssFix",ProcessorGnssFix)
+WOLF_REGISTER_PROCESSOR(ProcessorGnssFix)
+WOLF_REGISTER_PROCESSOR_AUTO(ProcessorGnssFix);
 } // namespace wolf
diff --git a/src/processor/processor_gnss_single_diff.cpp b/src/processor/processor_gnss_single_diff.cpp
index 3636ccd73..99c73f2fc 100644
--- a/src/processor/processor_gnss_single_diff.cpp
+++ b/src/processor/processor_gnss_single_diff.cpp
@@ -1,4 +1,25 @@
-#include "gnss/factor/factor_gnss_single_diff_2D.h"
+//--------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 "gnss/factor/factor_gnss_single_diff_2d.h"
 #include "gnss/feature/feature_gnss_single_diff.h"
 #include "gnss/processor/processor_gnss_single_diff.h"
 #include "gnss/capture/capture_gnss_single_diff.h"
@@ -6,8 +27,8 @@
 namespace wolf
 {
 
-ProcessorGnssSingleDiff::ProcessorGnssSingleDiff(ProcessorParamsGnssSingleDiffPtr _params_gnss) :
-        ProcessorBase("GNSS SINGLE DIFFERENCES", _params_gnss),
+ProcessorGnssSingleDiff::ProcessorGnssSingleDiff(ParamsProcessorGnssSingleDiffPtr _params_gnss) :
+        ProcessorBase("ProcessorGnssSingleDiff", 0, _params_gnss),
         params_gnss_(_params_gnss)
 {
     //
@@ -48,7 +69,7 @@ void ProcessorGnssSingleDiff::processCapture(CaptureBasePtr _capture)
     // MAKE KF
     else if (voteForKeyFrame() && permittedKeyFrame())
     {
-        new_frame = getProblem()->emplaceFrame(KEY, incoming_capture_->getTimeStamp());
+        new_frame = getProblem()->emplaceKeyFrame( incoming_capture_->getTimeStamp());
         getProblem()->keyFrameCallback(new_frame, shared_from_this(), params_->time_tolerance);
         WOLF_DEBUG( "PR ",getName()," - capture ", incoming_capture_->id(), " appended to new KF " , new_frame->id() , " TS: ", new_frame->getTimeStamp());
     }
@@ -90,12 +111,12 @@ void ProcessorGnssSingleDiff::processCapture(CaptureBasePtr _capture)
 FactorBasePtr ProcessorGnssSingleDiff::emplaceFactor(FeatureBasePtr _ftr)
 {
     //WOLF_DEBUG("creating the factor...");
-    // 2D
+    // 2d
     if (getProblem()->getDim() == 2)
-        return FactorBase::emplace<FactorGnssSingleDiff2D>(_ftr, _ftr, incoming_capture_->getOriginFrame(), sensor_gnss_, shared_from_this());
-    // 3D TODO
+        return FactorBase::emplace<FactorGnssSingleDiff2d>(_ftr, _ftr, incoming_capture_->getOriginFrame(), sensor_gnss_, shared_from_this(), params_->apply_loss_function);
+    // 3d TODO
     else
-        std::runtime_error("Single Differences in 3D not implemented yet.");
+        std::runtime_error("Single Differences in 3d not implemented yet.");
 
     return nullptr;
 }
@@ -114,9 +135,9 @@ bool ProcessorGnssSingleDiff::voteForKeyFrame() const
 
     // Distance criterion
     std::cout << "params_gnss_->dist_traveled = " << params_gnss_->dist_traveled << std::endl;
-    Eigen::Vector2s v_current_origin = (sensor_gnss_->getREnuMap().transpose() * sensor_gnss_->getREnuEcef() * incoming_capture_->getData()).head<2>();
+    Eigen::Vector2d v_current_origin = (sensor_gnss_->getREnuMap().transpose() * sensor_gnss_->getREnuEcef() * incoming_capture_->getData()).head<2>();
     std::cout << "v_current_origin: " << v_current_origin.transpose() << std::endl;
-    Eigen::Vector2s v_origin_last_KF = last_KF_->getP()->getState() - incoming_capture_->getOriginFrame()->getP()->getState();
+    Eigen::Vector2d v_origin_last_KF = last_KF_->getP()->getState() - incoming_capture_->getOriginFrame()->getP()->getState();
     std::cout << "v_origin_last_KF: " << v_origin_last_KF.transpose() << std::endl;
     std::cout << "v_current_origin + v_origin_last_KF: " << (v_current_origin + v_origin_last_KF).transpose() << std::endl;
     if ((v_current_origin + v_origin_last_KF).norm() > params_gnss_->dist_traveled)
@@ -141,9 +162,9 @@ void ProcessorGnssSingleDiff::configure(SensorBasePtr _sensor)
     sensor_gnss_ = std::static_pointer_cast<SensorGnss>(_sensor);
 };
 
-ProcessorBasePtr ProcessorGnssSingleDiff::create(const std::string& _unique_name, const ProcessorParamsBasePtr _params)
+ProcessorBasePtr ProcessorGnssSingleDiff::create(const std::string& _unique_name, const ParamsProcessorBasePtr _params)
 {
-    ProcessorGnssSingleDiffPtr prc = std::make_shared<ProcessorGnssSingleDiff>(std::static_pointer_cast<ProcessorParamsGnssSingleDiff>(_params));
+    ProcessorGnssSingleDiffPtr prc = std::make_shared<ProcessorGnssSingleDiff>(std::static_pointer_cast<ParamsProcessorGnssSingleDiff>(_params));
     prc->setName(_unique_name);
     return prc;
 }
@@ -151,8 +172,8 @@ ProcessorBasePtr ProcessorGnssSingleDiff::create(const std::string& _unique_name
 } // namespace wolf
 
 
-// Register in the SensorFactory
-#include "core/processor/processor_factory.h"
+// Register in the FactorySensor
+#include "core/processor/factory_processor.h"
 namespace wolf {
-WOLF_REGISTER_PROCESSOR("GNSS SINGLE DIFFERENCES",ProcessorGnssSingleDiff)
+WOLF_REGISTER_PROCESSOR(ProcessorGnssSingleDiff)
 } // namespace wolf
diff --git a/src/processor/processor_gnss_tdcp.cpp b/src/processor/processor_gnss_tdcp.cpp
new file mode 100644
index 000000000..c087f7074
--- /dev/null
+++ b/src/processor/processor_gnss_tdcp.cpp
@@ -0,0 +1,270 @@
+//--------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 "../../include/gnss/factor/factor_gnss_tdcp_2d.h"
+#include "../../include/gnss/factor/factor_gnss_tdcp_3d.h"
+#include "gnss/capture/capture_gnss_tdcp.h"
+#include "gnss/feature/feature_gnss_tdcp.h"
+#include "gnss/feature/feature_gnss_fix.h"
+#include "gnss/processor/processor_gnss_tdcp.h"
+
+namespace wolf
+{
+
+ProcessorGnssTdcp::ProcessorGnssTdcp(ParamsProcessorGnssTdcpPtr _params_gnss) :
+        ProcessorBase("ProcessorGnssTdcp", 0, _params_gnss),
+        params_tdcp_(_params_gnss)
+{
+    //
+}
+
+ProcessorGnssTdcp::~ProcessorGnssTdcp()
+{
+    //
+}
+
+void ProcessorGnssTdcp::processCapture(CaptureBasePtr _capture)
+{
+    if (std::dynamic_pointer_cast<CaptureGnss>(_capture))
+        buffer_capture_.emplace(_capture->getTimeStamp(), _capture);
+
+    /*
+    // TODO: keep captures in a buffer and deal with KFpacks
+    WOLF_DEBUG("ProcessorGnssTdcp::process()");
+    incoming_capture_ = std::static_pointer_cast<CaptureGnssTdcp>(_capture);
+
+    // discard capture with null or non-key origin frame
+    if (incoming_capture_->getOriginFrame() == nullptr || !incoming_capture_->getOriginFrame()->isKey())
+    {
+        WOLF_WARN("process single difference with null frame origin, skipping...");
+        return;
+    }
+
+    if (last_KF_ == nullptr && incoming_capture_->getOriginFrame() != nullptr && incoming_capture_->getOriginFrame()->isKey())
+        last_KF_ = incoming_capture_->getOriginFrame();
+
+    // NEW KF? ------------------------------------------------
+    FrameBasePtr new_frame = nullptr;
+
+    // ALREADY CREATED KF
+    PackKeyFramePtr KF_pack = buffer_pack_kf_.selectPack( incoming_capture_, params_->time_tolerance);
+    if (KF_pack && KF_pack->key_frame != incoming_capture_->getOriginFrame())
+    {
+        new_frame = KF_pack->key_frame;
+        WOLF_DEBUG( "PR ",getName()," - capture ", incoming_capture_->id(), " appended to existing KF " , KF_pack->key_frame->id() , " TS: ", KF_pack->key_frame->getTimeStamp());
+    }
+    // MAKE KF
+    else if (voteForKeyFrame() && permittedKeyFrame())
+    {
+        new_frame = getProblem()->emplaceKeyFrame( incoming_capture_->getTimeStamp());
+        getProblem()->keyFrameCallback(new_frame, shared_from_this(), params_->time_tolerance);
+        WOLF_DEBUG( "PR ",getName()," - capture ", incoming_capture_->id(), " appended to new KF " , new_frame->id() , " TS: ", new_frame->getTimeStamp());
+    }
+
+    // ESTABLISH FACTOR ------------------------------------------------
+    if (new_frame)
+    {
+        // LINK CAPTURE
+        _capture->link(new_frame); // Add incoming Capture to the new Frame
+
+        // EMPLACE FEATURE
+        auto ftr = FeatureBase::emplace<FeatureGnssTdcp>(incoming_capture_, incoming_capture_->getData(),incoming_capture_->getDataCovariance());
+
+        // ADD FACTOR
+        emplaceFactor(ftr);
+
+        // store last KF
+        last_KF_ = new_frame;
+    }
+
+    // INITIALIZE ENU_MAP IF 4 NECESSARY CONDITIONS ------------------------------------------------
+    //      1 - ENU-ECEF defined
+    //      2 - not initialized
+    //      3 - current capture in key-frame with factor
+    //      4 - frames constained by the factor separated enough ( > enu_map_init_dist_min)
+    if ( sensor_gnss_->isEnuDefined() &&
+        !sensor_gnss_->isEnuMapRotationInitialized() &&
+         new_frame != nullptr &&
+         incoming_capture_->getFrame() != nullptr && incoming_capture_->getFrame()->isKey() &&
+         incoming_capture_->getData().norm() > params_tdcp_->enu_map_init_dist_min)
+    {
+        WOLF_INFO("initializing enu map");
+        sensor_gnss_->initializeEnuMapYaw(incoming_capture_->getOriginFrame()->getState(),
+                                          incoming_capture_->getFrame()->getState(),
+                                          incoming_capture_->getData());
+    }
+    */
+}
+
+void ProcessorGnssTdcp::processKeyFrame(FrameBasePtr _keyframe)
+{
+    WOLF_INFO("ProcessorGnssTdcp::processKeyFrame()");
+
+    // update last_KF
+    if (!last_KF_ or last_KF_->getTimeStamp() < _keyframe->getTimeStamp())
+        last_KF_ = _keyframe;
+
+    // Add GNSS capture if not already stored
+    auto cap_gnss = _keyframe->getCaptureOf(this->getSensor(), "CaptureGnss");
+    if (!cap_gnss)
+    {
+        // find capture in timestamp
+        cap_gnss = buffer_capture_.select(_keyframe->getTimeStamp(), params_tdcp_->time_tolerance);
+
+        // remove previous captures
+        buffer_capture_.removeUpTo(_keyframe->getTimeStamp());
+
+        // return if no capture GNSS found in that timestamp
+        if (!cap_gnss)
+            return;
+
+        // remove previous captures
+        buffer_capture_.removeUpTo(cap_gnss->getTimeStamp());
+
+        // Add found Capture to the new Frame
+        cap_gnss->link(_keyframe);
+    }
+
+    // Iterate over all KF of the trajectory with GNSS captures
+    for (auto KF_it = getProblem()->getTrajectory()->rbegin();
+         KF_it != getProblem()->getTrajectory()->rend();
+         KF_it++)
+    {
+        auto KF_ref = *KF_it;
+        if (_keyframe->getTimeStamp() < KF_ref->getTimeStamp())
+            continue;
+
+        // jump the same keyframe
+        if (KF_ref == _keyframe)
+            continue;
+
+        // jump KF without GNSS captures
+        auto cap_gnss_ref = KF_ref->getCaptureOf(this->getSensor(), "CaptureGnss");
+        if (!cap_gnss_ref)
+            continue;
+
+        // stop if KF_ref too old w.r.t. keyframe
+        if (_keyframe->getTimeStamp() - KF_ref->getTimeStamp() > params_tdcp_->tdcp.time_window)
+            break;
+
+        // Get feature GNSS fix
+        FeatureGnssFixPtr feat_fix = nullptr;
+        for (auto cap : KF_ref->getCaptureList())
+        {
+            for (auto ftr : cap->getFeatureList())
+            {
+                feat_fix = std::dynamic_pointer_cast<FeatureGnssFix>(ftr);
+                if (feat_fix)
+                    break;
+            }
+            if (feat_fix)
+                break;
+        }
+
+        // (for now) jump KF without GNSS Fix features
+        if (!feat_fix)
+            continue; // TODO use KF state and compute in ECEF coordinates
+
+        WOLF_INFO("Past KF with feature fix found. Performing TDCP!");
+
+        // TDCP
+        std::set<int> discarded_sats;
+        auto tdcp_output = GnssUtils::Tdcp(std::static_pointer_cast<CaptureGnss>(cap_gnss_ref)->getSnapshot(),
+                                           std::static_pointer_cast<CaptureGnss>(cap_gnss)->getSnapshot(),
+                                           feat_fix->getMeasurement(),
+                                           Eigen::Vector4d::Zero(),
+                                           discarded_sats,
+                                           params_tdcp_->tdcp,
+                                           params_tdcp_->compute_pos_opt);
+        if (tdcp_output.success)
+        {
+            WOLF_INFO("TDCP successfully performed!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+            // EMPLACE FEATURE
+            auto ftr = FeatureBase::emplace<FeatureGnssTdcp>(cap_gnss, tdcp_output.d, tdcp_output.cov_d);
+
+            // ADD FACTOR
+            emplaceFactor(ftr, KF_ref);
+        }
+    }
+}
+
+FactorBasePtr ProcessorGnssTdcp::emplaceFactor(FeatureBasePtr _ftr, FrameBasePtr _frm_ref)
+{
+    //WOLF_DEBUG("creating the factor...");
+    // 2D
+    if (getProblem()->getDim() == 2)
+        return FactorBase::emplace<FactorGnssTdcp2d>(_ftr, _ftr, _frm_ref, sensor_gnss_, shared_from_this(), params_->apply_loss_function);
+    // 3D
+    else
+        return FactorBase::emplace<FactorGnssTdcp3d>(_ftr, _ftr, _frm_ref, sensor_gnss_, shared_from_this(), params_->apply_loss_function);
+
+    return nullptr;
+}
+
+bool ProcessorGnssTdcp::voteForKeyFrame() const
+{
+//    if (last_KF_==nullptr)
+//        return true;
+//
+//    //std::cout << "params_tdcp_->max_time_span" << params_tdcp_->max_time_span << std::endl;
+//    //std::cout << "(incoming_capture_->getTimeStamp() - last_KF_->getTimeStamp())" << (incoming_capture_->getTimeStamp() - last_KF_->getTimeStamp()) << std::endl;
+//
+//    // Elapsed time criterion: From the last KF with gnssfix capture
+//    if ((incoming_capture_->getTimeStamp() - last_KF_->getTimeStamp()) > params_tdcp_->max_time_span)
+//    {
+//    	WOLF_DEBUG("voting for KF: elapsed time criterion");
+//    	return true;
+//    }
+//
+//    // Distance criterion: From the last KF with gnssfix capture
+//    Eigen::Vector2d v_origin_current = (sensor_gnss_->getREnuMap().transpose() * sensor_gnss_->getREnuEcef() * incoming_capture_->getData()).head<2>();
+//    Eigen::Vector2d v_lastKF_origin = incoming_capture_->getOriginFrame()->getP()->getState() - last_KF_->getP()->getState();
+//	//std::cout << "params_tdcp_->dist_traveled" << params_tdcp_->dist_traveled << std::endl;
+//	//std::cout << "v_origin_current: " << v_origin_current.transpose() << std::endl;
+//	//std::cout << "v_lastKF_origin: " << v_lastKF_origin.transpose() << std::endl;
+//	//std::cout << "v_lastKF_origin + v_origin_current: " << (v_lastKF_origin + v_origin_current).transpose() << std::endl;
+//    if ((v_lastKF_origin + v_origin_current).norm() > params_tdcp_->dist_traveled)
+//	{
+//    	WOLF_DEBUG("voting for KF: distance criterion");
+//    	return true;
+//	}
+//
+//    // TODO: more alternatives?
+
+    // otherwise
+    return false;
+}
+
+void ProcessorGnssTdcp::configure(SensorBasePtr _sensor)
+{
+    sensor_gnss_ = std::dynamic_pointer_cast<SensorGnss>(_sensor);
+    assert(sensor_gnss_ != nullptr);
+};
+
+} // namespace wolf
+
+
+// Register in the FactoryProcessor
+#include "core/processor/factory_processor.h"
+namespace wolf {
+WOLF_REGISTER_PROCESSOR(ProcessorGnssTdcp)
+WOLF_REGISTER_PROCESSOR_AUTO(ProcessorGnssTdcp)
+} // namespace wolf
diff --git a/src/processor/processor_tracker_gnss.cpp b/src/processor/processor_tracker_gnss.cpp
new file mode 100644
index 000000000..ace52948c
--- /dev/null
+++ b/src/processor/processor_tracker_gnss.cpp
@@ -0,0 +1,870 @@
+//--------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 "gnss/processor/processor_tracker_gnss.h"
+
+#include "gnss/capture/capture_gnss.h"
+#include "gnss/feature/feature_gnss_tdcp.h"
+#include "gnss/feature/feature_gnss_fix.h"
+#include "gnss/factor/factor_gnss_tdcp.h"
+#include "gnss/factor/factor_gnss_tdcp_batch.h"
+#include "gnss/factor/factor_gnss_pseudo_range.h"
+#include "gnss/factor/factor_gnss_fix_3d.h"
+#include "gnss_utils/utils/rcv_position.h"
+#include "gnss_utils/tdcp.h"
+
+namespace wolf
+{
+
+ProcessorTrackerGnss::ProcessorTrackerGnss(ParamsProcessorTrackerGnssPtr _params_tracker_gnss) :
+        ProcessorTrackerFeature("ProcessorTrackerGnss", "PO", 3, _params_tracker_gnss),
+        params_tracker_gnss_(_params_tracker_gnss),
+        outliers_pseudorange_(0),
+        outliers_tdcp_(0),
+        inliers_pseudorange_(0),
+        inliers_tdcp_(0),
+        first_pos_(Eigen::Vector3d::Zero())
+{
+}
+
+void ProcessorTrackerGnss::preProcess()
+{
+    WOLF_DEBUG("ProcessorTrackerGnss::preProcess");
+
+    GnssUtils::SnapshotPtr inc_snapshot = std::static_pointer_cast<CaptureGnss>(incoming_ptr_)->getSnapshot();
+    GnssUtils::Options copy_opt = params_tracker_gnss_->gnss_opt;
+    bool eph_sbas34 = params_tracker_gnss_->gnss_opt.sateph == EPHOPT_SBAS3 or
+                      params_tracker_gnss_->gnss_opt.sateph == EPHOPT_SBAS4;
+    bool iono_sbas34 = params_tracker_gnss_->gnss_opt.ionoopt == IONOOPT_SBAS3 or
+                       params_tracker_gnss_->gnss_opt.ionoopt == IONOOPT_SBAS4;
+
+#ifdef _WOLF_DEBUG
+    int n_initial = inc_snapshot->getObservations()->size();
+    std::string initial_str;
+    for (auto obs : inc_snapshot->getObservations()->getObservations())
+        initial_str += std::to_string(obs.sat) + " ";
+#endif
+    // overload SBAS3 and SBAS4 first chance
+    if (eph_sbas34)
+        copy_opt.sateph = EPHOPT_SBAS;
+    if (iono_sbas34)
+        copy_opt.ionoopt = IONOOPT_SBAS;
+
+    // compute satellites positions
+    inc_snapshot ->computeSatellites(copy_opt.sateph);
+
+    /* this fix is used for:
+     *   - set ENU
+     *   - compute azimuths and elevations
+     *   - Take the eventual discarded sats by RAIM
+     *   - initialize clock bias
+     **/
+    fix_incoming_ = GnssUtils::computePos(*inc_snapshot->getObservations(),
+                                          *inc_snapshot->getNavigation(),
+                                          params_tracker_gnss_->fix_opt);
+
+    // Initialize clock bias stateblocks in capture
+    if (fix_incoming_.success)
+    {
+        incoming_ptr_->getStateBlock('T') ->setState(Eigen::Vector1d(CLIGHT*fix_incoming_.rcv_bias(0)));
+        if (sensor_gnss_->isStateBlockDynamic('G'))
+            incoming_ptr_->getStateBlock('G')->setState(Eigen::Vector1d(CLIGHT*fix_incoming_.rcv_bias(1)));
+        if (sensor_gnss_->isStateBlockDynamic('E'))
+            incoming_ptr_->getStateBlock('E')->setState(Eigen::Vector1d(CLIGHT*fix_incoming_.rcv_bias(2)));
+        if (sensor_gnss_->isStateBlockDynamic('M'))
+            incoming_ptr_->getStateBlock('M')->setState(Eigen::Vector1d(CLIGHT*fix_incoming_.rcv_bias(3)));
+
+        if (first_pos_.squaredNorm() < 1e-6)
+            first_pos_ = fix_incoming_.pos;
+    }
+
+    // Set ECEF-ENU
+    if (!sensor_gnss_->isEnuDefined() and sensor_gnss_->isEnuModeAuto() )
+    {
+        if (fix_incoming_.success)
+        {
+            WOLF_DEBUG("setting ECEF-ENU: ", fix_incoming_.pos.transpose());
+            sensor_gnss_->setEcefEnu(fix_incoming_.pos, true);
+        }
+        else
+            WOLF_WARN("Failed to compute fix to set ECEF-ENU in auto mode. Message: ", fix_incoming_.msg);
+    }
+    // Fix ENU-MAP
+    if ((fix_incoming_.pos - first_pos_).norm() > params_tracker_gnss_->enu_map_fix_dist)
+    {
+        sensor_gnss_->getEnuMapTranslation()->fix();
+        sensor_gnss_->getEnuMapRoll()->fix();
+        sensor_gnss_->getEnuMapPitch()->fix();
+        sensor_gnss_->getEnuMapYaw()->fix();
+    }
+
+    WOLF_DEBUG("TS: ", incoming_ptr_->getTimeStamp(), " - Fix solution (ECEF): ", fix_incoming_.pos.transpose(), " - Fix solution (GEO): ", fix_incoming_.lat_lon.transpose());
+
+    // store initial observation before filtering
+    GnssUtils::ObservationsPtr init_obs;
+    if (eph_sbas34 or iono_sbas34)
+        init_obs = std::make_shared<GnssUtils::Observations>(*inc_snapshot->getObservations());
+
+    // filter observations (available ephemeris, constellations and elevation&SNR)
+    #ifdef _WOLF_DEBUG
+        auto discarded_gnssutils =
+    #endif
+    inc_snapshot->filterObservations(fix_incoming_.discarded_sats, // discarded sats
+                                     fix_incoming_.sat_azel,
+                                     false, // check code
+                                     false, // check carrier phase
+                                     copy_opt);
+
+    // compute corrected Ranges
+    inc_snapshot->computeRanges(fix_incoming_.sat_azel,
+                                fix_incoming_.lat_lon,
+                                copy_opt);
+
+    /* NOT ENOUGH SATS/RANGES in case of:
+     *     eph:  EPHOPT_SBAS3 or EPHOPT_SBAS4
+     *     iono: IONOOPT_SBAS3 or IONOOPT_SBAS4
+     */
+    if (inc_snapshot->getRanges().size() < params_tracker_gnss_->min_sbas_sats and
+        (eph_sbas34 or iono_sbas34))
+    {
+        WOLF_DEBUG("ProcessorTrackerGnss::preProcess: with SBAS3/SBAS4, not enough ranges: ", inc_snapshot->getObservations()->size(), " Computing with SBAS2/BRDC");
+
+        if (params_tracker_gnss_->gnss_opt.sateph == EPHOPT_SBAS3)
+            copy_opt.sateph = EPHOPT_SBAS2;
+        if (params_tracker_gnss_->gnss_opt.sateph == EPHOPT_SBAS4)
+            copy_opt.sateph = EPHOPT_BRDC;
+
+        if (params_tracker_gnss_->gnss_opt.ionoopt == IONOOPT_SBAS3)
+            copy_opt.ionoopt = IONOOPT_SBAS2;
+        if (params_tracker_gnss_->gnss_opt.ionoopt == IONOOPT_SBAS4)
+            copy_opt.ionoopt = IONOOPT_BRDC;
+
+        // reset observations
+        inc_snapshot->setObservations(init_obs);
+        inc_snapshot->getRanges().clear();
+        // recompute satellites
+        inc_snapshot ->computeSatellites(copy_opt.sateph);
+        // filter
+        #ifdef _WOLF_DEBUG
+            discarded_gnssutils =
+        #endif
+        inc_snapshot->filterObservations(fix_incoming_.discarded_sats, // discarded sats
+                                         fix_incoming_.sat_azel,
+                                         false, // check code
+                                         false, // check carrier phase
+                                         copy_opt);
+
+
+        // recompute corrected Ranges
+        inc_snapshot->computeRanges(fix_incoming_.sat_azel,
+                                    fix_incoming_.lat_lon,
+                                    copy_opt);
+
+        WOLF_DEBUG("ProcessorTrackerGnss::preProcess: with SBAS2/BRDC, obtained ranges: ", inc_snapshot->getRanges().size());
+    }
+
+    // create features pseudorange
+    for (auto obs_sat : inc_snapshot->getObservations()->getObservations())
+    {
+        if(inc_snapshot->getSatellites().count(obs_sat.sat) == 0)
+        {
+            WOLF_DEBUG("ProcessorTrackerGnss::preProcess(): Creating features of obs_sat ", obs_sat.sat , " satellite not found");
+            continue;
+        }
+        if (inc_snapshot->getRanges().count(obs_sat.sat) == 0)
+        {
+            WOLF_DEBUG("ProcessorTrackerGnss::preProcess(): Creating features of obs_sat ", obs_sat.sat , " pseudorange not found");
+            continue;
+        }
+
+        auto feat = FeatureBase::emplace<FeatureGnssSatellite>(incoming_ptr_, obs_sat, inc_snapshot->getSatellites().at(obs_sat.sat), inc_snapshot->getRanges().at(obs_sat.sat));
+        assert(untracked_incoming_features_.count(feat->satNumber()) == 0);
+        untracked_incoming_features_[feat->satNumber()] = feat;
+    }
+
+#ifdef _WOLF_DEBUG
+    std::string used_rtklib_str, discarded_rtklib_str, detected_str, discarded_gnssutils_str;
+    for (auto sat : fix_incoming_.used_sats)
+        used_rtklib_str += std::to_string(sat) + " ";
+    for (auto sat : fix_incoming_.discarded_sats)
+        discarded_rtklib_str += std::to_string(sat) + " ";
+    for (auto ftr_pair : untracked_incoming_features_)
+        detected_str += std::to_string(ftr_pair.first) + " ";
+    for (auto sat_disc : discarded_gnssutils)
+        if (fix_incoming_.discarded_sats.count(sat_disc) == 0)
+            discarded_gnssutils_str += std::to_string(sat_disc) + " ";
+#endif
+
+    WOLF_DEBUG("ProcessorTrackerGnss::preProcess()",
+              "\n\tinitial observations: ", n_initial, " (", initial_str, ")",
+              "\n\tRTKLIB used: ", fix_incoming_.used_sats.size(), " (", used_rtklib_str, ")",
+              //"\n\tRTKLIB discarded: ", fix_incoming_.discarded_sats.size(), " (", discarded_rtklib_str, ")",
+              "\n\tgnssutils discarded: ", n_initial - untracked_incoming_features_.size() - fix_incoming_.discarded_sats.size(), " (", discarded_gnssutils_str, ")",
+              "\n\tdetected incoming features: ", untracked_incoming_features_.size(), " (", detected_str, ")");
+}
+
+unsigned int ProcessorTrackerGnss::trackFeatures(const FeatureBasePtrList& _features_in,
+                                                 const CaptureBasePtr& _capture,
+                                                 FeatureBasePtrList& _features_out,
+                                                 FeatureMatchMap& _feature_correspondences)
+{
+    if (_features_in.empty())
+        return 0;
+
+    WOLF_DEBUG_COND(_features_out.begin() == new_features_incoming_.begin(), "ProcessorTrackerGnss::trackFeatures tracking " , _features_in.size() , " new features...");
+    WOLF_DEBUG_COND(_features_out.begin() == known_features_incoming_.begin(), "ProcessorTrackerGnss::trackFeatures tracking " , _features_in.size() , " known features...");
+
+    assert(_capture == incoming_ptr_);
+
+    int common_sats = 0;
+
+    for (auto feat_in : _features_in)
+    {
+        auto feat_in_gnss = std::dynamic_pointer_cast<FeatureGnssSatellite>(feat_in);
+        assert(feat_in_gnss);
+
+        int sat_num = std::static_pointer_cast<FeatureGnssSatellite>(feat_in)->satNumber();
+
+        WOLF_DEBUG("tracking " , feat_in->trackId() , ", sat number ", sat_num);
+
+        if (untracked_incoming_features_.count(sat_num) != 0)
+            common_sats++;
+
+        if (untracked_incoming_features_.count(sat_num) != 0 and
+            std::abs(untracked_incoming_features_.at(sat_num)->getObservation().L[0]) > 1e-12) // Track only carrier phase valid
+        {
+            auto ftr = untracked_incoming_features_[sat_num];
+            untracked_incoming_features_.erase(sat_num);
+            _features_out.push_back(ftr);
+            _feature_correspondences[ftr] = std::make_shared<FeatureMatch>(FeatureMatch({feat_in,0}));
+
+            WOLF_DEBUG("\ttracked: " , feat_in->trackId() , " last: " , feat_in->id() , " inc: " , ftr->id() , " !" );
+        }
+        else
+        {
+            WOLF_DEBUG_COND(untracked_incoming_features_.count(sat_num) == 0, "\tnot tracked, missing satellite" );
+            WOLF_DEBUG_COND(untracked_incoming_features_.count(sat_num) == 1, "\tnot tracked, wrong carrier phase value" );
+        }
+    }
+    WOLF_WARN_COND(_features_out.empty() and
+                   _features_out.begin() == known_features_incoming_.begin(),
+                   "ProcessorTrackerGnss::trackFeatures: LOST TRACK OF ALL SATELLITES of ", _features_in.size(),
+                   " - ", common_sats, " due to wrong Carrier Phase data.");
+    WOLF_DEBUG_COND(_features_out.begin() == new_features_incoming_.begin(),
+                    "ProcessorTrackerGnss::trackFeatures: tracked " ,
+                    _features_out.size(), " new features (of ", _features_in.size(), ")");
+    WOLF_DEBUG_COND(_features_out.begin() == known_features_incoming_.begin(),
+                    "ProcessorTrackerGnss::trackFeatures: tracked " ,
+                    _features_out.size(), " known features (of ", _features_in.size(), ")");
+
+    return _features_out.size();
+}
+
+bool ProcessorTrackerGnss::voteForKeyFrame() const
+{
+    //WOLF_DEBUG("ProcessorTrackerGnss::voteForKeyFrame");
+
+    // too old origin
+    if (origin_ptr_== nullptr or (last_ptr_->getTimeStamp() - origin_ptr_->getTimeStamp()) > params_tracker_gnss_->max_time_span )
+    {
+        WOLF_DEBUG( "Vote for KF because of time span or null origin" );
+        return true;
+    }
+
+    /* KNOWN FEATURES
+     * If tracked sats are lower than minimum (min_features_for_keyframe), a KF in last will be useful
+     * if it allows to add more satellites (untracked features in last is not empty)
+     */
+    WOLF_DEBUG("Nbr. of active feature tracks: " , known_features_incoming_.size() );
+    if (known_features_incoming_.size() < params_tracker_feature_->min_features_for_keyframe
+        and not untracked_last_features_.empty() )
+    {
+        WOLF_DEBUG( "Vote for KF because of too less known_features_incoming and not empty untracked in last" );
+        return true;
+    }
+
+    // TODO: more alternatives?
+
+   // otherwise
+   return false;
+}
+
+unsigned int ProcessorTrackerGnss::detectNewFeatures(const int& _max_new_features,
+                                                     const CaptureBasePtr& _capture,
+                                                     FeatureBasePtrList& _features_out)
+{
+    WOLF_DEBUG("ProcessorTrackerGnss::detectNewFeatures");
+
+    assert(_capture == last_ptr_);
+
+    for (auto feat_pair : untracked_last_features_)
+    {
+        if (_features_out.size() == _max_new_features)
+            break;
+
+        // discard non-valid carrier phase
+        if (std::abs(std::static_pointer_cast<FeatureGnssSatellite>(feat_pair.second)->getObservation().L[0]) < 1e-12)
+            continue;
+
+        _features_out.push_back(feat_pair.second);
+        WOLF_DEBUG("feature " , feat_pair.second->id() , " detected!" );
+    }
+    WOLF_DEBUG(_features_out.size() , " new features detected!");
+
+    return _features_out.size();
+}
+
+void ProcessorTrackerGnss::establishFactors()
+{
+    WOLF_DEBUG("ProcessorTrackerGnss::establishFactors");
+
+    // initialize frame state with antenna position in map coordinates
+    // (since we don't have orientation for removing extrinsics)
+    if (params_tracker_gnss_->init_frames and fix_last_.success)
+        last_ptr_->getFrame()->getP()->setState(sensor_gnss_->getREnuMap().transpose() * (sensor_gnss_->getREnuEcef() * fix_last_.pos + sensor_gnss_->gettEnuEcef() - sensor_gnss_->gettEnuMap()));
+
+    FactorBasePtrList new_factors;
+
+    // FIX FACTOR
+    if (params_tracker_gnss_->fix)
+    {
+        GnssUtils::SnapshotPtr last_snapshot = std::static_pointer_cast<CaptureGnss>(last_ptr_)->getSnapshot();
+
+
+        auto fix = GnssUtils::computePos(*last_snapshot->getObservations(),
+                                          *last_snapshot->getNavigation(),
+                                          params_tracker_gnss_->gnss_opt);
+        if (fix.success)
+        {
+            FeatureBasePtr ftr = FeatureBase::emplace<FeatureGnssFix>(last_ptr_, fix);
+
+            FactorBase::emplace<FactorGnssFix3d>(ftr,
+                                                 ftr,
+                                                 sensor_gnss_,
+                                                 shared_from_this(),
+                                                 params_tracker_gnss_->apply_loss_function,
+                                                 FAC_ACTIVE);
+        }
+    }
+
+    // PSEUDO RANGE FACTORS (all sats)
+    if (params_tracker_gnss_->pseudo_ranges)
+        for (auto ftr : last_ptr_->getFeatureList())
+        {
+            auto ftr_sat = std::dynamic_pointer_cast<FeatureGnssSatellite>(ftr);
+            if (ftr_sat == nullptr)
+                continue;
+
+            // Check valid measurement
+            if (std::abs(ftr_sat->getMeasurement()(0)) < 1e-12)
+            {
+                WOLF_WARN("Ignoring feature with not valid pseudorange: ", ftr_sat->getMeasurement()(0));
+                continue;
+            }
+
+            // unfix clock bias
+            last_ptr_->getStateBlock('T')->unfix();
+            // unfix clock bias inter-constellation (if observed)
+            if (ftr_sat->getSatellite().sys == SYS_GLO)
+                last_ptr_->getStateBlock('G')->unfix();
+            if (ftr_sat->getSatellite().sys == SYS_GAL)
+                last_ptr_->getStateBlock('E')->unfix();
+            if (ftr_sat->getSatellite().sys == SYS_CMP)
+                last_ptr_->getStateBlock('M')->unfix();
+
+            // Emplace a pseudo range factor
+            auto new_fac = FactorBase::emplace<FactorGnssPseudoRange>(ftr_sat,
+                                                                      ftr_sat,
+                                                                      sensor_gnss_,
+                                                                      shared_from_this(),
+                                                                      params_->apply_loss_function);
+            new_factors.push_back(new_fac);
+
+            WOLF_DEBUG("FactorGnssPseudoRange emplaced for satellite: ", ftr_sat->satNumber());
+        }
+
+    // TDCP FACTORS (tracked sats)
+    if (origin_ptr_ != last_ptr_ and params_tracker_gnss_->tdcp_enabled)
+    {
+        // Displacement factor from batch TDCP (FactorGnssTdcp3d)
+        if (params_tracker_gnss_->tdcp_params.batch)
+        {
+            WOLF_DEBUG("TDCP BATCH frame ", last_ptr_->getFrame()->id());
+            FactorBasePtr last_fac_ptr = nullptr;
+
+            for (auto KF_rit = getProblem()->getTrajectory()->rbegin();
+                 KF_rit != getProblem()->getTrajectory()->rend();
+                 KF_rit++)
+            {
+                FrameBasePtr KF = (*KF_rit);
+
+                WOLF_DEBUG("TDCP BATCH ref frame ", KF->id());
+
+                // discard non-key frames, last-last pair and frames without CaptureGnss
+                if (KF == last_ptr_->getFrame() or
+                    KF->getCaptureOf(getSensor(),"CaptureGnss") == nullptr)
+                    continue;
+
+                // static cast
+                auto ref_cap_gnss = std::static_pointer_cast<CaptureGnss>((*KF_rit)->getCaptureOf(getSensor(),"CaptureGnss"));
+                auto last_cap_gnss = std::static_pointer_cast<CaptureGnss>(last_ptr_);
+
+                // dt
+                double dt = last_ptr_->getTimeStamp() - ref_cap_gnss->getTimeStamp();
+                WOLF_DEBUG("TDCP BATCH dt = ", dt);
+
+                // discard strange cases
+                if (dt <= 0)
+                    continue;
+
+                // within time window
+                if (dt > params_tracker_gnss_->tdcp_params.time_window)
+                    break;
+
+                // discard removing Frame/capture/feature
+                if (ref_cap_gnss->isRemoving() or ref_cap_gnss->getFrame()->isRemoving())
+                    continue;
+
+                // get common sats from tracking
+                auto matches = track_matrix_.matches(ref_cap_gnss, last_cap_gnss);
+                WOLF_DEBUG("TDCP BATCH matches ", matches.size());
+                std::set<int> common_sats;
+                for (auto match : matches)
+                {
+                    assert(std::dynamic_pointer_cast<FeatureGnssSatellite>(match.second.first)->satNumber());
+                    assert(std::dynamic_pointer_cast<FeatureGnssSatellite>(match.second.second)->satNumber());
+                    assert(std::static_pointer_cast<FeatureGnssSatellite>(match.second.first)->satNumber() ==
+                           std::static_pointer_cast<FeatureGnssSatellite>(match.second.second)->satNumber());
+                    common_sats.insert(std::static_pointer_cast<FeatureGnssSatellite>(match.second.first)->satNumber());
+                }
+                WOLF_DEBUG("TDCP BATCH common_sats: ", common_sats.size());
+                //for (auto sat : common_sats)
+                //    std::cout << sat << " ";
+                //std::cout << std::endl;
+
+                // DEBUG: FIND COMMON SATELLITES OBSERVATIONS
+                //std::set<int> common_sats_debug = GnssUtils::Observations::findCommonObservations(*ref_cap_gnss->getSnapshot()->getObservations(),
+                //                                                                                  *last_cap_gnss->getSnapshot()->getObservations());
+                //WOLF_DEBUG("TDCP BATCH common_sats_debug: ", common_sats_debug.size());
+                //for (auto sat : common_sats_debug)
+                //    std::cout << sat << " ";
+                //std::cout << std::endl;
+
+                // reference ECEF position
+                Eigen::Vector3d x_r = sensor_gnss_->computeFrameAntennaPosEcef(KF);
+
+                // compute TDCP batch
+                auto tdcp_output = GnssUtils::Tdcp(ref_cap_gnss->getSnapshot(),
+                                                   last_cap_gnss->getSnapshot(),
+                                                   x_r,
+                                                   common_sats,
+                                                   Eigen::Vector4d::Zero(),
+                                                   params_tracker_gnss_->tdcp_params);
+                if (tdcp_output.success)
+                {
+                    WOLF_DEBUG("TDCP BATCH d = ", tdcp_output.d.transpose());
+                    WOLF_DEBUG("TDCP BATCH cov =\n", tdcp_output.cov_d);
+
+                    // EMPLACE FEATURE
+                    auto factor_status = FAC_ACTIVE;
+                    if (params_tracker_gnss_->tdcp_structure == "first-all")
+                        factor_status = FAC_INACTIVE;
+
+                    auto ftr = FeatureBase::emplace<FeatureGnssTdcp>(last_cap_gnss,
+                                                                     tdcp_output.d,
+                                                                     tdcp_output.cov_d);
+
+                    // EMPLACE FACTOR
+                    last_fac_ptr = FactorBase::emplace<FactorGnssTdcpBatch>(ftr,
+                                                                            ftr,
+                                                                            ref_cap_gnss,
+                                                                            sensor_gnss_,
+                                                                            shared_from_this(),
+                                                                            params_->apply_loss_function,
+                                                                            factor_status);
+
+                    WOLF_DEBUG("TDCP BATCH factor ", last_fac_ptr->id() , " emplaced as ", (last_fac_ptr->getStatus() == FAC_ACTIVE ? "ACTIVE" : "INACTIVE"));
+
+                    // only first (with previous frame) factor in "consecutive" structure
+                    if (params_tracker_gnss_->tdcp_structure == "consecutive")
+                        break;
+                }
+                else
+                {
+                    WOLF_DEBUG("TDCP BATCH failed with msg: ", tdcp_output.msg);
+                }
+            }
+            // only last (with first frame) factor in "first-all" structure
+            if (params_tracker_gnss_->tdcp_structure == "first-all" and last_fac_ptr)
+            {
+                last_fac_ptr->setStatus(FAC_ACTIVE);
+                WOLF_DEBUG("TDCP BATCH factor ", last_fac_ptr->id() , " changed to ", (last_fac_ptr->getStatus() == FAC_ACTIVE ? "ACTIVE" : "INACTIVE"));
+            }
+        }
+        // FACTOR per SATELLITE (FactorGnssTdcp)
+        else
+        {
+            // iterate over tracked features of last
+            for (auto ftr_k_pair : track_matrix_.snapshot(last_ptr_))
+            {
+                // current feature
+                auto ftr_k = std::dynamic_pointer_cast<FeatureGnssSatellite>(ftr_k_pair.second);
+                assert(ftr_k != nullptr);
+
+                // check valid measurement
+                assert(std::abs(ftr_k->getObservation().L[0]) > 1e-12);
+
+                WOLF_DEBUG("FeatureGnssSatellite sat: ", ftr_k->satNumber(), " id: ", ftr_k->id());
+
+                // unfix clock bias
+                last_ptr_->getStateBlock('T')->unfix();
+
+                // emplace a tdcp factor from last to each KF
+                auto ts_ftr_r_map = track_matrix_.trackAtKeyframes(ftr_k_pair.first);
+                for (auto ts_ftr_r_it = ts_ftr_r_map.rbegin(); ts_ftr_r_it != ts_ftr_r_map.rend(); ts_ftr_r_it++)
+                {
+                    // cast reference feature
+                    auto ftr_r = std::dynamic_pointer_cast<FeatureGnssSatellite>(ts_ftr_r_it->second);
+                    assert(ftr_r != nullptr);
+
+                    // dt
+                    double dt = ftr_k->getCapture()->getTimeStamp() - ts_ftr_r_it->first;
+
+                    // discard incomming-last and last-last
+                    if (dt < 0 or ftr_k == ftr_r)
+                        continue;
+
+                    // within time window
+                    if (dt > params_tracker_gnss_->tdcp_params.time_window)
+                        break;
+
+                    // discard removing Frame/capture/feature
+                    if (ftr_r->isRemoving() or ftr_r->getCapture()->isRemoving() or ftr_r->getCapture()->getFrame()->isRemoving())
+                        continue;
+
+                    // check valid measurement
+                    assert(std::abs(ftr_r->getObservation().L[0]) > 1e-12);
+
+                    WOLF_DEBUG("previous feature at KF: ", ftr_r->getCapture()->getFrame()->id(), " sat: ", ftr_r->satNumber(), " id: ", ftr_r->id());
+
+                    // emplace tdcp factor
+                    auto factor_status = FAC_ACTIVE;
+                    if (params_tracker_gnss_->tdcp_structure == "first-all")
+                        factor_status = FAC_INACTIVE;
+                    double var_tdcp = dt * std::pow(params_tracker_gnss_->tdcp_params.sigma_atm,2) + std::pow(params_tracker_gnss_->tdcp_params.sigma_carrier,2);
+                    auto new_fac = FactorBase::emplace<FactorGnssTdcp>(ftr_k,
+                                                                       sqrt(var_tdcp),
+                                                                       ftr_r,
+                                                                       ftr_k,
+                                                                       sensor_gnss_,
+                                                                       shared_from_this(),
+                                                                       params_tracker_gnss_->tdcp_params.loss_function,
+                                                                       factor_status);
+                    new_factors.push_back(new_fac);
+
+                    // WOLF_DEBUG( "Factor: track: " , feature_in_last->trackId(),
+                    //             " origin: "       , feature_in_origin->id() ,
+                    //             " from last: "    , feature_in_last->id() );
+
+                    // only first (with previous frame) factor in "consecutive" structure
+                    if (params_tracker_gnss_->tdcp_structure == "consecutive")
+                        break;
+                }
+                // only last (with first frame) factor in "first-all" structure
+                if (params_tracker_gnss_->tdcp_structure == "first-all")
+                    new_factors.back()->setStatus(FAC_ACTIVE);
+
+                WOLF_DEBUG("All TDCP factors emplaced!");
+            }
+        }
+    }
+
+    // remove outliers
+    if (!new_factors.empty() and (params_tracker_gnss_->remove_outliers or params_tracker_gnss_->remove_outliers_tdcp))
+        removeOutliers(new_factors, last_ptr_);
+
+    //getProblem()->print(4, 1, 1, 1);
+
+    WOLF_DEBUG("ProcessorTrackerGnss::establishFactors done");
+}
+
+void ProcessorTrackerGnss::advanceDerived()
+{
+    ProcessorTrackerFeature::advanceDerived();
+
+    untracked_last_features_ = std::move(untracked_incoming_features_);
+    fix_last_ = fix_incoming_;
+}
+
+void ProcessorTrackerGnss::resetDerived()
+{
+    ProcessorTrackerFeature::resetDerived();
+
+    untracked_last_features_ = std::move(untracked_incoming_features_);
+    fix_last_ = fix_incoming_;
+}
+
+void ProcessorTrackerGnss::postProcess()
+{
+    // Fix orientation of last frame if no other processors
+    if (getSensor()->getHardware()->getSensorList().size() == 1)
+        getProblem()->getLastFrame()->getO()->fix();
+}
+
+void ProcessorTrackerGnss::removeOutliers(FactorBasePtrList fac_list, CaptureBasePtr cap)
+{
+    WOLF_DEBUG("ProcessorTrackerGnss::removeOutliers");
+
+    FactorBasePtrList remove_fac;
+
+    //WOLF_DEBUG( "PR ", getName()," rejectOutlier...");
+
+    // PseudoRange states
+    Eigen::Vector3d x;
+    Eigen::Vector4d o;
+    Eigen::Vector1d clock_bias;
+    Eigen::Vector1d clock_bias_glo;
+    Eigen::Vector1d clock_bias_gal;
+    Eigen::Vector1d clock_bias_cmp;
+    Eigen::Vector3d x_antena_pr;
+
+
+    // state for pseudoranges is fix solution if OK
+    if (cap == last_ptr_ and fix_last_.stat != 0 and params_tracker_gnss_->remove_outliers_with_fix)
+    {
+        WOLF_DEBUG("OUTLIERS COMPUTED USING fix_last");
+        x = sensor_gnss_->getREnuMap().transpose() * (sensor_gnss_->getREnuEcef() * fix_last_.pos + sensor_gnss_->gettEnuEcef() - sensor_gnss_->gettEnuMap());
+        o << 0,0,0,1;
+        clock_bias <<  CLIGHT * fix_last_.rcv_bias(0);
+        clock_bias_glo << CLIGHT * fix_last_.rcv_bias(1);
+        clock_bias_gal << CLIGHT * fix_last_.rcv_bias(2);
+        clock_bias_cmp << CLIGHT * fix_last_.rcv_bias(3);
+        x_antena_pr = Eigen::Vector3d::Zero();
+
+        //std::cout << "x:              " << x.transpose() << std::endl;
+        //std::cout << "o:              " << o.transpose() << std::endl;
+        //std::cout << "clock_bias:     " << clock_bias << std::endl;
+        //std::cout << "clock_bias_glo: " << clock_bias_glo << std::endl;
+        //std::cout << "clock_bias_gal: " << clock_bias_gal << std::endl;
+        //std::cout << "clock_bias_cmp: " << clock_bias_cmp << std::endl;
+        //std::cout << "x_antena_pr:    " << x_antena_pr.transpose() << std::endl;
+        //std::cout << "frame p:        " << cap->getFrame()->getP()->getState().transpose() << std::endl;
+        //std::cout << "frame o:        " << cap->getFrame()->getO()->getState().transpose() << std::endl;
+        //std::cout << "sb clock:       " << cap->getStateBlock('T')->getState() << std::endl;
+        //std::cout << "sb clock glo:   " << cap->getStateBlock('G')->getState() << std::endl;
+        //std::cout << "sb clock gal:   " << cap->getStateBlock('E')->getState() << std::endl;
+        //std::cout << "sb clock cmp:   " << cap->getStateBlock('M')->getState() << std::endl;
+        //std::cout << "sb antena:      " << sensor_gnss_->getP()->getState().transpose() << std::endl;
+    }
+    else
+    {
+        x = cap->getFrame()->getP()->getState();
+        o = cap->getFrame()->getO()->getState();
+        clock_bias = cap->getStateBlock('T')->getState();
+        clock_bias_glo = cap->getStateBlock('G')->getState();
+        clock_bias_gal = cap->getStateBlock('E')->getState();
+        clock_bias_cmp = cap->getStateBlock('M')->getState();
+        x_antena_pr = sensor_gnss_->getP()->getState();
+    }
+
+    // TDCP states
+    Eigen::Vector3d x_k(cap->getFrame()->getP()->getState());
+    Eigen::Vector4d o_k(cap->getFrame()->getO()->getState());
+    Eigen::Vector3d x_r(cap->getFrame()->getP()->getState());
+    Eigen::Vector4d o_r(cap->getFrame()->getO()->getState());
+    Eigen::Vector1d clock_bias_k(cap->getStateBlock('T')->getState());
+    Eigen::Vector1d clock_bias_r(cap->getStateBlock('T')->getState());
+    Eigen::Vector3d x_antena(sensor_gnss_->getP()->getState());
+
+    // Common states
+    Eigen::Vector3d t_ENU_map(sensor_gnss_->getEnuMapTranslation()->getState());
+    Eigen::Vector1d roll_ENU_map(sensor_gnss_->getEnuMapRoll()->getState());
+    Eigen::Vector1d pitch_ENU_map(sensor_gnss_->getEnuMapPitch()->getState());
+    Eigen::Vector1d yaw_ENU_map(sensor_gnss_->getEnuMapYaw()->getState());
+
+    //std::cout << "t_ENU_map:     " << t_ENU_map.transpose() << std::endl;
+    //std::cout << "roll_ENU_map:  " << roll_ENU_map << std::endl;
+    //std::cout << "pitch_ENU_map: " << pitch_ENU_map << std::endl;
+    //std::cout << "yaw_ENU_map:   " << yaw_ENU_map << std::endl;
+
+    // create double* array of a copy of the state for pseudo range factors
+    double* parameters_glo_pr[9] = {x.data(),
+                                    o.data(),
+                                    clock_bias.data(),
+                                    clock_bias_glo.data(),
+                                    x_antena_pr.data(),
+                                    t_ENU_map.data(),
+                                    roll_ENU_map.data(),
+                                    pitch_ENU_map.data(),
+                                    yaw_ENU_map.data()};
+    double* parameters_gal_pr[9] = {x.data(),
+                                    o.data(),
+                                    clock_bias.data(),
+                                    clock_bias_gal.data(),
+                                    x_antena_pr.data(),
+                                    t_ENU_map.data(),
+                                    roll_ENU_map.data(),
+                                    pitch_ENU_map.data(),
+                                    yaw_ENU_map.data()};
+    double* parameters_cmp_pr[9] = {x.data(),
+                                    o.data(),
+                                    clock_bias.data(),
+                                    clock_bias_cmp.data(),
+                                    x_antena_pr.data(),
+                                    t_ENU_map.data(),
+                                    roll_ENU_map.data(),
+                                    pitch_ENU_map.data(),
+                                    yaw_ENU_map.data()};
+    double* parameters_tdcp[11] = {x_r.data(),
+                                   o_r.data(),
+                                   clock_bias_r.data(),
+                                   x_k.data(),
+                                   o_k.data(),
+                                   clock_bias_k.data(),
+                                   x_antena.data(),
+                                   t_ENU_map.data(),
+                                   roll_ENU_map.data(),
+                                   pitch_ENU_map.data(),
+                                   yaw_ENU_map.data()};
+
+    // create residuals
+    double residual;
+
+    for (auto fac : fac_list)
+    {
+        // PSEUDO RANGE FACTORS
+        auto fac_pr = std::dynamic_pointer_cast<FactorGnssPseudoRange>(fac);
+        auto ftr_sat = std::static_pointer_cast<FeatureGnssSatellite>(fac->getFeature());
+        int sat = ftr_sat->satNumber();
+
+        if (fac_pr)
+        {
+            // evaluate the factor in this state
+            switch (ftr_sat->getSatellite().sys)
+            {
+                case SYS_GLO:
+                    fac_pr->evaluate(parameters_glo_pr, &residual, nullptr);
+                    break;
+                case SYS_GAL:
+                    fac_pr->evaluate(parameters_gal_pr, &residual, nullptr);
+                    break;
+                case SYS_CMP:
+                    fac_pr->evaluate(parameters_cmp_pr, &residual, nullptr);
+                    break;
+                default:
+                    fac_pr->evaluate(parameters_glo_pr, &residual, nullptr);
+                    break;
+            }
+
+            // RTKLIB FIX error
+            //int sat = std::static_pointer_cast<FeatureGnssSatellite>(fac->getFeature())->getSatellite().sat;
+            //assert(fix_last_.prange_residuals.count(sat) && "sat not used when computing fix!");
+            //WOLF_DEBUG("FactorGnssPseudoRange error = ", fac->getMeasurementSquareRootInformationUpper().inverse()*residual);
+            //WOLF_DEBUG("RTKLIB pntpos error         = ", fix_last_.prange_residuals.at(sat));
+
+            // discard if residual too high evaluated at the current estimation
+            if (std::abs(residual) > params_tracker_gnss_->outlier_residual_th)
+            {
+                WOLF_WARN("Discarding FactorGnssPseudoRange, considered OUTLIER");
+                WOLF_TRACE("Feature: ", fac->getMeasurement(),"\nError: ",fac->getMeasurementSquareRootInformationUpper().inverse()*residual);
+                remove_fac.push_back(fac_pr);
+                // store for statistics
+                outliers_pseudorange_++;
+                if (not sat_outliers_.count(sat))
+                    sat_outliers_.emplace(sat, 1);
+                else
+                    sat_outliers_[sat]++;
+
+            }
+            else
+                inliers_pseudorange_++;
+        }
+        // TDCP FACTORS
+        auto fac_tdcp = std::dynamic_pointer_cast<FactorGnssTdcp>(fac);
+        if (fac_tdcp and params_tracker_gnss_->remove_outliers_tdcp)
+        {
+            // update ref frame
+            x_r = fac_tdcp->getCaptureOther()->getFrame()->getP()->getState();
+            o_r = fac_tdcp->getCaptureOther()->getFrame()->getO()->getState();
+            clock_bias_r = fac_tdcp->getCaptureOther()->getStateBlock('T')->getState();
+            parameters_tdcp[0] = x_r.data();
+            parameters_tdcp[1] = o_r.data();
+            parameters_tdcp[2] = clock_bias_r.data();
+
+            // evaluate
+            fac_tdcp->evaluate(parameters_tdcp, &residual, nullptr);
+
+            //WOLF_DEBUG("FactorGnssTdcp with residual = ", residual);
+
+            // discard if residual too high evaluated at the current estimation
+            if (std::abs(residual) > params_tracker_gnss_->outlier_residual_th)
+            {
+                WOLF_WARN("Discarding FactorGnssTdcp, considered OUTLIER");
+                WOLF_TRACE("Residual: ", residual,"\nError: ",fac->getMeasurementSquareRootInformationUpper().inverse()*residual);
+                remove_fac.push_back(fac_tdcp);
+                // store for statistics
+                outliers_tdcp_++;
+                if (not sat_outliers_.count(sat))
+                    sat_outliers_.emplace(sat, 1);
+                else
+                    sat_outliers_[sat]++;
+            }
+            else
+                inliers_tdcp_++;
+        }
+    }
+
+    // remove outliers
+    for (auto fac : remove_fac)
+    {
+        //assert(false);
+        fac->remove();
+    }
+    WOLF_INFO_COND(not remove_fac.empty(),
+                   "ProcessorTrackerGnss::removeOutliers:",
+                   "\n\tPseudorange: ", outliers_pseudorange_,
+                   "\t( ", (100.0 * outliers_pseudorange_)/(outliers_pseudorange_+inliers_pseudorange_), " %)");
+    if (not remove_fac.empty() and
+            params_tracker_gnss_->tdcp_enabled and
+            params_tracker_gnss_->remove_outliers_tdcp)
+        std::cout << "\tTDCP:        "
+                  << outliers_tdcp_
+                  << "\t( "
+                  << (100.0 * outliers_tdcp_)/(outliers_tdcp_+inliers_tdcp_)
+                  << " \%)\n";
+    //std::cout << "\tsats:";
+    //for (auto sat_out : sat_outliers_)
+    //{
+    //    const int& sat = sat_out.first;
+    //    int sys = satsys(sat,NULL);
+    //    const unsigned int& outliers = sat_out.second;
+    //    std::cout << "\t\tsat " << sat << "(" << sys << "): " << outliers << std::endl;
+    //}
+}
+
+} // namespace wolf
+
+// Register in the FactoryProcessor
+#include "core/processor/factory_processor.h"
+namespace wolf {
+WOLF_REGISTER_PROCESSOR(ProcessorTrackerGnss)
+WOLF_REGISTER_PROCESSOR_AUTO(ProcessorTrackerGnss)
+} // namespace wolf
diff --git a/src/sensor/sensor_gnss.cpp b/src/sensor/sensor_gnss.cpp
index 5beaf5097..6e8d73c39 100644
--- a/src/sensor/sensor_gnss.cpp
+++ b/src/sensor/sensor_gnss.cpp
@@ -1,59 +1,89 @@
+//--------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 "gnss/sensor/sensor_gnss.h"
 #include "core/state_block/state_block.h"
-#include "core/state_block/state_quaternion.h"
+#include "core/state_block/state_angle.h"
+#include "gnss_utils/gnss_utils.h"
+#include "gnss_utils/utils/transformations.h"
 
 namespace wolf {
 
-// Geodetic system parameters
-static Scalar kSemimajorAxis = 6378137;
-static Scalar kSemiminorAxis = 6356752.3142;
-static Scalar kFirstEccentricitySquared = 6.69437999014 * 0.001;
-static Scalar kSecondEccentricitySquared = 6.73949674228 * 0.001;
-//static Scalar kFlattening = 1 / 298.257223563;
-
-SensorGnss::SensorGnss(StateBlockPtr _p_ptr,             //GNSS antena 3D position with respect to the car frame (base frame)
-                       StateBlockPtr _bias_ptr,          //GNSS sensor time bias
-                       IntrinsicsGnssPtr params)         //sensor params
+SensorGnss::SensorGnss(const Eigen::VectorXd& _extrinsics,
+                       const ParamsSensorGnssPtr& _params)
         :
-        SensorBase("SensorGnss", _p_ptr, nullptr, _bias_ptr, 0), // antena orientation has no sense in GNSS
-        params_(params),
+        SensorBase("SensorGnss",
+                   std::make_shared<StateBlock>(_extrinsics,_params->extrinsics_fixed),
+                   nullptr, // antena orientation has no sense in GNSS
+                   nullptr, // SensorParams are set afterwards
+                   0),
+        params_(_params),
         ENU_defined_(false),
-        ENU_MAP_initialized_(false)
+        t_ENU_MAP_initialized_(false),
+        R_ENU_MAP_initialized_(false),
+        R_ENU_ECEF_(Eigen::Matrix3d::Identity()),
+        t_ENU_ECEF_(Eigen::Vector3d::Zero())
 {
-    assert(_p_ptr->getSize() == 3 && "Bad extrinsics size");
-    assert((_bias_ptr == nullptr || _bias_ptr->getSize() == 1 )&& "Bad bias size");
-
-    getStateBlockVec().resize(7);
-    setStateBlockPtrStatic(3, std::make_shared<StateBlock>(3, params_->translation_fixed_));
-    setStateBlockPtrStatic(4, std::make_shared<StateBlock>(1, params_->roll_fixed_));
-    setStateBlockPtrStatic(5, std::make_shared<StateBlock>(1, params_->pitch_fixed_));
-    setStateBlockPtrStatic(6, std::make_shared<StateBlock>(1, params_->yaw_fixed_));
-}
-
-SensorGnss::SensorGnss(StateBlockPtr _p_ptr,             //GNSS sensor position with respect to the car frame (base frame)
-                       StateBlockPtr _bias_ptr,          //GNSS sensor time bias
-                       IntrinsicsGnssPtr params,         //sensor params
-                       StateBlockPtr _t_ENU_MAP_ptr,     //ENU_MAP translation
-                       StateBlockPtr _roll_ENU_MAP_ptr,  //ENU_MAP Roll
-                       StateBlockPtr _pitch_ENU_MAP_ptr, //ENU_MAP Pitch
-                       StateBlockPtr _yaw_ENU_MAP_ptr)   //ENU_MAP Yaw
-        :
-        SensorBase("SensorGnss", _p_ptr, nullptr, _bias_ptr, 0),// antena orientation has no sense in GNSS
-        params_(params),
-        ENU_defined_(false),
-        ENU_MAP_initialized_(false)
-{
-    assert(_p_ptr->getSize() == 3 && "Bad extrinsics size");
-    assert((_bias_ptr == nullptr || _bias_ptr->getSize() == 1 )&& "Bad bias size");
-    assert(_roll_ENU_MAP_ptr->getSize() == 1 && "Bad ENU_MAP roll size");
-    assert(_pitch_ENU_MAP_ptr->getSize() == 1 && "Bad ENU_MAP pitch size");
-    assert(_yaw_ENU_MAP_ptr->getSize() == 1 && "Bad ENU_MAP yaw size");
-
-    getStateBlockVec().resize(7);
-    setStateBlockPtrStatic(3, _t_ENU_MAP_ptr);
-    setStateBlockPtrStatic(4, _roll_ENU_MAP_ptr);
-    setStateBlockPtrStatic(5, _pitch_ENU_MAP_ptr);
-    setStateBlockPtrStatic(6, _yaw_ENU_MAP_ptr);
+    assert(_extrinsics.size() == 3 && "Bad extrinsics size");
+
+    // STATE BLOCKS
+    // ENU-MAP
+    addStateBlock('t', std::make_shared<StateBlock>(3, params_->translation_fixed), false);
+    addStateBlock('r', std::make_shared<StateAngle>(0.0, params_->roll_fixed), false);
+    addStateBlock('p', std::make_shared<StateAngle>(0.0, params_->pitch_fixed), false);
+    addStateBlock('y', std::make_shared<StateAngle>(0.0, params_->yaw_fixed), false);
+    // clock bias
+    addStateBlock(CLOCK_BIAS_KEY, std::make_shared<StateBlock>(1,false), true); // receiver clock bias
+    addStateBlock(CLOCK_BIAS_GPS_GLO_KEY, std::make_shared<StateBlock>(1,true), params_->clock_bias_GPS_GLO_dynamic); // GPS-GLO clock bias
+    addStateBlock(CLOCK_BIAS_GPS_GAL_KEY, std::make_shared<StateBlock>(1,true), params_->clock_bias_GPS_GAL_dynamic); // GPS-GAL clock bias
+    addStateBlock(CLOCK_BIAS_GPS_CMP_KEY, std::make_shared<StateBlock>(1,true), params_->clock_bias_GPS_CMP_dynamic); // GPS-CMP clock bias
+
+    // ENU-ECEF
+    // Mode "manual": ENU provided via params
+    if (params_->ENU_mode == "manual")
+        setEcefEnu(params_->ENU_latlonalt, false);
+    // Mode "ECEF": ENU = ECEF (disabling this coordinates system)
+    if (params_->ENU_mode == "ECEF")
+        setEnuEcef(Eigen::Matrix3d::Identity(),Eigen::Vector3d::Zero());
+
+    // PRIORS
+    // prior to ENU-MAP (avoid non-observable problem)
+    if (params_->ENU_mode != "ECEF")
+    {
+        if (not params_->translation_fixed)
+            addPriorParameter('t', Eigen::Vector3d::Zero(), 10*Eigen::Matrix3d::Identity());
+        if (not params_->roll_fixed)
+            addPriorParameter('r', Eigen::Vector1d::Zero(), M_PI*Eigen::Matrix1d::Identity());
+        if (not params_->pitch_fixed)
+            addPriorParameter('p', Eigen::Vector1d::Zero(), M_PI*Eigen::Matrix1d::Identity());
+        if (not params_->yaw_fixed)
+            addPriorParameter('y', Eigen::Vector1d::Zero(), M_PI*Eigen::Matrix1d::Identity());
+    }
+    // prior to inter-constellation clock bias (avoid non-observable problem)
+    if (not params_->clock_bias_GPS_GLO_dynamic)
+        addPriorParameter(CLOCK_BIAS_GPS_GLO_KEY, Eigen::Vector1d::Zero(), Eigen::Matrix1d::Identity());
+    if (not params_->clock_bias_GPS_GAL_dynamic)
+        addPriorParameter(CLOCK_BIAS_GPS_GAL_KEY, Eigen::Vector1d::Zero(), Eigen::Matrix1d::Identity());
+    if (not params_->clock_bias_GPS_CMP_dynamic)
+        addPriorParameter(CLOCK_BIAS_GPS_CMP_KEY, Eigen::Vector1d::Zero(), Eigen::Matrix1d::Identity());
 }
 
 SensorGnss::~SensorGnss()
@@ -61,100 +91,75 @@ SensorGnss::~SensorGnss()
     //
 }
 
-void SensorGnss::computeEnuEcef(const Eigen::Vector3s& _t_ECEF_ENU, Eigen::Matrix3s& R_ENU_ECEF, Eigen::Vector3s& t_ENU_ECEF) const
+void SensorGnss::setEcefEnu(const Eigen::Vector3d& _ENU, bool _ECEF_coordinates)
 {
-    // Convert ECEF coordinates to geodetic coordinates.
-    // J. Zhu, "Conversion of Earth-centered Earth-fixed coordinates
-    // to geodetic coordinates," IEEE Transactions on Aerospace and
-    // Electronic Systems, vol. 30, pp. 957-961, 1994.
-
-    Scalar r = std::sqrt(_t_ECEF_ENU(0) * _t_ECEF_ENU(0) + _t_ECEF_ENU(1) * _t_ECEF_ENU(1));
-    Scalar Esq = kSemimajorAxis * kSemimajorAxis - kSemiminorAxis * kSemiminorAxis;
-    Scalar F = 54 * kSemiminorAxis * kSemiminorAxis * _t_ECEF_ENU(2) * _t_ECEF_ENU(2);
-    Scalar G = r * r + (1 - kFirstEccentricitySquared) * _t_ECEF_ENU(2) * _t_ECEF_ENU(2) - kFirstEccentricitySquared * Esq;
-    Scalar C = (kFirstEccentricitySquared * kFirstEccentricitySquared * F * r * r) / pow(G, 3);
-    Scalar S = cbrt(1 + C + sqrt(C * C + 2 * C));
-    Scalar P = F / (3 * pow((S + 1 / S + 1), 2) * G * G);
-    Scalar Q = sqrt(1 + 2 * kFirstEccentricitySquared * kFirstEccentricitySquared * P);
-    Scalar r_0 = -(P * kFirstEccentricitySquared * r) / (1 + Q)
-                 + sqrt(0.5 * kSemimajorAxis * kSemimajorAxis * (1 + 1.0 / Q)
-                 - P * (1 - kFirstEccentricitySquared) * _t_ECEF_ENU(2) * _t_ECEF_ENU(2) / (Q * (1 + Q)) - 0.5 * P * r * r);
-    Scalar V = sqrt(pow((r - kFirstEccentricitySquared * r_0), 2) + (1 - kFirstEccentricitySquared) * _t_ECEF_ENU(2) * _t_ECEF_ENU(2));
-    Scalar Z_0 = kSemiminorAxis * kSemiminorAxis * _t_ECEF_ENU(2) / (kSemimajorAxis * V);
-
-    Scalar latitude = atan((_t_ECEF_ENU(2) + kSecondEccentricitySquared * Z_0) / r);
-    Scalar longitude = atan2(_t_ECEF_ENU(1), _t_ECEF_ENU(0));
-
-    Scalar sLat = sin(latitude);
-    Scalar cLat = cos(latitude);
-    Scalar sLon = sin(longitude);
-    Scalar cLon = cos(longitude);
-
-    R_ENU_ECEF(0,0) = -sLon;
-    R_ENU_ECEF(0,1) =  cLon;
-    R_ENU_ECEF(0,2) =  0.0;
-
-    R_ENU_ECEF(1,0) = -sLat*cLon;
-    R_ENU_ECEF(1,1) = -sLat * sLon;
-    R_ENU_ECEF(1,2) =  cLat;
-
-    R_ENU_ECEF(2,0) =  cLat * cLon;
-    R_ENU_ECEF(2,1) =  cLat * sLon;
-    R_ENU_ECEF(2,2) =  sLat;
-
-    t_ENU_ECEF = -R_ENU_ECEF*_t_ECEF_ENU;
-}
+    WOLF_WARN_COND(ENU_defined_,"setEnuEcef: ENU already defined!");
+    assert(!(ENU_defined_ && params_->ENU_mode=="ECEF") && "ENU cannot be redefined if set to ECEF");
+
+    if (_ECEF_coordinates)
+        GnssUtils::computeEnuEcefFromEcef(_ENU, R_ENU_ECEF_, t_ENU_ECEF_);
+    else
+        GnssUtils::computeEnuEcefFromEcef(GnssUtils::latLonAltToEcef(_ENU), R_ENU_ECEF_, t_ENU_ECEF_);
 
-void SensorGnss::setEcefEnu(const Eigen::Vector3s& _t_ECEF_ENU)
-{
-    computeEnuEcef(_t_ECEF_ENU, R_ENU_ECEF_, t_ENU_ECEF_);
     ENU_defined_ = true;
 
-    WOLF_INFO("SensorGnss: ECEF-ENU initialized.")
+    WOLF_INFO("SensorGnss: ECEF-ENU set.")
 }
 
-void SensorGnss::setEnuEcef(const Eigen::Matrix3s& _R_ENU_ECEF, const Eigen::Vector3s& _t_ENU_ECEF)
+void SensorGnss::setEnuEcef(const Eigen::Matrix3d& _R_ENU_ECEF, const Eigen::Vector3d& _t_ENU_ECEF)
 {
+    WOLF_WARN_COND(ENU_defined_,"setEnuEcef: ENU already defined!");
+    assert(!(ENU_defined_ && params_->ENU_mode=="ECEF") && "ENU cannot be redefined if set to ECEF");
+
     R_ENU_ECEF_ = _R_ENU_ECEF;
     t_ENU_ECEF_ = _t_ENU_ECEF;
     ENU_defined_ = true;
 }
 
-void SensorGnss::initializeEnuMap(const Eigen::VectorXs& _pose_MAP_frameENU, const Eigen::Vector3s& _t_ECEF_antenaENU,
-                                  const Eigen::VectorXs& _pose_MAP_frame2, const Eigen::Vector3s& _t_ECEF_antena2)
+void SensorGnss::initializeEnuMap(const Eigen::VectorXd& _pose_MAP_frameENU, const Eigen::Vector3d& _t_ECEF_antenaENU,
+                                  const Eigen::VectorXd& _pose_MAP_frame2, const Eigen::Vector3d& _t_ECEF_antena2)
 {
-    Eigen::Matrix3s R_ENU_ECEF;//, R_ENU2_ECEF;
-    Eigen::Vector3s t_ENU_ECEF;//, t_ENU2_ECEF;
-    computeEnuEcef(_t_ECEF_antenaENU, R_ENU_ECEF, t_ENU_ECEF);
-    //computeENU_ECEF(_t_ECEF_antena2, R_ENU2_ECEF, t_ENU2_ECEF);
+    assert(ENU_defined_ && "initializing ENU-MAP before setting ENU");
 
     // compute fix vector (from 1 to 2) in ENU coordinates
-    Eigen::Vector3s v_ENU = R_ENU_ECEF * (_t_ECEF_antena2 - _t_ECEF_antenaENU);
+    Eigen::Vector3d v_ENU = R_ENU_ECEF_ * (_t_ECEF_antena2 - _t_ECEF_antenaENU);
 
-    // 2D
+    // 2d
     if (getProblem()->getDim() == 2)
     {
+        WOLF_INFO("SensorGnss: initializing ENU-MAP 2D case....");
+
         // compute antena vector (from 1 to 2) in MAP
-        Eigen::Vector2s t_MAP_antenaENU = _pose_MAP_frameENU.head<2>() + Eigen::Rotation2D<Scalar>(_pose_MAP_frameENU(2)) * getP()->getState().head<2>();
-        Eigen::Vector2s t_MAP_antena2 = _pose_MAP_frame2.head<2>() + Eigen::Rotation2D<Scalar>(_pose_MAP_frame2(2)) * getP()->getState().head<2>();
-        Eigen::Vector2s v_MAP =  t_MAP_antena2 - t_MAP_antenaENU;
-
-        // initialize yaw
-        Scalar theta_ENU = atan2(v_ENU(1),v_ENU(0));
-        Scalar theta_MAP = atan2(v_MAP(1),v_MAP(0));
-        Scalar yaw_ENU_MAP = theta_ENU-theta_MAP;
-        setEnuMapYawState(yaw_ENU_MAP);
-
-        // initialize translation
-        Eigen::Vector3s t_ENU_MAP(Eigen::Vector3s::Zero());
-        Eigen::Matrix2s R_ENU_MAP = computeREnuMap(getEnuMapRoll()->getState()(0),
-                                                   getEnuMapPitch()->getState()(0),
-                                                   getEnuMapYaw()->getState()(0)).topLeftCorner<2,2>();
+        Eigen::Vector2d t_MAP_antenaENU = _pose_MAP_frameENU.head<2>() + Eigen::Rotation2D<double>(_pose_MAP_frameENU(2)) * getP()->getState().head<2>();
+        Eigen::Vector2d t_MAP_antena2 = _pose_MAP_frame2.head<2>() + Eigen::Rotation2D<double>(_pose_MAP_frame2(2)) * getP()->getState().head<2>();
+        Eigen::Vector2d v_MAP =  t_MAP_antena2 - t_MAP_antenaENU;
+
+        // ENU-MAP Rotation
+        Eigen::Matrix2d R_ENU_MAP;
+        // get it if already initialized
+        if (R_ENU_MAP_initialized_)
+        	R_ENU_MAP = getREnuMap().topLeftCorner<2,2>();
+		// initialize yaw if not initialized
+        else
+        {
+			double theta_ENU = atan2(v_ENU(1),v_ENU(0));
+			double theta_MAP = atan2(v_MAP(1),v_MAP(0));
+			double yaw_ENU_MAP = theta_ENU-theta_MAP;
+			setEnuMapYawState(yaw_ENU_MAP);
+
+			R_ENU_MAP = computeREnuMap(getEnuMapRoll()->getState()(0),
+									   getEnuMapPitch()->getState()(0),
+									   getEnuMapYaw()->getState()(0)).topLeftCorner<2,2>();
+
+	        WOLF_INFO("SensorGnss: ENU-MAP rotation initialized.");
+        }
+		// ENU-MAP translation
+		Eigen::Vector3d t_ENU_MAP(Eigen::Vector3d::Zero());
         t_ENU_MAP.head<2>() = -R_ENU_MAP * t_MAP_antenaENU;
-
-        // set translation state
         setEnuMapTranslationState(t_ENU_MAP);
 
+        WOLF_INFO("SensorGnss: ENU-MAP translation initialized.");
+
         //std::cout << "-----------------------------------------\n";
         //std::cout << "t_ECEF_antenaENU: " << _t_ECEF_antenaENU.transpose() << std::endl;
         //std::cout << "t_ECEF_antena2: " << _t_ECEF_antena2.transpose() << std::endl;
@@ -167,111 +172,87 @@ void SensorGnss::initializeEnuMap(const Eigen::VectorXs& _pose_MAP_frameENU, con
         //std::cout << "yaw set: " << yaw << std::endl;
         //std::cout << "t_ENU1_origin: " << t_ENU_MAP.transpose() << std::endl;
         //std::cout << "-----checks\n";
-        //std::cout << "R(-yaw) * v_ENU: " << (Eigen::Rotation2D<Scalar>(-yaw) * v_ENU.head<2>()).transpose() << std::endl;
+        //std::cout << "R(-yaw) * v_ENU: " << (Eigen::Rotation2D<double>(-yaw) * v_ENU.head<2>()).transpose() << std::endl;
         //std::cout << "should be: " << v_MAP.transpose() << std::endl;
-        //std::cout << "atan2(R(-yaw) * v_ENU): " << atan2((Eigen::Rotation2D<Scalar>(-yaw) * v_ENU.head<2>())(1), (Eigen::Rotation2D<Scalar>(-yaw) * v_ENU.head<2>())(0)) << std::endl;
+        //std::cout << "atan2(R(-yaw) * v_ENU): " << atan2((Eigen::Rotation2D<double>(-yaw) * v_ENU.head<2>())(1), (Eigen::Rotation2D<double>(-yaw) * v_ENU.head<2>())(0)) << std::endl;
         //std::cout << "should be: " << atan2(v_MAP(1),v_MAP(0)) << std::endl;
-        //std::cout << "R(yaw) * v_MAP: " << (Eigen::Rotation2D<Scalar>(yaw) * v_MAP).transpose() << std::endl;
+        //std::cout << "R(yaw) * v_MAP: " << (Eigen::Rotation2D<double>(yaw) * v_MAP).transpose() << std::endl;
         //std::cout << "should be: " << v_ENU.head<2>().transpose() << std::endl;
-        //std::cout << "atan2(R(yaw) * v_MAP): " << atan2((Eigen::Rotation2D<Scalar>(yaw) * v_MAP)(1),(Eigen::Rotation2D<Scalar>(yaw) * v_MAP)(0)) << std::endl;
+        //std::cout << "atan2(R(yaw) * v_MAP): " << atan2((Eigen::Rotation2D<double>(yaw) * v_MAP)(1),(Eigen::Rotation2D<double>(yaw) * v_MAP)(0)) << std::endl;
         //std::cout << "should be: " << atan2(v_ENU(1),v_ENU(0)) << std::endl;
         //std::cout << "v_ENU.norm(): " << v_ENU.norm() << std::endl;
         //std::cout << "v_MAP.norm(): " << v_MAP.norm() << std::endl;
-
-        WOLF_INFO("SensorGnss: ENU-MAP initialized.")
     }
-    // 3D
+    // 3d
     else
     {
         //TODO
-        std::runtime_error("not implemented yet");
+        WOLF_WARN("initialization of ENU-MAP not implemented in 3D")
+        //throw std::runtime_error("not implemented yet");
     }
 }
 
-void SensorGnss::initializeEnuMapYaw(const Eigen::VectorXs& _pose_MAP_frame1, const Eigen::VectorXs& _pose_MAP_frame2,
-                                     const Eigen::Vector3s& _v_ECEF_antena1_antena2)
+void SensorGnss::initializeEnuMapYaw(const Eigen::VectorXd& _pose_MAP_frame1, const Eigen::VectorXd& _pose_MAP_frame2,
+                                     const Eigen::Vector3d& _v_ECEF_antena1_antena2)
 {
     assert(ENU_defined_ && "initializing ENU-MAP yaw without ENU defined");
 
-    Eigen::Vector2s t_MAP_antena1 = _pose_MAP_frame1.head<2>() + Eigen::Rotation2D<Scalar>(_pose_MAP_frame1(2)) * getP()->getState().head<2>();
-    Eigen::Vector2s t_MAP_antena2 = _pose_MAP_frame2.head<2>() + Eigen::Rotation2D<Scalar>(_pose_MAP_frame2(2)) * getP()->getState().head<2>();
-    Eigen::Vector2s v_MAP_antena1_antena2 = t_MAP_antena2 - t_MAP_antena1;
-    Eigen::Vector3s v_ENU_antena1_antena2 = R_ENU_ECEF_ * _v_ECEF_antena1_antena2;
+    Eigen::Vector2d t_MAP_antena1 = _pose_MAP_frame1.head<2>() + Eigen::Rotation2D<double>(_pose_MAP_frame1(2)) * getP()->getState().head<2>();
+    Eigen::Vector2d t_MAP_antena2 = _pose_MAP_frame2.head<2>() + Eigen::Rotation2D<double>(_pose_MAP_frame2(2)) * getP()->getState().head<2>();
+    Eigen::Vector2d v_MAP_antena1_antena2 = t_MAP_antena2 - t_MAP_antena1;
+    Eigen::Vector3d v_ENU_antena1_antena2 = R_ENU_ECEF_ * _v_ECEF_antena1_antena2;
 
-    Scalar theta_ENU = atan2(v_ENU_antena1_antena2(1),v_ENU_antena1_antena2(0));
-    Scalar theta_MAP = atan2(v_MAP_antena1_antena2(1),v_MAP_antena1_antena2(0));
-    Scalar yaw_ENU_MAP = theta_ENU-theta_MAP;
+    double theta_ENU = atan2(v_ENU_antena1_antena2(1),v_ENU_antena1_antena2(0));
+    double theta_MAP = atan2(v_MAP_antena1_antena2(1),v_MAP_antena1_antena2(0));
+    double yaw_ENU_MAP = theta_ENU-theta_MAP;
 
     setEnuMapYawState(yaw_ENU_MAP);
+
+    WOLF_INFO("SensorGnss: ENU-MAP Rotation initialized.")
 }
 
-void SensorGnss::setEnuMapTranslationState(const Eigen::Vector3s& t_ENU_MAP)
+void SensorGnss::setEnuMapTranslationState(const Eigen::Vector3d& t_ENU_MAP)
 {
     getEnuMapTranslation()->setState(t_ENU_MAP);
 
-    ENU_MAP_initialized_ = true;
+    t_ENU_MAP_initialized_ = true;
 }
 
-void SensorGnss::setEnuMapRollState(const Scalar& roll_ENU_MAP)
+void SensorGnss::setEnuMapRollState(const double& roll_ENU_MAP)
 {
-    getEnuMapRoll()->setState(Eigen::Vector1s(roll_ENU_MAP));
+    getEnuMapRoll()->setState(Eigen::Vector1d(roll_ENU_MAP));
 
-    ENU_MAP_initialized_ = true;
+    R_ENU_MAP_initialized_ = true;
 }
 
-void SensorGnss::setEnuMapPitchState(const Scalar& pitch_ENU_MAP)
+void SensorGnss::setEnuMapPitchState(const double& pitch_ENU_MAP)
 {
-    getEnuMapPitch()->setState(Eigen::Vector1s(pitch_ENU_MAP));
+    getEnuMapPitch()->setState(Eigen::Vector1d(pitch_ENU_MAP));
 
-    ENU_MAP_initialized_ = true;
+    R_ENU_MAP_initialized_ = true;
 }
 
-void SensorGnss::setEnuMapYawState(const Scalar& yaw_ENU_MAP)
+void SensorGnss::setEnuMapYawState(const double& yaw_ENU_MAP)
 {
-    getEnuMapYaw()->setState(Eigen::Vector1s(yaw_ENU_MAP));
+    getEnuMapYaw()->setState(Eigen::Vector1d(yaw_ENU_MAP));
 
-    ENU_MAP_initialized_ = true;
-}
-
-Eigen::Matrix3s SensorGnss::getREnuMap() const
-{
-    return Eigen::Matrix3s(Eigen::AngleAxis<Scalar>(getEnuMapRoll() ->getState()(0), Eigen::Vector3s::UnitX()) *
-                           Eigen::AngleAxis<Scalar>(getEnuMapPitch()->getState()(0), Eigen::Vector3s::UnitY()) *
-                           Eigen::AngleAxis<Scalar>(getEnuMapYaw()  ->getState()(0), Eigen::Vector3s::UnitZ()));
+    R_ENU_MAP_initialized_ = true;
 }
 
-// Define the factory method
-SensorBasePtr SensorGnss::create(const std::string& _unique_name, const Eigen::VectorXs& _extrinsics,
-                              const IntrinsicsBasePtr _intrinsics)
+Eigen::Vector3d SensorGnss::computeFrameAntennaPosEcef(const FrameBasePtr& frm) const
 {
-    assert((_extrinsics.size() == 3 || _extrinsics.size() == 9) && "Bad extrinsics vector length. Should be 3 (antena position) or 9 (antena position and MAP-ENU initialization: position XYZ and orientation RPY)");
-
-    SensorBasePtr sen = nullptr;
-    IntrinsicsGnssPtr intrinsics_gnss_ptr = std::static_pointer_cast<IntrinsicsGnss>(_intrinsics);
-    StateBlockPtr p_ptr = std::make_shared<StateBlock>(_extrinsics.head(3), intrinsics_gnss_ptr->extrinsics_fixed_);
-
-    if (_extrinsics.size() == 3)
-        sen = std::make_shared<SensorGnss>(p_ptr, nullptr, intrinsics_gnss_ptr);
-    else
-    {
-        StateBlockPtr p_ptr             = std::make_shared<StateBlock>(_extrinsics.head<3>(),     intrinsics_gnss_ptr->extrinsics_fixed_);
-        StateBlockPtr translation_ptr   = std::make_shared<StateBlock>(_extrinsics.segment<3>(3), intrinsics_gnss_ptr->translation_fixed_);
-        StateBlockPtr roll_ptr          = std::make_shared<StateBlock>(_extrinsics.segment<1>(6), intrinsics_gnss_ptr->roll_fixed_);
-        StateBlockPtr pitch_ptr         = std::make_shared<StateBlock>(_extrinsics.segment<1>(7), intrinsics_gnss_ptr->pitch_fixed_);
-        StateBlockPtr yaw_ptr           = std::make_shared<StateBlock>(_extrinsics.segment<1>(8), intrinsics_gnss_ptr->yaw_fixed_);
-
-        sen = std::make_shared<SensorGnss>(p_ptr, nullptr, intrinsics_gnss_ptr, translation_ptr, roll_ptr, pitch_ptr, yaw_ptr);
-    }
+    assert(isEnuDefined());
+    assert(frm and frm->getP() and frm->getO());
 
-    sen->setName(_unique_name);
-    return sen;
+    return R_ENU_ECEF_.transpose() * (-t_ENU_ECEF_ + gettEnuMap() + getREnuMap() * (frm->getP()->getState() + Eigen::Quaterniond(Eigen::Vector4d(frm->getO()->getState())) * this->getP()->getState()));
 }
 
 } // namespace wolf
 
 
-// Register in the SensorFactory
-#include "core/sensor/sensor_factory.h"
+// Register in the FactorySensor
+#include "core/sensor/factory_sensor.h"
 namespace wolf {
-WOLF_REGISTER_SENSOR("SensorGnss",SensorGnss)
+WOLF_REGISTER_SENSOR(SensorGnss)
+WOLF_REGISTER_SENSOR_AUTO(SensorGnss)
 } // namespace wolf
diff --git a/src/tree_manager/tree_manager_sliding_window_tdcp.cpp b/src/tree_manager/tree_manager_sliding_window_tdcp.cpp
new file mode 100644
index 000000000..6d97ad40a
--- /dev/null
+++ b/src/tree_manager/tree_manager_sliding_window_tdcp.cpp
@@ -0,0 +1,66 @@
+//--------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 "gnss/tree_manager/tree_manager_sliding_window_tdcp.h"
+
+namespace wolf
+{
+
+TreeManagerSlidingWindowTdcp::TreeManagerSlidingWindowTdcp(ParamsTreeManagerSlidingWindowTdcpPtr _params) :
+            TreeManagerSlidingWindow(_params),
+            params_sw_sb_(_params)
+{
+    NodeBase::setType("TreeManagerSlidingWindowTdcp");
+};
+
+void TreeManagerSlidingWindowTdcp::keyFrameCallback(FrameBasePtr _key_frame)
+{
+    assert(getProblem() && "TreeManagerSlidingWindowTdcp::keyFrameCallback: problem not set.");
+
+    // store first frame
+    auto first_frame = getProblem()->getTrajectory()->getFirstFrame();
+
+    // call base sliding window tree manager
+    TreeManagerSlidingWindow::keyFrameCallback(_key_frame);
+
+    // if first frame was removed, activate all factors of new first frame
+    if (first_frame != getProblem()->getTrajectory()->getFirstFrame())
+    {
+        assert(first_frame->isRemoving());
+        for (auto fac : getProblem()->getTrajectory()->getFirstFrame()->getConstrainedByList())
+            if (fac and not fac->isRemoving() and
+                (fac->getType() == "FactorGnssTdcp" or
+                 fac->getType() == "FactorGnssTdcp2d" or
+                 fac->getType() == "FactorGnssTdcp3d" or
+                 fac->getType() == "FactorGnssTdcpBatch"))
+                fac->setStatus(FAC_ACTIVE);
+    }
+}
+
+} /* namespace wolf */
+
+// Register in the FactoryTreeManager
+#include "core/tree_manager/factory_tree_manager.h"
+namespace wolf {
+WOLF_REGISTER_TREE_MANAGER(TreeManagerSlidingWindowTdcp);
+WOLF_REGISTER_TREE_MANAGER_AUTO(TreeManagerSlidingWindowTdcp);
+} // namespace wolf
+
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 726d98fd4..fc829135d 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -1,22 +1,26 @@
 # Retrieve googletest from github & compile
 add_subdirectory(gtest)
 
-
 # Include gtest directory.
 include_directories(${GTEST_INCLUDE_DIRS})
 
 ############# USE THIS TEST AS AN EXAMPLE #################
 #                                                         #
 # Create a specific test executable for gtest_example     #
-wolf_add_gtest(gtest_example gtest_example.cpp)           #
-target_link_libraries(gtest_example ${PLUGIN_NAME})      #
+# wolf_add_gtest(gtest_example gtest_example.cpp)         #
+# target_link_libraries(gtest_example ${PLUGIN_NAME})     #
 #                                                         #
 ###########################################################
 
-# FactorGnssFix2D test
-wolf_add_gtest(gtest_factor_gnss_fix_2D gtest_factor_gnss_fix_2D.cpp)
-target_link_libraries(gtest_factor_gnss_fix_2D ${PLUGIN_NAME} ${wolf_LIBRARY})
+# FactorGnssFix2d test
+wolf_add_gtest(gtest_factor_gnss_fix_2d gtest_factor_gnss_fix_2d.cpp)
+target_link_libraries(gtest_factor_gnss_fix_2d ${PLUGIN_NAME})
+
+# FactorGnssPseudoRange test
+wolf_add_gtest(gtest_factor_gnss_pseudo_range gtest_factor_gnss_pseudo_range.cpp)
+target_link_libraries(gtest_factor_gnss_pseudo_range ${PLUGIN_NAME})
+
+# FactorGnssTdcp test
+wolf_add_gtest(gtest_factor_gnss_tdcp gtest_factor_gnss_tdcp.cpp)
+target_link_libraries(gtest_factor_gnss_tdcp ${PLUGIN_NAME})
 
-# FactorGnssSingleDiff2D test
-wolf_add_gtest(gtest_factor_gnss_single_diff_2D gtest_factor_gnss_single_diff_2D.cpp)
-target_link_libraries(gtest_factor_gnss_single_diff_2D ${PLUGIN_NAME} ${wolf_LIBRARY})
\ No newline at end of file
diff --git a/test/gtest_example.cpp b/test/gtest_example.cpp
index 140792d53..c73292612 100644
--- a/test/gtest_example.cpp
+++ b/test/gtest_example.cpp
@@ -1,3 +1,24 @@
+//--------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>
 
 TEST(TestTest, DummyTestExample)
diff --git a/test/gtest_factor_gnss_fix_2D.cpp b/test/gtest_factor_gnss_fix_2d.cpp
similarity index 51%
rename from test/gtest_factor_gnss_fix_2D.cpp
rename to test/gtest_factor_gnss_fix_2d.cpp
index 762e61fb4..a95688fd9 100644
--- a/test/gtest_factor_gnss_fix_2D.cpp
+++ b/test/gtest_factor_gnss_fix_2d.cpp
@@ -1,34 +1,53 @@
+//--------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--------
 /**
- * \file gtest_factor_gnss_fix_2D.cpp
+ * \file gtest_factor_gnss_fix_2d.cpp
  *
  *  Created on: Aug 1, 2018
  *      \author: jvallve
  */
 
-
-#include "gnss/factor/factor_gnss_fix_2D.h"
+#include <core/ceres_wrapper/solver_ceres.h>
 #include <core/utils/utils_gtest.h>
 
 #include "core/problem/problem.h"
 #include "gnss/sensor/sensor_gnss.h"
 #include "gnss/processor/processor_gnss_fix.h"
-
-#include "core/ceres_wrapper/ceres_manager.h"
-
+#include "gnss/capture/capture_gnss_fix.h"
+#include "gnss/factor/factor_gnss_fix_2d.h"
 
 using namespace Eigen;
 using namespace wolf;
 
 void resetStates(SensorGnssPtr gnss_sensor_ptr, FrameBasePtr frame_ptr,
-                 const Vector1s& o_enu_map,
-                 const Vector3s& t_base_antena,
-                 const Vector3s& t_enu_map,
-                 const Vector3s& t_map_base,
-                 const Vector1s& o_map_base)
+                 const Vector1d& o_enu_map,
+                 const Vector3d& t_base_antena,
+                 const Vector3d& t_enu_map,
+                 const Vector3d& t_map_base,
+                 const Vector1d& o_map_base)
 {
     // ENU-MAP
-    gnss_sensor_ptr->getEnuMapRoll()->setState(Eigen::Vector1s::Zero());
-    gnss_sensor_ptr->getEnuMapPitch()->setState(Eigen::Vector1s::Zero());
+    gnss_sensor_ptr->getEnuMapRoll()->setState(Eigen::Vector1d::Zero());
+    gnss_sensor_ptr->getEnuMapPitch()->setState(Eigen::Vector1d::Zero());
     gnss_sensor_ptr->getEnuMapYaw()->setState(o_enu_map);
     gnss_sensor_ptr->getEnuMapTranslation()->setState(t_enu_map);
     // Antena
@@ -52,52 +71,53 @@ void fixAllStates(SensorGnssPtr gnss_sensor_ptr, FrameBasePtr frame_ptr)
     frame_ptr->getO()->fix();
  }
 
-void computeParamSizes(const CeresManagerPtr& ceres_mgr_ptr, int& num_params_reduced, int& num_param_blocks_reduced )
+void computeParamSizes(const SolverCeresPtr& solver_ceres, int& num_params_reduced, int& num_param_blocks_reduced )
 {
     num_param_blocks_reduced = 0;
     num_params_reduced = 0;
 
-    std::vector<Scalar*> param_blocks;
-    ceres_mgr_ptr->getCeresProblem()->GetParameterBlocks(&param_blocks);
+    std::vector<double*> param_blocks;
+    solver_ceres->getCeresProblem()->GetParameterBlocks(&param_blocks);
 
     for (auto pb : param_blocks)
     {
         std::vector<ceres::ResidualBlockId> residual_blocks;
-        ceres_mgr_ptr->getCeresProblem()->GetResidualBlocksForParameterBlock(pb,&residual_blocks);
+        solver_ceres->getCeresProblem()->GetResidualBlocksForParameterBlock(pb,&residual_blocks);
 
-        if (!ceres_mgr_ptr->getCeresProblem()->IsParameterBlockConstant(pb) && !residual_blocks.empty())
+        if (not solver_ceres->getCeresProblem()->IsParameterBlockConstant(pb) and
+            not residual_blocks.empty())
         {
-            num_param_blocks_reduced ++;
-            num_params_reduced += ceres_mgr_ptr->getCeresProblem()->ParameterBlockLocalSize(pb);
+                num_param_blocks_reduced ++;
+                num_params_reduced += solver_ceres->getCeresProblem()->ParameterBlockLocalSize(pb);
         }
     }
 }
 
 // groundtruth transformations
-Vector1s o_enu_map = (Vector1s() << 2.6).finished();
-Vector3s t_enu_map = (Vector3s() << 12, -34, 4).finished();
-Vector3s t_map_base = (Vector3s() << 32, -34, 0).finished(); // z=0
-Vector1s o_map_base = (Vector1s() << -0.4).finished();
-Vector3s t_base_antena = (Vector3s() << 3,-2,8).finished();// Antena extrinsics
-Vector3s t_ecef_enu = (Vector3s() << 100,30,50).finished();
-Matrix3s R_ecef_enu = (AngleAxis<Scalar>(1.3, Vector3s::UnitX()) *
-                       AngleAxis<Scalar>(-2.2, Vector3s::UnitY()) *
-                       AngleAxis<Scalar>(-1.8, Vector3s::UnitZ())).toRotationMatrix();
-Matrix3s R_enu_map  = AngleAxis<Scalar>(o_enu_map(0),  Vector3s::UnitZ()).toRotationMatrix();
-Matrix3s R_map_base = AngleAxis<Scalar>(o_map_base(0), Vector3s::UnitZ()).toRotationMatrix();
-Vector3s t_ecef_antena = R_ecef_enu * (R_enu_map * (R_map_base * t_base_antena + t_map_base) + t_enu_map ) + t_ecef_enu;
+Vector1d o_enu_map = (Vector1d() << 2.6).finished();
+Vector3d t_enu_map = (Vector3d() << 12, -34, 4).finished();
+Vector3d t_map_base = (Vector3d() << 32, -34, 0).finished(); // z=0
+Vector1d o_map_base = (Vector1d() << -0.4).finished();
+Vector3d t_base_antena = (Vector3d() << 3,-2,8).finished();// Antena extrinsics
+Vector3d t_ecef_enu = (Vector3d() << 100,30,50).finished();
+Matrix3d R_ecef_enu = (AngleAxis<double>(1.3, Vector3d::UnitX()) *
+                       AngleAxis<double>(-2.2, Vector3d::UnitY()) *
+                       AngleAxis<double>(-1.8, Vector3d::UnitZ())).toRotationMatrix();
+Matrix3d R_enu_map  = AngleAxis<double>(o_enu_map(0),  Vector3d::UnitZ()).toRotationMatrix();
+Matrix3d R_map_base = AngleAxis<double>(o_map_base(0), Vector3d::UnitZ()).toRotationMatrix();
+Vector3d t_ecef_antena = R_ecef_enu * (R_enu_map * (R_map_base * t_base_antena + t_map_base) + t_enu_map ) + t_ecef_enu;
 
 // WOLF
 ProblemPtr problem_ptr = Problem::create("PO", 2);
-CeresManagerPtr ceres_mgr_ptr = std::make_shared<CeresManager>(problem_ptr);
-SensorGnssPtr gnss_sensor_ptr = std::static_pointer_cast<SensorGnss>(problem_ptr->installSensor("SensorGnss", "gnss", t_base_antena, std::make_shared<IntrinsicsGnss>()));
+SolverCeresPtr solver_ceres = std::make_shared<SolverCeres>(problem_ptr);
+SensorGnssPtr gnss_sensor_ptr = std::static_pointer_cast<SensorGnss>(problem_ptr->installSensor("SensorGnss", "gnss", t_base_antena, std::make_shared<ParamsSensorGnss>()));
 FrameBasePtr frame_ptr;
 
 ////////////////////////////////////////////////////////
 
-TEST(FactorGnssFix2DTest, configure_tree)
+TEST(FactorGnssFix2dTest, configure_tree)
 {
-    ceres_mgr_ptr->getSolverOptions().max_num_iterations = 100;
+    solver_ceres->getSolverOptions().max_num_iterations = 100;
 
     // Configure sensor and processor
     gnss_sensor_ptr->setEnuMapTranslationState(t_enu_map);
@@ -106,23 +126,23 @@ TEST(FactorGnssFix2DTest, configure_tree)
     gnss_sensor_ptr->getEnuMapPitch()->fix();
     gnss_sensor_ptr->setEnuEcef(R_ecef_enu.transpose(), R_ecef_enu.transpose()*(-t_ecef_enu));
 
-    ProcessorParamsGnssFixPtr gnss_params_ptr = std::make_shared<ProcessorParamsGnssFix>();
+    ParamsProcessorGnssFixPtr gnss_params_ptr = std::make_shared<ParamsProcessorGnssFix>();
     gnss_params_ptr->time_tolerance = 1.0;
     gnss_params_ptr->voting_active = true;
+    gnss_params_ptr->apply_loss_function = false;
     problem_ptr->installProcessor("ProcessorGnssFix", "gnss fix", gnss_sensor_ptr, gnss_params_ptr);
 
     // Emplace a frame (FIXED)
-    Vector3s frame_pose = (Vector3s() << t_map_base(0), t_map_base(1), o_map_base(0)).finished();
-    frame_ptr = problem_ptr->emplaceFrame(KEY, frame_pose, TimeStamp(0));
-    problem_ptr->keyFrameCallback(frame_ptr, nullptr, 1.0);
+    Vector3d frame_pose = (Vector3d() << t_map_base(0), t_map_base(1), o_map_base(0)).finished();
+    frame_ptr = problem_ptr->emplaceFrame( TimeStamp(0), frame_pose);
+    problem_ptr->keyFrameCallback(frame_ptr, nullptr);
 
     // Create & process GNSS Fix capture
-    CaptureGnssFixPtr cap_gnss_ptr = std::make_shared<CaptureGnssFix>(TimeStamp(0), gnss_sensor_ptr, t_ecef_antena, 1e-3*Matrix3s::Identity());
+    CaptureGnssFixPtr cap_gnss_ptr = std::make_shared<CaptureGnssFix>(TimeStamp(0), gnss_sensor_ptr, t_ecef_antena, 1e-3*Matrix3d::Identity());
     gnss_sensor_ptr->process(cap_gnss_ptr);
 
     // Checks
-    ASSERT_TRUE(problem_ptr->check(0));
-    ASSERT_TRUE(frame_ptr->isKey());
+    EXPECT_TRUE(problem_ptr->check(1));
 }
 
 /*
@@ -131,7 +151,7 @@ TEST(FactorGnssFix2DTest, configure_tree)
  * Estimating: MAP_BASE position.
  * Fixed: ENU_MAP, MAP_BASE orientation, BASE_ANTENA.
  */
-TEST(FactorGnssFix2DTest, gnss_1_map_base_position)
+TEST(FactorGnssFix2dTest, gnss_1_map_base_position)
 {
     // --------------------------- Reset states
     resetStates(gnss_sensor_ptr, frame_ptr, o_enu_map, t_base_antena, t_enu_map, t_map_base, o_map_base);
@@ -141,34 +161,34 @@ TEST(FactorGnssFix2DTest, gnss_1_map_base_position)
     frame_ptr->getP()->unfix();
 
     // --------------------------- distort: map base position
-    Vector3s frm_dist = frame_ptr->getState();
+    Vector3d frm_dist = frame_ptr->getState().vector("PO");
     frm_dist(0) += 0.18;
     frm_dist(1) += -0.58;
-    frame_ptr->setState(frm_dist);
+    frame_ptr->setState(frm_dist, "PO", {2,1});
 
     // --------------------------- update solver
-    ceres_mgr_ptr->update();
+    solver_ceres->update();
 
     // --------------------------- check problem parameters
     int num_params_reduced, num_param_blocks_reduced;
-    computeParamSizes(ceres_mgr_ptr, num_params_reduced, num_param_blocks_reduced);
+    computeParamSizes(solver_ceres, num_params_reduced, num_param_blocks_reduced);
 
-    ASSERT_EQ(num_param_blocks_reduced, 1);
-    ASSERT_EQ(num_params_reduced, 2);
+    EXPECT_EQ(num_param_blocks_reduced, 1);
+    EXPECT_EQ(num_params_reduced, 2);
 
     // --------------------------- solve
-    std::string report = ceres_mgr_ptr->solve(SolverManager::ReportVerbosity::FULL);
+    std::string report = solver_ceres->solve(SolverManager::ReportVerbosity::FULL);
 
     //std::cout << report << std::endl;
 
     // --------------------------- check summary parameters & residuals
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_parameter_blocks_reduced, 1);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_parameters_reduced, 2);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_residual_blocks_reduced, 1);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_residuals_reduced, 3);
+    EXPECT_EQ(solver_ceres->getSummary().num_parameter_blocks_reduced, 1);
+    EXPECT_EQ(solver_ceres->getSummary().num_parameters_reduced, 2);
+    EXPECT_EQ(solver_ceres->getSummary().num_residual_blocks_reduced, 1);
+    EXPECT_EQ(solver_ceres->getSummary().num_residuals_reduced, 3);
 
     // --------------------------- check solver solution
-    ASSERT_MATRIX_APPROX(frame_ptr->getState().head(2), t_map_base.head(2), 1e-6);
+    EXPECT_MATRIX_APPROX(frame_ptr->getState().at('P'), t_map_base.head(2), 1e-6);
 }
 
 /*
@@ -177,7 +197,7 @@ TEST(FactorGnssFix2DTest, gnss_1_map_base_position)
  * Estimating: MAP_BASE orientation.
  * Fixed: ENU_MAP, MAP_BASE position, BASE_ANTENA.
  */
-TEST(FactorGnssFix2DTest, gnss_1_map_base_orientation)
+TEST(FactorGnssFix2dTest, gnss_1_map_base_orientation)
 {
     // --------------------------- Reset states
     resetStates(gnss_sensor_ptr, frame_ptr, o_enu_map, t_base_antena, t_enu_map, t_map_base, o_map_base);
@@ -187,31 +207,31 @@ TEST(FactorGnssFix2DTest, gnss_1_map_base_orientation)
     frame_ptr->getO()->unfix();
 
     // --------------------------- distort: map base orientation
-    Vector1s frm_dist = frame_ptr->getO()->getState();
+    Vector1d frm_dist = frame_ptr->getO()->getState();
     frm_dist(0) += 0.18;
     frame_ptr->getO()->setState(frm_dist);
 
     // --------------------------- update solver
-    ceres_mgr_ptr->update();
+    solver_ceres->update();
 
     // --------------------------- check problem parameters
     int num_params_reduced, num_param_blocks_reduced;
-    computeParamSizes(ceres_mgr_ptr, num_params_reduced, num_param_blocks_reduced);
+    computeParamSizes(solver_ceres, num_params_reduced, num_param_blocks_reduced);
 
-    ASSERT_EQ(num_param_blocks_reduced, 1);
-    ASSERT_EQ(num_params_reduced, 1);
+    EXPECT_EQ(num_param_blocks_reduced, 1);
+    EXPECT_EQ(num_params_reduced, 1);
 
     // --------------------------- solve
-    std::string report = ceres_mgr_ptr->solve(SolverManager::ReportVerbosity::QUIET);
+    std::string report = solver_ceres->solve(SolverManager::ReportVerbosity::QUIET);
 
     // --------------------------- check summary parameters & residuals
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_parameter_blocks_reduced, 1);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_parameters_reduced, 1);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_residual_blocks_reduced, 1);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_residuals_reduced, 3);
+    EXPECT_EQ(solver_ceres->getSummary().num_parameter_blocks_reduced, 1);
+    EXPECT_EQ(solver_ceres->getSummary().num_parameters_reduced, 1);
+    EXPECT_EQ(solver_ceres->getSummary().num_residual_blocks_reduced, 1);
+    EXPECT_EQ(solver_ceres->getSummary().num_residuals_reduced, 3);
 
     // --------------------------- check solver solution
-    ASSERT_MATRIX_APPROX(frame_ptr->getO()->getState(), o_map_base, 1e-6);
+    EXPECT_MATRIX_APPROX(frame_ptr->getO()->getState(), o_map_base, 1e-6);
 }
 
 /*
@@ -220,7 +240,7 @@ TEST(FactorGnssFix2DTest, gnss_1_map_base_orientation)
  * Estimating: ENU_MAP yaw.
  * Fixed: ENU_MAP position, MAP_BASE and BASE_ANTENA.
  */
-TEST(FactorGnssFix2DTest, gnss_1_enu_map_yaw)
+TEST(FactorGnssFix2dTest, gnss_1_enu_map_yaw)
 {
     // --------------------------- Reset states
     resetStates(gnss_sensor_ptr, frame_ptr, o_enu_map, t_base_antena, t_enu_map, t_map_base, o_map_base);
@@ -230,31 +250,34 @@ TEST(FactorGnssFix2DTest, gnss_1_enu_map_yaw)
     gnss_sensor_ptr->getEnuMapYaw()->unfix();
 
     // --------------------------- distort: yaw enu_map
-    Vector1s o_enu_map_dist = gnss_sensor_ptr->getEnuMapYaw()->getState();
+    Vector1d o_enu_map_dist = gnss_sensor_ptr->getEnuMapYaw()->getState();
     o_enu_map_dist(0) += 0.18;
     gnss_sensor_ptr->getEnuMapYaw()->setState(o_enu_map_dist);
 
     // --------------------------- update solver
-    ceres_mgr_ptr->update();
+    solver_ceres->update();
+    EXPECT_TRUE(solver_ceres->check());
 
     // --------------------------- check problem parameters
     int num_params_reduced, num_param_blocks_reduced;
-    computeParamSizes(ceres_mgr_ptr, num_params_reduced, num_param_blocks_reduced);
+    computeParamSizes(solver_ceres, num_params_reduced, num_param_blocks_reduced);
 
-    ASSERT_EQ(num_param_blocks_reduced, 1);
-    ASSERT_EQ(num_params_reduced, 1);
+    EXPECT_EQ(num_param_blocks_reduced, 1);
+    EXPECT_EQ(num_params_reduced, 1);
 
     // --------------------------- solve
-    std::string report = ceres_mgr_ptr->solve(SolverManager::ReportVerbosity::QUIET);
+    std::string report = solver_ceres->solve(SolverManager::ReportVerbosity::FULL);
 
     // --------------------------- check summary parameters & residuals
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_parameter_blocks_reduced, 1);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_parameters_reduced, 1);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_residual_blocks_reduced, 1);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_residuals_reduced, 3);
+    EXPECT_EQ(solver_ceres->getSummary().num_parameter_blocks_reduced, 1);
+    EXPECT_EQ(solver_ceres->getSummary().num_parameters_reduced, 1);
+    EXPECT_EQ(solver_ceres->getSummary().num_residual_blocks_reduced, 1);
+    EXPECT_EQ(solver_ceres->getSummary().num_residuals_reduced, 3);
 
     // --------------------------- check solver solution
-    ASSERT_MATRIX_APPROX(gnss_sensor_ptr->getEnuMapYaw()->getState(), o_enu_map, 1e-6);
+    EXPECT_MATRIX_APPROX(gnss_sensor_ptr->getEnuMapYaw()->getState(), o_enu_map, 1e-6);
+
+    problem_ptr->print(4,1,1,1);
 }
 
 /*
@@ -263,7 +286,7 @@ TEST(FactorGnssFix2DTest, gnss_1_enu_map_yaw)
  * Estimating: ENU_MAP position.
  * Fixed: ENU_MAP yaw, MAP_BASE and BASE_ANTENA.
  */
-TEST(FactorGnssFix2DTest, gnss_1_enu_map_position)
+TEST(FactorGnssFix2dTest, gnss_1_enu_map_position)
 {
     // --------------------------- Reset states
     resetStates(gnss_sensor_ptr, frame_ptr, o_enu_map, t_base_antena, t_enu_map, t_map_base, o_map_base);
@@ -273,32 +296,32 @@ TEST(FactorGnssFix2DTest, gnss_1_enu_map_position)
     gnss_sensor_ptr->getEnuMapTranslation()->unfix();// enu-map yaw translation
 
     // --------------------------- distort: position enu_map
-    Vector3s t_enu_map_dist = gnss_sensor_ptr->getEnuMapTranslation()->getState();
+    Vector3d t_enu_map_dist = gnss_sensor_ptr->getEnuMapTranslation()->getState();
     t_enu_map_dist(0) += 0.86;
     t_enu_map_dist(1) += -0.34;
     gnss_sensor_ptr->getEnuMapTranslation()->setState(t_enu_map_dist);
 
     // --------------------------- update solver
-    ceres_mgr_ptr->update();
+    solver_ceres->update();
 
     // --------------------------- check problem parameters
     int num_params_reduced, num_param_blocks_reduced;
-    computeParamSizes(ceres_mgr_ptr, num_params_reduced, num_param_blocks_reduced);
+    computeParamSizes(solver_ceres, num_params_reduced, num_param_blocks_reduced);
 
-    ASSERT_EQ(num_param_blocks_reduced, 1);
-    ASSERT_EQ(num_params_reduced, 3);
+    EXPECT_EQ(num_param_blocks_reduced, 1);
+    EXPECT_EQ(num_params_reduced, 3);
 
     // --------------------------- solve
-    std::string report = ceres_mgr_ptr->solve(SolverManager::ReportVerbosity::QUIET);
+    std::string report = solver_ceres->solve(SolverManager::ReportVerbosity::QUIET);
 
     // --------------------------- check summary parameters & residuals
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_parameter_blocks_reduced, 1);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_parameters_reduced, 3);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_residual_blocks_reduced, 1);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_residuals_reduced, 3);
+    EXPECT_EQ(solver_ceres->getSummary().num_parameter_blocks_reduced, 1);
+    EXPECT_EQ(solver_ceres->getSummary().num_parameters_reduced, 3);
+    EXPECT_EQ(solver_ceres->getSummary().num_residual_blocks_reduced, 1);
+    EXPECT_EQ(solver_ceres->getSummary().num_residuals_reduced, 3);
 
     // --------------------------- check solver solution
-    ASSERT_MATRIX_APPROX(gnss_sensor_ptr->getEnuMapTranslation()->getState().head(2), t_enu_map.head(2), 1e-6);
+    EXPECT_MATRIX_APPROX(gnss_sensor_ptr->getEnuMapTranslation()->getState().head(2), t_enu_map.head(2), 1e-6);
 }
 
 /*
@@ -307,7 +330,7 @@ TEST(FactorGnssFix2DTest, gnss_1_enu_map_position)
  * Estimating: BASE_ANTENA (2 first components that are observable).
  * Fixed: ENU_MAP, MAP_BASE.
  */
-TEST(FactorGnssFix2DTest, gnss_1_base_antena)
+TEST(FactorGnssFix2dTest, gnss_1_base_antena)
 {
     // --------------------------- Reset states
     resetStates(gnss_sensor_ptr, frame_ptr, o_enu_map, t_base_antena, t_enu_map, t_map_base, o_map_base);
@@ -317,32 +340,32 @@ TEST(FactorGnssFix2DTest, gnss_1_base_antena)
     gnss_sensor_ptr->getP()->unfix();
 
     // --------------------------- distort: base_antena
-    Vector3s base_antena_dist = gnss_sensor_ptr->getP()->getState();
+    Vector3d base_antena_dist = gnss_sensor_ptr->getP()->getState();
     base_antena_dist(0) += 1.68;
     base_antena_dist(0) += -0.48;
     gnss_sensor_ptr->getP()->setState(base_antena_dist);
 
     // --------------------------- update solver
-    ceres_mgr_ptr->update();
+    solver_ceres->update();
 
     // --------------------------- check problem parameters
     int num_params_reduced, num_param_blocks_reduced;
-    computeParamSizes(ceres_mgr_ptr, num_params_reduced, num_param_blocks_reduced);
+    computeParamSizes(solver_ceres, num_params_reduced, num_param_blocks_reduced);
 
-    ASSERT_EQ(num_param_blocks_reduced, 1);
-    ASSERT_EQ(num_params_reduced, 3);
+    EXPECT_EQ(num_param_blocks_reduced, 1);
+    EXPECT_EQ(num_params_reduced, 3);
 
     // --------------------------- solve
-    std::string report = ceres_mgr_ptr->solve(SolverManager::ReportVerbosity::QUIET);
+    std::string report = solver_ceres->solve(SolverManager::ReportVerbosity::QUIET);
 
     // --------------------------- check summary parameters & residuals
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_parameter_blocks_reduced, 1);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_parameters_reduced, 3);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_residual_blocks_reduced, 1);
-    ASSERT_EQ(ceres_mgr_ptr->getSummary().num_residuals_reduced, 3);
+    EXPECT_EQ(solver_ceres->getSummary().num_parameter_blocks_reduced, 1);
+    EXPECT_EQ(solver_ceres->getSummary().num_parameters_reduced, 3);
+    EXPECT_EQ(solver_ceres->getSummary().num_residual_blocks_reduced, 1);
+    EXPECT_EQ(solver_ceres->getSummary().num_residuals_reduced, 3);
 
     // --------------------------- check solver solution
-    ASSERT_MATRIX_APPROX(gnss_sensor_ptr->getP()->getState().head(2), t_base_antena.head(2), 1e-6);
+    EXPECT_MATRIX_APPROX(gnss_sensor_ptr->getP()->getState().head(2), t_base_antena.head(2), 1e-6);
 }
 
 int main(int argc, char **argv)
@@ -350,4 +373,3 @@ int main(int argc, char **argv)
   testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
 }
-
diff --git a/test/gtest_factor_gnss_pseudo_range.cpp b/test/gtest_factor_gnss_pseudo_range.cpp
new file mode 100644
index 000000000..f6691f178
--- /dev/null
+++ b/test/gtest_factor_gnss_pseudo_range.cpp
@@ -0,0 +1,322 @@
+//--------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/ceres_wrapper/solver_ceres.h>
+#include <core/utils/utils_gtest.h>
+
+#include "core/problem/problem.h"
+#include "gnss/sensor/sensor_gnss.h"
+#include "gnss/capture/capture_gnss.h"
+#include "gnss/factor/factor_gnss_pseudo_range.h"
+
+using namespace Eigen;
+using namespace wolf;
+using namespace GnssUtils;
+
+// groundtruth transformations
+Vector3d t_ecef_enu, t_enu_map, t_map_base, t_base_antena; // Antena extrinsics
+Vector3d t_ecef_antena;
+Vector3d rpy_enu_map;
+Matrix3d R_ecef_enu, R_enu_map;
+Quaterniond q_map_base;
+Vector1d clock_drift;
+
+GnssUtils::Satellite sat1({SYS_GPS, 1,Eigen::Vector3d::Zero(),Eigen::Vector3d::Zero(),0,0,0});
+GnssUtils::Satellite sat2({SYS_GPS, 1,Eigen::Vector3d::Zero(),Eigen::Vector3d::Zero(),0,0,0});
+GnssUtils::Satellite sat3({SYS_GPS, 1,Eigen::Vector3d::Zero(),Eigen::Vector3d::Zero(),0,0,0});
+GnssUtils::Satellite sat4({SYS_GPS, 1,Eigen::Vector3d::Zero(),Eigen::Vector3d::Zero(),0,0,0});
+
+GnssUtils::Range range1, range2, range3, range4;
+
+
+// WOLF
+ProblemPtr prb = Problem::create("PO", 3);
+SolverCeresPtr solver = std::make_shared<SolverCeres>(prb);
+SensorGnssPtr gnss_sensor = std::static_pointer_cast<SensorGnss>(prb->installSensor("SensorGnss", "gnss", t_base_antena, std::make_shared<ParamsSensorGnss>()));
+FrameBasePtr frm;
+CaptureGnssPtr cap;
+FeatureGnssSatellitePtr ftr1, ftr2, ftr3, ftr4;
+FactorGnssPseudoRangePtr fac1, fac2, fac3, fac4;
+
+void randomGroundtruth()
+{
+    // ECEF-ENU
+    t_ecef_enu = Vector3d::Random() * 1e3;
+    Vector3d rpy_ecef_enu = M_PI*Vector3d::Random();
+    R_ecef_enu = (AngleAxis<double>(rpy_ecef_enu(0), Vector3d::UnitX()) *
+                  AngleAxis<double>(rpy_ecef_enu(1), Vector3d::UnitY()) *
+                  AngleAxis<double>(rpy_ecef_enu(2), Vector3d::UnitZ())).toRotationMatrix();
+
+    // ENU-MAP
+    t_enu_map = Vector3d::Random() * 1e3;
+    rpy_enu_map = M_PI*Vector3d::Random();
+    R_enu_map  = (AngleAxis<double>(rpy_enu_map(0), Vector3d::UnitX()) *
+                  AngleAxis<double>(rpy_enu_map(1), Vector3d::UnitY()) *
+                  AngleAxis<double>(rpy_enu_map(2), Vector3d::UnitZ())).toRotationMatrix();
+
+    // MAP-BASE
+    t_map_base = Vector3d::Random() * 1e2;
+    q_map_base = Quaterniond::UnitRandom();
+
+    // BASE-ANTENA (EXTRINSICS)
+    t_base_antena = Vector3d::Random();
+
+    // composition
+    t_ecef_antena = R_ecef_enu * (R_enu_map * (q_map_base * t_base_antena + t_map_base) + t_enu_map ) + t_ecef_enu;
+
+    // SATELLITES
+    sat1.pos = Vector3d::Random() * 1e4;
+    sat2.pos = Vector3d::Random() * 1e4;
+    sat3.pos = Vector3d::Random() * 1e4;
+    sat4.pos = Vector3d::Random() * 1e4;
+
+    // clock drift
+    clock_drift = Vector1d::Random() * 1e2;
+
+    // pseudo ranges
+    range1.P_corrected = (sat1.pos-t_ecef_antena).norm() + clock_drift(0);
+    range2.P_corrected = (sat2.pos-t_ecef_antena).norm() + clock_drift(0);
+    range3.P_corrected = (sat3.pos-t_ecef_antena).norm() + clock_drift(0);
+    range4.P_corrected = (sat4.pos-t_ecef_antena).norm() + clock_drift(0);
+    range1.P_var = 1.0;
+    range2.P_var = 1.0;
+    range3.P_var = 1.0;
+    range4.P_var = 1.0;
+}
+
+void setUpProblem()
+{
+    // remove previous setup
+    if (frm)
+        frm->remove();
+
+    solver->getSolverOptions().max_num_iterations = 100;
+
+    // Sensor
+    // ENU-MAP
+    gnss_sensor->setEnuMapTranslationState(t_enu_map);
+    gnss_sensor->setEnuMapRollState(rpy_enu_map(0));
+    gnss_sensor->setEnuMapPitchState(rpy_enu_map(1));
+    gnss_sensor->setEnuMapYawState(rpy_enu_map(2));
+    // ECEF-ENU
+    gnss_sensor->setEnuEcef(R_ecef_enu.transpose(), R_ecef_enu.transpose()*(-t_ecef_enu));
+    // Extrinsics
+    gnss_sensor->getP()->setState(t_base_antena);
+
+    // Frame
+    Vector7d frm_state;
+    frm_state.head<3>() = t_map_base;
+    frm_state.tail<4>() = q_map_base.coeffs();
+    frm = prb->emplaceFrame( TimeStamp(0), frm_state);
+
+    // capture
+    cap = CaptureBase::emplace<CaptureGnss>(frm, TimeStamp(0), gnss_sensor, nullptr);
+    cap->getStateBlock('T')->unfix();
+    cap->getStateBlock('T')->setState(clock_drift);
+
+    // features
+    ftr1 = FeatureBase::emplace<FeatureGnssSatellite>(cap, obsd_t(), sat1, range1); // obsd_t data is not used in pseudo range factors
+    ftr2 = FeatureBase::emplace<FeatureGnssSatellite>(cap, obsd_t(), sat2, range2); // obsd_t data is not used in pseudo range factors
+    ftr3 = FeatureBase::emplace<FeatureGnssSatellite>(cap, obsd_t(), sat3, range3); // obsd_t data is not used in pseudo range factors
+    ftr4 = FeatureBase::emplace<FeatureGnssSatellite>(cap, obsd_t(), sat4, range4); // obsd_t data is not used in pseudo range factors
+
+    // factors
+    fac1 = FactorBase::emplace<FactorGnssPseudoRange>(ftr1, ftr1, gnss_sensor, nullptr, false);
+    fac2 = FactorBase::emplace<FactorGnssPseudoRange>(ftr2, ftr2, gnss_sensor, nullptr, false);
+    fac3 = FactorBase::emplace<FactorGnssPseudoRange>(ftr3, ftr3, gnss_sensor, nullptr, false);
+    fac4 = FactorBase::emplace<FactorGnssPseudoRange>(ftr4, ftr4, gnss_sensor, nullptr, false);
+
+    // ASSERTS
+    // ENU-MAP
+    ASSERT_FLOAT_EQ(gnss_sensor->getEnuMapRoll()->getState()(0), rpy_enu_map(0));
+    ASSERT_FLOAT_EQ(gnss_sensor->getEnuMapPitch()->getState()(0), rpy_enu_map(1));
+    ASSERT_FLOAT_EQ(gnss_sensor->getEnuMapYaw()->getState()(0), rpy_enu_map(2));
+    ASSERT_MATRIX_APPROX(gnss_sensor->getEnuMapTranslation()->getState(), t_enu_map, 1e-3);
+    // extrinsics
+    ASSERT_MATRIX_APPROX(gnss_sensor->getP()->getState(), t_base_antena, 1e-3);
+    // Frame
+    ASSERT_MATRIX_APPROX(frm->getP()->getState(), t_map_base, 1e-3);
+    ASSERT_MATRIX_APPROX(frm->getO()->getState(), q_map_base.coeffs(), 1e-3);
+    // clock drift
+    ASSERT_MATRIX_APPROX(clock_drift, cap->getStateBlock('T')->getState(), 1e-3);
+    // composition
+    ASSERT_MATRIX_APPROX(gnss_sensor->computeFrameAntennaPosEcef(frm), t_ecef_antena, 1e-3);
+}
+
+void fixAllStates()
+{
+    // ENU-MAP
+    gnss_sensor->getEnuMapRoll()->fix();
+    gnss_sensor->getEnuMapPitch()->fix();
+    gnss_sensor->getEnuMapYaw()->fix();
+    gnss_sensor->getEnuMapTranslation()->fix();
+    // Antena
+    gnss_sensor->getP()->fix();
+    // Frame
+    frm->fix();
+    // clock drift
+    cap->fix();
+ }
+
+////////////////////////////////////////////////////////
+TEST(FactorGnssPreusoRangeTest, observe_clock_drift)
+{
+    for (auto i = 0; i < 100; i++)
+    {
+        // setup random problem
+        randomGroundtruth();
+        setUpProblem();
+
+        // fix/unfix
+        fixAllStates();
+        cap->getStateBlock('T')->unfix();
+
+        // perturb
+        cap->getStateBlock('T')->perturb(1e2);
+
+        // Only 1 factor
+        fac2->setStatus(FAC_INACTIVE);
+        fac3->setStatus(FAC_INACTIVE);
+        fac4->setStatus(FAC_INACTIVE);
+
+        // solve
+        std::string report = solver->solve(SolverManager::ReportVerbosity::FULL);
+        //std::cout << report << std::endl;
+
+        ASSERT_MATRIX_APPROX(clock_drift, cap->getStateBlock('T')->getState(), 1e-3);
+    }
+}
+
+TEST(FactorGnssPreusoRangeTest, observe_frame_p)
+{
+    for (auto i = 0; i < 100; i++)
+    {
+        // setup random problem
+        randomGroundtruth();
+        setUpProblem();
+
+        // fix/unfix
+        fixAllStates();
+        frm->getP()->unfix();
+
+        // perturb
+        frm->getP()->perturb(1);
+
+        // Only 3 factors
+        fac4->setStatus(FAC_INACTIVE);
+
+        // solve
+        std::string report = solver->solve(SolverManager::ReportVerbosity::FULL);
+        //std::cout << report << std::endl;
+
+        ASSERT_MATRIX_APPROX(frm->getP()->getState(), t_map_base, 1e-3);
+    }
+}
+
+TEST(FactorGnssPreusoRangeTest, observe_frame_p_clock)
+{
+    for (auto i = 0; i < 100; i++)
+    {
+        // setup random problem
+        randomGroundtruth();
+        setUpProblem();
+
+        // fix/unfix
+        fixAllStates();
+        frm->getP()->unfix();
+        cap->getStateBlock('T')->unfix();
+
+        // perturb
+        frm->getP()->perturb(1);
+        cap->getStateBlock('T')->perturb(1e2);
+
+        // all 4 factors
+
+        // solve
+        std::string report = solver->solve(SolverManager::ReportVerbosity::FULL);
+        //std::cout << report << std::endl;
+
+        ASSERT_MATRIX_APPROX(frm->getP()->getState(), t_map_base, 1e-3);
+        ASSERT_MATRIX_APPROX(clock_drift, cap->getStateBlock('T')->getState(), 1e-3);
+    }
+}
+
+TEST(FactorGnssPreusoRangeTest, observe_enumap_p)
+{
+    for (auto i = 0; i < 100; i++)
+    {
+        // setup random problem
+        randomGroundtruth();
+        setUpProblem();
+
+        // fix/unfix
+        fixAllStates();
+        gnss_sensor->getEnuMapTranslation()->unfix();
+
+        // perturb
+        gnss_sensor->getEnuMapTranslation()->perturb(1e2);
+
+        // Only 3 factors
+        fac4->setStatus(FAC_INACTIVE);
+
+        // solve
+        std::string report = solver->solve(SolverManager::ReportVerbosity::FULL);
+        //std::cout << report << std::endl;
+
+        ASSERT_MATRIX_APPROX(gnss_sensor->getEnuMapTranslation()->getState(), t_enu_map, 1e-3);
+    }
+}
+
+TEST(FactorGnssPreusoRangeTest, observe_enumap_o)
+{
+    for (auto i = 0; i < 100; i++)
+    {
+        // setup random problem
+        randomGroundtruth();
+        setUpProblem();
+
+        // fix/unfix
+        fixAllStates();
+        gnss_sensor->getEnuMapRoll()->unfix();
+        gnss_sensor->getEnuMapPitch()->unfix();
+        gnss_sensor->getEnuMapYaw()->unfix();
+
+        // perturb
+        gnss_sensor->getEnuMapRoll()->perturb(1);
+        gnss_sensor->getEnuMapPitch()->perturb(1);
+        gnss_sensor->getEnuMapYaw()->perturb(1);
+
+        // Only 3 factors
+        fac4->setStatus(FAC_INACTIVE);
+
+        // solve
+        std::string report = solver->solve(SolverManager::ReportVerbosity::FULL);
+        //std::cout << report << std::endl;
+
+        ASSERT_MATRIX_APPROX(gnss_sensor->getEnuMapTranslation()->getState(), t_enu_map, 1e-3);
+    }
+}
+
+int main(int argc, char **argv)
+{
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/test/gtest_factor_gnss_single_diff_2D.cpp b/test/gtest_factor_gnss_single_diff_2D.cpp
deleted file mode 100644
index dcabb00df..000000000
--- a/test/gtest_factor_gnss_single_diff_2D.cpp
+++ /dev/null
@@ -1,267 +0,0 @@
-/**
- * \file gtest_factor_gnss_single_diff_2D.cpp
- *
- *  Created on: Aug 28, 2018
- *      \author: jvallve
- */
-
-
-#include "gnss/factor/factor_gnss_single_diff_2D.h"
-#include <core/utils/utils_gtest.h>
-
-#include "core/capture/capture_motion.h"
-#include "core/problem/problem.h"
-#include "gnss/sensor/sensor_gnss.h"
-#include "core/sensor/sensor_odom_2D.h"
-#include "core/processor/processor_odom_2D.h"
-#include "gnss/processor/processor_gnss_single_diff.h"
-
-#include "core/ceres_wrapper/ceres_manager.h"
-
-
-using namespace Eigen;
-using namespace wolf;
-using std::cout;
-using std::endl;
-
-
-class FactorGnssSingleDiff2DTest : public testing::Test
-{
-    public:
-
-        // groundtruth transformations
-        Vector3s t_ecef_enu;
-        Matrix3s R_ecef_enu, R_enu_map, R_map_base1, R_map_base2;
-        Vector3s t_base_antena, t_ecef_antena1, t_ecef_antena2;
-        Vector1s o_enu_map;
-        Vector3s t_map_base1, t_map_base2;
-        Vector1s o_map_base1, o_map_base2;
-
-        // WOLF
-        ProblemPtr problem_ptr;
-        CeresManagerPtr ceres_mgr_ptr;
-        SensorGnssPtr gnss_sensor_ptr;
-        SensorOdom2DPtr odom_sensor_ptr;
-        FrameBasePtr prior_frame_ptr;
-
-        FactorGnssSingleDiff2DTest()
-        {
-            o_enu_map << 2.6;
-            t_map_base1 << 32, -34, 0; // z=0
-            o_map_base1 << -0.4;
-            t_map_base2 << -76, 63, 0; // z=0
-            o_map_base2 << 0.7;
-            t_base_antena << 3,-2,8;// Antena extrinsics
-            t_ecef_enu << 100,30,50;
-            R_ecef_enu = (AngleAxis<Scalar>(1.3, Vector3s::UnitX())
-                         *AngleAxis<Scalar>(-2.2, Vector3s::UnitY())
-                         *AngleAxis<Scalar>(-1.8, Vector3s::UnitZ())).toRotationMatrix();
-
-            // ----------------------------------------------------
-            // Problem and solver
-            problem_ptr = Problem::create("PO", 2);
-            ceres_mgr_ptr = std::make_shared<CeresManager>(problem_ptr);
-            ceres_mgr_ptr->getSolverOptions().max_num_iterations = 10;
-
-            // gnss sensor
-            gnss_sensor_ptr = std::static_pointer_cast<SensorGnss>(problem_ptr->installSensor("SensorGnss", "gnss", t_base_antena, std::make_shared<IntrinsicsBase>()));
-            gnss_sensor_ptr->getEnuMapRoll()->fix();
-            gnss_sensor_ptr->getEnuMapPitch()->fix();
-            gnss_sensor_ptr->setEnuEcef(R_ecef_enu.transpose(), R_ecef_enu.transpose()*(-t_ecef_enu));
-
-            // gnss processor
-            ProcessorParamsGnssSingleDiffPtr gnss_params_ptr = std::make_shared<ProcessorParamsGnssSingleDiff>();
-            gnss_params_ptr->time_tolerance = 1.0;
-            gnss_params_ptr->voting_active = true;
-            gnss_params_ptr->voting_aux_active = false;
-            gnss_params_ptr->time_th = 0;
-            gnss_params_ptr->dist_traveled = 0;
-            gnss_params_ptr->enu_map_init_dist_min = 0;
-            problem_ptr->installProcessor("GNSS SINGLE DIFFERENCES", "gnss single difference", gnss_sensor_ptr, gnss_params_ptr);
-
-            // odom sensor & processor
-            IntrinsicsOdom2DPtr odom_intrinsics_ptr = std::make_shared<IntrinsicsOdom2D>();
-            odom_intrinsics_ptr->k_disp_to_disp = 0.1;
-            odom_intrinsics_ptr->k_rot_to_rot = 0.1;
-            odom_sensor_ptr = std::static_pointer_cast<SensorOdom2D>(problem_ptr->installSensor("SensorOdom2D", "odometer", VectorXs::Zero(3), odom_intrinsics_ptr));
-            ProcessorParamsOdom2DPtr odom_params_ptr = std::make_shared<ProcessorParamsOdom2D>();
-            odom_params_ptr->voting_active = true;
-            odom_params_ptr->dist_traveled = 1;
-            odom_params_ptr->angle_turned = 2.0;
-            odom_params_ptr->max_time_span = 1.0;
-            odom_params_ptr->time_tolerance = 1.0;
-            problem_ptr->installProcessor("ProcessorOdom2D", "main odometry", odom_sensor_ptr, odom_params_ptr);
-            //problem_ptr->setProcessorMotion("main odometry");
-
-            // set prior (FIXED)
-            Vector3s frame1state = t_map_base1;
-            frame1state(2) = o_map_base1(0);
-            prior_frame_ptr = problem_ptr->setPrior(frame1state, Matrix3s::Identity(), TimeStamp(0), Scalar(0.1));
-            prior_frame_ptr->fix();
-        };
-
-        virtual void SetUp()
-        {
-            // reset grountruth parameters
-            R_enu_map = AngleAxis<Scalar>(o_enu_map(0), Vector3s::UnitZ()).toRotationMatrix();
-            R_map_base1 = Matrix3s::Identity();
-            R_map_base1.topLeftCorner(2,2) = Rotation2D<Scalar>(o_map_base1(0)).matrix();
-            R_map_base2 = Matrix3s::Identity();
-            R_map_base2.topLeftCorner(2,2) = Rotation2D<Scalar>(o_map_base2(0)).matrix();
-
-            t_ecef_antena1 = R_ecef_enu * R_enu_map * (R_map_base1 * t_base_antena + t_map_base1) + t_ecef_enu;
-            t_ecef_antena2 = R_ecef_enu * R_enu_map * (R_map_base2 * t_base_antena + t_map_base2) + t_ecef_enu;
-
-            // Reset antena extrinsics
-            gnss_sensor_ptr->getP()->setState(t_base_antena);
-
-            // Reset ENU_ECEF
-            gnss_sensor_ptr->setEnuEcef(R_ecef_enu.transpose(), R_ecef_enu.transpose()*(-t_ecef_enu));
-        }
-};
-
-////////////////////////////////////////////////////////
-
-TEST_F(FactorGnssSingleDiff2DTest, check_tree)
-{
-    ASSERT_TRUE(problem_ptr->check(0));
-    ASSERT_TRUE(prior_frame_ptr != nullptr);
-}
-
-/*
- * Test with one GNSS Single Difference capture.
- *
- * Estimating: MAP_BASE2 position.
- * Fixed: MAP_BASE1, ENU_MAP fixed, MAP_BASE2 orientation, BASE_ANTENA.
- */
-TEST_F(FactorGnssSingleDiff2DTest, gnss_1_map_base_position)
-{
-    // Create GNSS Fix capture
-    CaptureGnssSingleDiffPtr cap_gnss_ptr = std::make_shared<CaptureGnssSingleDiff>(TimeStamp(0), gnss_sensor_ptr, t_ecef_antena2-t_ecef_antena1, 1e-6*Matrix3s::Identity(), prior_frame_ptr);
-    gnss_sensor_ptr->process(cap_gnss_ptr);
-
-    // fixing things
-    gnss_sensor_ptr->setEnuMapYawState(o_enu_map(0)); // enu-map yaw orientation
-    gnss_sensor_ptr->getEnuMapYaw()->fix();
-    cap_gnss_ptr->getFrame()->getO()->setState(o_map_base2); // frame orientation
-    cap_gnss_ptr->getFrame()->getO()->fix();
-
-    std::cout << "frame1: " << prior_frame_ptr->getState().transpose() << std::endl;
-
-    // distort frm position
-    Vector3s frm_dist = cap_gnss_ptr->getFrame()->getState();
-    frm_dist(0) += 18;
-    frm_dist(1) += -58;
-    cap_gnss_ptr->getFrame()->setState(frm_dist);
-
-    // solve for frm
-    std::string report = ceres_mgr_ptr->solve(SolverManager::ReportVerbosity::BRIEF);
-
-    ASSERT_TRUE(cap_gnss_ptr->getFrame()->isKey());
-    ASSERT_MATRIX_APPROX(cap_gnss_ptr->getFrame()->getState().head(2), t_map_base2.head(2), 1e-6);
-}
-
-/*
- * Test with one GNSS Single Difference capture.
- *
- * Estimating: MAP_BASE2 orientation.
- * Fixed: MAP_BASE1, ENU_MAP fixed, MAP_BASE2 position, BASE_ANTENA.
- */
-TEST_F(FactorGnssSingleDiff2DTest, gnss_1_map_base_orientation)
-{
-    ASSERT_TRUE(prior_frame_ptr != nullptr);
-    // Create GNSS Fix capture
-    CaptureGnssSingleDiffPtr cap_gnss_ptr = std::make_shared<CaptureGnssSingleDiff>(TimeStamp(0), gnss_sensor_ptr, t_ecef_antena2-t_ecef_antena1, 1e-6*Matrix3s::Identity(), prior_frame_ptr);
-    ASSERT_TRUE(cap_gnss_ptr->getOriginFrame() != nullptr);
-    gnss_sensor_ptr->process(cap_gnss_ptr);
-
-    // fixing things
-    gnss_sensor_ptr->setEnuMapYawState(o_enu_map(0)); // enu-map yaw orientation
-    gnss_sensor_ptr->getEnuMapYaw()->fix();
-    cap_gnss_ptr->getFrame()->getP()->setState(t_map_base2.head(2)); // frame position
-    cap_gnss_ptr->getFrame()->getP()->fix();
-    // distort frm position
-    Vector1s frm_dist = cap_gnss_ptr->getFrame()->getO()->getState();
-    frm_dist(0) += 1.8;
-    cap_gnss_ptr->getFrame()->getO()->setState(frm_dist);
-
-    // solve for frm
-    std::string report = ceres_mgr_ptr->solve(SolverManager::ReportVerbosity::QUIET);
-
-    ASSERT_TRUE(cap_gnss_ptr->getFrame()->isKey());
-    ASSERT_MATRIX_APPROX(cap_gnss_ptr->getFrame()->getO()->getState(), o_map_base2, 1e-6);
-}
-
-/*
- * Test with one GNSS Single Difference capture.
- *
- * Estimating: ENU_MAP yaw.
- * Fixed: MAP_BASE1, MAP_BASE2 and BASE_ANTENA.
- */
-TEST_F(FactorGnssSingleDiff2DTest, gnss_1_enu_map_yaw)
-{
-    // Create GNSS Fix capture
-    CaptureGnssSingleDiffPtr cap_gnss_ptr = std::make_shared<CaptureGnssSingleDiff>(TimeStamp(0), gnss_sensor_ptr, t_ecef_antena2-t_ecef_antena1, 1e-6*Matrix3s::Identity(), prior_frame_ptr);
-    gnss_sensor_ptr->process(cap_gnss_ptr);
-
-    // unfixing things
-    gnss_sensor_ptr->getEnuMapYaw()->unfix();
-    // fixing things
-    cap_gnss_ptr->getFrame()->getP()->setState(t_map_base2.head(2)); // frame position
-    cap_gnss_ptr->getFrame()->getP()->fix();
-    cap_gnss_ptr->getFrame()->getO()->setState(o_map_base2); // frame orientation
-    cap_gnss_ptr->getFrame()->getO()->fix();
-
-    // distort yaw enu_map
-    Vector1s o_enu_map_dist = gnss_sensor_ptr->getEnuMapYaw()->getState();
-    o_enu_map_dist(0) += 1.8;
-    gnss_sensor_ptr->setEnuMapYawState(o_enu_map_dist(0));
-
-    // solve for frm
-    std::string report = ceres_mgr_ptr->solve(SolverManager::ReportVerbosity::QUIET);
-
-    ASSERT_TRUE(cap_gnss_ptr->getFrame()->isKey());
-    ASSERT_MATRIX_APPROX(gnss_sensor_ptr->getEnuMapYaw()->getState(), o_enu_map, 1e-6);
-}
-
-/*
- * Test with one GNSS Single Difference capture.
- *
- * Estimating: BASE_ANTENA (2 first components obsevable).
- * Fixed: MAP_BASE1, ENU_MAP, MAP_BASE2.
- */
-TEST_F(FactorGnssSingleDiff2DTest, gnss_1_base_antena)
-{
-    // Create GNSS Fix capture
-    CaptureGnssSingleDiffPtr cap_gnss_ptr = std::make_shared<CaptureGnssSingleDiff>(TimeStamp(0), gnss_sensor_ptr, t_ecef_antena2-t_ecef_antena1, 1e-6*Matrix3s::Identity(), prior_frame_ptr);
-    gnss_sensor_ptr->process(cap_gnss_ptr);
-
-    // unfixing things
-    gnss_sensor_ptr->getP()->unfix();
-    // fixing things
-    gnss_sensor_ptr->setEnuMapYawState(o_enu_map(0)); // enu-map yaw orientation
-    gnss_sensor_ptr->getEnuMapYaw()->fix();
-    cap_gnss_ptr->getFrame()->getP()->setState(t_map_base2.head(2)); // frame position
-    cap_gnss_ptr->getFrame()->getP()->fix();
-    cap_gnss_ptr->getFrame()->getO()->setState(o_map_base2); // frame orientation
-    cap_gnss_ptr->getFrame()->getO()->fix();
-
-    // distort base_antena
-    Vector3s base_antena_dist = gnss_sensor_ptr->getP()->getState();
-    base_antena_dist(0) += 16.8;
-    base_antena_dist(0) += -40.8;
-    gnss_sensor_ptr->getP()->setState(base_antena_dist);
-
-    // solve for frm
-    std::string report = ceres_mgr_ptr->solve(SolverManager::ReportVerbosity::QUIET);
-
-    ASSERT_TRUE(cap_gnss_ptr->getFrame()->isKey());
-    ASSERT_MATRIX_APPROX(gnss_sensor_ptr->getP()->getState().head(2), t_base_antena.head(2), 1e-6);
-}
-
-int main(int argc, char **argv)
-{
-  testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
-}
-
diff --git a/test/gtest_factor_gnss_tdcp.cpp b/test/gtest_factor_gnss_tdcp.cpp
new file mode 100644
index 000000000..7818ab4a9
--- /dev/null
+++ b/test/gtest_factor_gnss_tdcp.cpp
@@ -0,0 +1,440 @@
+//--------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/ceres_wrapper/solver_ceres.h>
+
+#include "gnss/factor/factor_gnss_tdcp.h"
+#include <core/utils/utils_gtest.h>
+
+#include "core/problem/problem.h"
+#include "gnss/sensor/sensor_gnss.h"
+#include "gnss/capture/capture_gnss.h"
+
+
+using namespace Eigen;
+using namespace wolf;
+using namespace GnssUtils;
+
+// groundtruth transformations
+Vector3d t_ecef_enu, t_enu_map, t_map_base_r, t_map_base_k, t_base_antena; // Antena extrinsics
+Vector3d t_ecef_antena_r, t_ecef_antena_k;
+Vector3d rpy_enu_map;
+Matrix3d R_ecef_enu, R_enu_map;
+Quaterniond q_map_base_r, q_map_base_k;
+//double range1_r, range2_r, range3_r, range4_r, range1_k, range2_k, range3_k, range4_k;
+Vector1d clock_drift_r, clock_drift_k;
+
+GnssUtils::Satellite sat1_r({SYS_GPS, 1,Eigen::Vector3d::Zero(),Eigen::Vector3d::Zero(),0,0,0});
+GnssUtils::Satellite sat2_r({SYS_GPS, 1,Eigen::Vector3d::Zero(),Eigen::Vector3d::Zero(),0,0,0});
+GnssUtils::Satellite sat3_r({SYS_GPS, 1,Eigen::Vector3d::Zero(),Eigen::Vector3d::Zero(),0,0,0});
+GnssUtils::Satellite sat4_r({SYS_GPS, 1,Eigen::Vector3d::Zero(),Eigen::Vector3d::Zero(),0,0,0});
+GnssUtils::Satellite sat1_k({SYS_GPS, 1,Eigen::Vector3d::Zero(),Eigen::Vector3d::Zero(),0,0,0});
+GnssUtils::Satellite sat2_k({SYS_GPS, 1,Eigen::Vector3d::Zero(),Eigen::Vector3d::Zero(),0,0,0});
+GnssUtils::Satellite sat3_k({SYS_GPS, 1,Eigen::Vector3d::Zero(),Eigen::Vector3d::Zero(),0,0,0});
+GnssUtils::Satellite sat4_k({SYS_GPS, 1,Eigen::Vector3d::Zero(),Eigen::Vector3d::Zero(),0,0,0});
+GnssUtils::Range range1_r, range2_r, range3_r, range4_r, range1_k, range2_k, range3_k, range4_k;
+
+// WOLF
+ProblemPtr prb = Problem::create("PO", 3);
+SolverCeresPtr solver = std::make_shared<SolverCeres>(prb);
+SensorGnssPtr gnss_sensor = std::static_pointer_cast<SensorGnss>(prb->installSensor("SensorGnss", "gnss", t_base_antena, std::make_shared<ParamsSensorGnss>()));
+FrameBasePtr frm_r, frm_k;
+CaptureGnssPtr cap_r, cap_k;
+FeatureGnssSatellitePtr ftr1_r, ftr2_r, ftr3_r, ftr4_r, ftr1_k, ftr2_k, ftr3_k, ftr4_k;
+FactorGnssTdcpPtr fac1, fac2, fac3, fac4;
+
+void randomGroundtruth()
+{
+    // ECEF-ENU
+    t_ecef_enu = Vector3d::Random() * 1e3;
+    Vector3d rpy_ecef_enu = M_PI*Vector3d::Random();
+    R_ecef_enu = (AngleAxis<double>(rpy_ecef_enu(0), Vector3d::UnitX()) *
+                  AngleAxis<double>(rpy_ecef_enu(1), Vector3d::UnitY()) *
+                  AngleAxis<double>(rpy_ecef_enu(2), Vector3d::UnitZ())).toRotationMatrix();
+
+    // ENU-MAP
+    t_enu_map = Vector3d::Random() * 1e3;
+    rpy_enu_map = M_PI*Vector3d::Random();
+    R_enu_map  = (AngleAxis<double>(rpy_enu_map(0), Vector3d::UnitX()) *
+                  AngleAxis<double>(rpy_enu_map(1), Vector3d::UnitY()) *
+                  AngleAxis<double>(rpy_enu_map(2), Vector3d::UnitZ())).toRotationMatrix();
+
+    // MAP-BASE
+    t_map_base_k = Vector3d::Random() * 1e2;
+    q_map_base_k = Quaterniond::UnitRandom();
+    t_map_base_r = Vector3d::Random() * 1e2;
+    q_map_base_r = Quaterniond::UnitRandom();
+
+    // BASE-ANTENA (EXTRINSICS)
+    t_base_antena = Vector3d::Random();
+
+    // composition
+    t_ecef_antena_r = R_ecef_enu * (R_enu_map * (q_map_base_r * t_base_antena + t_map_base_r) + t_enu_map ) + t_ecef_enu;
+    t_ecef_antena_k = R_ecef_enu * (R_enu_map * (q_map_base_k * t_base_antena + t_map_base_k) + t_enu_map ) + t_ecef_enu;
+
+    // SATELLITES
+    sat1_r.pos = Vector3d::Random() * 1e4;
+    sat2_r.pos = Vector3d::Random() * 1e4;
+    sat3_r.pos = Vector3d::Random() * 1e4;
+    sat4_r.pos = Vector3d::Random() * 1e4;
+    sat1_k.pos = Vector3d::Random() * 1e4;
+    sat2_k.pos = Vector3d::Random() * 1e4;
+    sat3_k.pos = Vector3d::Random() * 1e4;
+    sat4_k.pos = Vector3d::Random() * 1e4;
+
+    // clock drift
+    clock_drift_r = Vector1d::Random() * 1e2;
+    clock_drift_k = Vector1d::Random() * 1e2;
+
+    // ranges
+    range1_r.L_corrected = (sat1_r.pos-t_ecef_antena_r).norm() + clock_drift_r(0);
+    range2_r.L_corrected = (sat2_r.pos-t_ecef_antena_r).norm() + clock_drift_r(0);
+    range3_r.L_corrected = (sat3_r.pos-t_ecef_antena_r).norm() + clock_drift_r(0);
+    range4_r.L_corrected = (sat4_r.pos-t_ecef_antena_r).norm() + clock_drift_r(0);
+    range1_k.L_corrected = (sat1_k.pos-t_ecef_antena_k).norm() + clock_drift_k(0);
+    range2_k.L_corrected = (sat2_k.pos-t_ecef_antena_k).norm() + clock_drift_k(0);
+    range3_k.L_corrected = (sat3_k.pos-t_ecef_antena_k).norm() + clock_drift_k(0);
+    range4_k.L_corrected = (sat4_k.pos-t_ecef_antena_k).norm() + clock_drift_k(0);
+}
+
+void setUpProblem()
+{
+    // remove previous setup
+    if (frm_r)
+        frm_r->remove();
+    if (frm_k)
+        frm_k->remove();
+
+    solver->getSolverOptions().max_num_iterations = 100;
+
+    // Sensor
+    // ENU-MAP
+    gnss_sensor->setEnuMapTranslationState(t_enu_map);
+    gnss_sensor->setEnuMapRollState(rpy_enu_map(0));
+    gnss_sensor->setEnuMapPitchState(rpy_enu_map(1));
+    gnss_sensor->setEnuMapYawState(rpy_enu_map(2));
+    // ECEF-ENU
+    gnss_sensor->setEnuEcef(R_ecef_enu.transpose(), R_ecef_enu.transpose()*(-t_ecef_enu));
+    // Extrinsics
+    gnss_sensor->getP()->setState(t_base_antena);
+
+    // Frame r
+    Vector7d frm_r_state;
+    frm_r_state.head<3>() = t_map_base_r;
+    frm_r_state.tail<4>() = q_map_base_r.coeffs();
+    frm_r = prb->emplaceFrame( TimeStamp(0), frm_r_state);
+
+    // Frame k
+    Vector7d frm_k_state;
+    frm_k_state.head<3>() = t_map_base_k;
+    frm_k_state.tail<4>() = q_map_base_k.coeffs();
+    frm_k = prb->emplaceFrame( TimeStamp(1), frm_k_state);
+
+    // capture r
+    cap_r = CaptureBase::emplace<CaptureGnss>(frm_r, TimeStamp(0), gnss_sensor, nullptr);
+    cap_r->getStateBlock('T')->unfix();
+    cap_r->getStateBlock('T')->setState(clock_drift_r);
+
+    // capture k
+    cap_k = CaptureBase::emplace<CaptureGnss>(frm_k, TimeStamp(1), gnss_sensor, nullptr);
+    cap_k->getStateBlock('T')->unfix();
+    cap_k->getStateBlock('T')->setState(clock_drift_k);
+
+    // features r
+    ftr1_r = FeatureBase::emplace<FeatureGnssSatellite>(cap_r, obsd_t(), sat1_r, range1_r);
+    ftr2_r = FeatureBase::emplace<FeatureGnssSatellite>(cap_r, obsd_t(), sat2_r, range2_r);
+    ftr3_r = FeatureBase::emplace<FeatureGnssSatellite>(cap_r, obsd_t(), sat3_r, range3_r);
+    ftr4_r = FeatureBase::emplace<FeatureGnssSatellite>(cap_r, obsd_t(), sat4_r, range4_r);
+
+    // features k
+    ftr1_k = FeatureBase::emplace<FeatureGnssSatellite>(cap_k, obsd_t(), sat1_k, range1_k);
+    ftr2_k = FeatureBase::emplace<FeatureGnssSatellite>(cap_k, obsd_t(), sat2_k, range2_k);
+    ftr3_k = FeatureBase::emplace<FeatureGnssSatellite>(cap_k, obsd_t(), sat3_k, range3_k);
+    ftr4_k = FeatureBase::emplace<FeatureGnssSatellite>(cap_k, obsd_t(), sat4_k, range4_k);
+
+    // factors
+    fac1 = FactorBase::emplace<FactorGnssTdcp>(ftr1_r, 0.1, ftr1_r, ftr1_k, gnss_sensor, nullptr, false);
+    fac2 = FactorBase::emplace<FactorGnssTdcp>(ftr2_r, 0.1, ftr2_r, ftr2_k, gnss_sensor, nullptr, false);
+    fac3 = FactorBase::emplace<FactorGnssTdcp>(ftr3_r, 0.1, ftr3_r, ftr3_k, gnss_sensor, nullptr, false);
+    fac4 = FactorBase::emplace<FactorGnssTdcp>(ftr4_r, 0.1, ftr4_r, ftr4_k, gnss_sensor, nullptr, false);
+
+    // ASSERTS
+    // ENU-MAP
+    ASSERT_FLOAT_EQ(gnss_sensor->getEnuMapRoll()->getState()(0), rpy_enu_map(0));
+    ASSERT_FLOAT_EQ(gnss_sensor->getEnuMapPitch()->getState()(0), rpy_enu_map(1));
+    ASSERT_FLOAT_EQ(gnss_sensor->getEnuMapYaw()->getState()(0), rpy_enu_map(2));
+    ASSERT_MATRIX_APPROX(gnss_sensor->getEnuMapTranslation()->getState(), t_enu_map, 1e-3);
+    // Antena
+    ASSERT_MATRIX_APPROX(gnss_sensor->getP()->getState(), t_base_antena, 1e-3);
+    // Frame r
+    ASSERT_MATRIX_APPROX(frm_r->getP()->getState(), t_map_base_r, 1e-3);
+    ASSERT_MATRIX_APPROX(frm_r->getO()->getState(), q_map_base_r.coeffs(), 1e-3);
+    // Frame k
+    ASSERT_MATRIX_APPROX(frm_k->getP()->getState(), t_map_base_k, 1e-3);
+    ASSERT_MATRIX_APPROX(frm_k->getO()->getState(), q_map_base_k.coeffs(), 1e-3);
+    // clock drift
+    ASSERT_MATRIX_APPROX(clock_drift_r, cap_r->getStateBlock('T')->getState(), 1e-3);
+    ASSERT_MATRIX_APPROX(clock_drift_k, cap_k->getStateBlock('T')->getState(), 1e-3);
+}
+
+void fixAllStates()
+{
+    // ENU-MAP
+    gnss_sensor->getEnuMapRoll()->fix();
+    gnss_sensor->getEnuMapPitch()->fix();
+    gnss_sensor->getEnuMapYaw()->fix();
+    gnss_sensor->getEnuMapTranslation()->fix();
+    // Antena
+    gnss_sensor->getP()->fix();
+    // Frames
+    frm_r->fix();
+    frm_k->fix();
+    // clock drift
+    cap_r->fix();
+    cap_k->fix();
+ }
+
+////////////////////////////////////////////////////////
+TEST(FactorGnssTdcpTest, observe_clock_drift_r)
+{
+    for (auto i = 0; i < 100; i++)
+    {
+        // setup random problem
+        randomGroundtruth();
+        setUpProblem();
+
+        // fix/unfix
+        fixAllStates();
+        cap_r->getStateBlock('T')->unfix();
+
+        // perturb
+        cap_r->getStateBlock('T')->perturb(1e2);
+
+        // Only 1 factor
+        fac2->setStatus(FAC_INACTIVE);
+        fac3->setStatus(FAC_INACTIVE);
+        fac4->setStatus(FAC_INACTIVE);
+
+        // solve
+        std::string report = solver->solve(SolverManager::ReportVerbosity::FULL);
+        //std::cout << report << std::endl;
+
+        ASSERT_MATRIX_APPROX(clock_drift_r, cap_r->getStateBlock('T')->getState(), 1e-3);
+    }
+}
+
+TEST(FactorGnssTdcpTest, observe_clock_drift_k)
+{
+    for (auto i = 0; i < 100; i++)
+    {
+        // setup random problem
+        randomGroundtruth();
+        setUpProblem();
+
+        // fix/unfix
+        fixAllStates();
+        cap_k->getStateBlock('T')->unfix();
+
+        // perturb
+        cap_k->getStateBlock('T')->perturb(1e2);
+
+        // Only 1 factor
+        fac2->setStatus(FAC_INACTIVE);
+        fac3->setStatus(FAC_INACTIVE);
+        fac4->setStatus(FAC_INACTIVE);
+
+        // solve
+        std::string report = solver->solve(SolverManager::ReportVerbosity::FULL);
+        //std::cout << report << std::endl;
+
+        ASSERT_MATRIX_APPROX(clock_drift_k, cap_k->getStateBlock('T')->getState(), 1e-3);
+    }
+}
+
+TEST(FactorGnssTdcpTest, observe_frame_p_r)
+{
+    for (auto i = 0; i < 100; i++)
+    {
+        // setup random problem
+        randomGroundtruth();
+        setUpProblem();
+
+        // fix/unfix
+        fixAllStates();
+        frm_r->getP()->unfix();
+
+        // perturb
+        frm_r->getP()->perturb(1);
+
+        // Only 3 factors
+        fac4->setStatus(FAC_INACTIVE);
+
+        // solve
+        std::string report = solver->solve(SolverManager::ReportVerbosity::FULL);
+        //std::cout << report << std::endl;
+
+        ASSERT_MATRIX_APPROX(frm_r->getP()->getState(), t_map_base_r, 1e-3);
+    }
+}
+
+TEST(FactorGnssTdcpTest, observe_frame_p_k)
+{
+    for (auto i = 0; i < 100; i++)
+    {
+        // setup random problem
+        randomGroundtruth();
+        setUpProblem();
+
+        // fix/unfix
+        fixAllStates();
+        frm_k->getP()->unfix();
+
+        // perturb
+        frm_k->getP()->perturb(1);
+
+        // Only 3 factors
+        fac4->setStatus(FAC_INACTIVE);
+
+        // solve
+        std::string report = solver->solve(SolverManager::ReportVerbosity::FULL);
+        //std::cout << report << std::endl;
+
+        ASSERT_MATRIX_APPROX(frm_k->getP()->getState(), t_map_base_k, 1e-3);
+    }
+}
+
+TEST(FactorGnssTdcpTest, observe_frame_p_clock_k)
+{
+    for (auto i = 0; i < 100; i++)
+    {
+        // setup random problem
+        randomGroundtruth();
+        setUpProblem();
+
+        // fix/unfix
+        fixAllStates();
+        frm_k->getP()->unfix();
+        cap_k->getStateBlock('T')->unfix();
+
+        // perturb
+        frm_k->getP()->perturb(1);
+        cap_k->getStateBlock('T')->perturb(1e2);
+
+        // all 4 factors
+
+        // solve
+        std::string report = solver->solve(SolverManager::ReportVerbosity::FULL);
+        //std::cout << report << std::endl;
+
+        ASSERT_MATRIX_APPROX(frm_k->getP()->getState(), t_map_base_k, 1e-3);
+        ASSERT_MATRIX_APPROX(clock_drift_k, cap_k->getStateBlock('T')->getState(), 1e-3);
+    }
+}
+
+TEST(FactorGnssTdcpTest, observe_frame_p_clock_r)
+{
+    for (auto i = 0; i < 100; i++)
+    {
+        // setup random problem
+        randomGroundtruth();
+        setUpProblem();
+
+        // fix/unfix
+        fixAllStates();
+        frm_r->getP()->unfix();
+        cap_r->getStateBlock('T')->unfix();
+
+        // perturb
+        frm_r->getP()->perturb(1);
+        cap_r->getStateBlock('T')->perturb(1e2);
+
+        // all 4 factors
+
+        // solve
+        std::string report = solver->solve(SolverManager::ReportVerbosity::FULL);
+        //std::cout << report << std::endl;
+
+        ASSERT_MATRIX_APPROX(frm_r->getP()->getState(), t_map_base_r, 1e-3);
+        ASSERT_MATRIX_APPROX(clock_drift_r, cap_r->getStateBlock('T')->getState(), 1e-3);
+    }
+}
+
+TEST(FactorGnssTdcpTest, observe_enumap_p)
+{
+    for (auto i = 0; i < 100; i++)
+    {
+        // setup random problem
+        randomGroundtruth();
+        setUpProblem();
+
+        // fix/unfix
+        fixAllStates();
+        gnss_sensor->getEnuMapTranslation()->unfix();
+
+        // perturb
+        gnss_sensor->getEnuMapTranslation()->perturb(1e2);
+
+        // Only 3 factors
+        fac4->setStatus(FAC_INACTIVE);
+
+        // solve
+        std::string report = solver->solve(SolverManager::ReportVerbosity::FULL);
+        //std::cout << report << std::endl;
+
+        ASSERT_MATRIX_APPROX(gnss_sensor->getEnuMapTranslation()->getState(), t_enu_map, 1e-3);
+    }
+}
+
+TEST(FactorGnssTdcpTest, observe_enumap_o)
+{
+    for (auto i = 0; i < 100; i++)
+    {
+        // setup random problem
+        randomGroundtruth();
+        setUpProblem();
+
+        // fix/unfix
+        fixAllStates();
+        gnss_sensor->getEnuMapRoll()->unfix();
+        gnss_sensor->getEnuMapPitch()->unfix();
+        gnss_sensor->getEnuMapYaw()->unfix();
+
+        // perturb
+        gnss_sensor->getEnuMapRoll()->perturb(1);
+        gnss_sensor->getEnuMapPitch()->perturb(1);
+        gnss_sensor->getEnuMapYaw()->perturb(1);
+
+        // Only 3 factors
+        fac4->setStatus(FAC_INACTIVE);
+
+        // solve
+        std::string report = solver->solve(SolverManager::ReportVerbosity::FULL);
+        //std::cout << report << std::endl;
+
+        ASSERT_MATRIX_APPROX(gnss_sensor->getEnuMapTranslation()->getState(), t_enu_map, 1e-3);
+    }
+}
+
+int main(int argc, char **argv)
+{
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
-- 
GitLab