diff --git a/CMakeLists.txt b/CMakeLists.txt
index 161bfba7b1dc0d8bcba7e67d89e98a92e0413c5a..79f3a083a35fade4c72b0ea711b365d937864f2f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,6 +14,7 @@ find_package(catkin REQUIRED COMPONENTS
   iri_gnss_msgs
   std_msgs
   tf
+  tf_conversions
   dynamic_reconfigure
   wolf_ros_node
   novatel_oem7_msgs
@@ -135,9 +136,13 @@ include_directories(
 # add_library(${PROJECT_NAME}
 #   src/${PROJECT_NAME}/wolf_ros.cpp
 # )
+add_library(publisher_${PROJECT_NAME}
+  src/publisher_gnss_tf.cpp)
 add_library(subscriber_${PROJECT_NAME}
   src/subscriber_gnss.cpp
   src/subscriber_gnss_fix.cpp
+  src/subscriber_gnss_fix_publisher_ecef.cpp
+  src/subscriber_gnss_fix_novatel_publisher_ecef.cpp
   src/subscriber_gnss_tdcp.cpp
   src/subscriber_gnss_receiver.cpp
   src/subscriber_gnss_ublox.cpp
@@ -171,6 +176,13 @@ target_link_libraries(subscriber_${PROJECT_NAME}
             ${wolf_LIBRARIES}
             ${wolfgnss_LIBRARIES}
             ${iri_gnss_msgs_LIBRARIES}
+            ${catkin_LIBRARIES}
+            )
+target_link_libraries(publisher_${PROJECT_NAME}
+            ${wolf_LIBRARIES}
+            ${wolfgnss_LIBRARIES}
+            ${iri_gnss_msgs_LIBRARIES}
+            ${catkin_LIBRARIES}
             )
 
 #############
diff --git a/include/publisher_gnss_tf.h b/include/publisher_gnss_tf.h
new file mode 100644
index 0000000000000000000000000000000000000000..b88c88b3b428992868f049e759ac095ef072cdf9
--- /dev/null
+++ b/include/publisher_gnss_tf.h
@@ -0,0 +1,40 @@
+#ifndef PUBLISHER_GNSS_TF_H
+#define PUBLISHER_GNSS_TF_H
+
+/**************************
+ *      WOLF includes     *
+ **************************/
+#include "core/problem/problem.h"
+#include "gnss/sensor/sensor_gnss.h"
+
+#include "publisher.h"
+
+#include <tf/transform_broadcaster.h>
+
+namespace wolf
+{
+
+class PublisherGnssTf: public Publisher
+{
+        std::string map_frame_id_, enu_frame_id_, ecef_frame_id_;
+        tf::StampedTransform T_enu_map_, T_ecef_enu_;
+        tf::TransformBroadcaster tfb_;
+        SensorGnssPtr sensor_gnss_;
+
+    public:
+        PublisherGnssTf(const std::string& _unique_name,
+                       const ParamsServer& _server,
+                       const ProblemPtr _problem);
+        WOLF_PUBLISHER_CREATE(PublisherGnssTf);
+
+        virtual ~PublisherGnssTf(){};
+
+        void initialize(ros::NodeHandle &nh, const std::string& topic) override;
+
+        void publishDerived() override;
+};
+
+WOLF_REGISTER_PUBLISHER(PublisherGnssTf)
+}
+
+#endif
diff --git a/include/subscriber_gnss_fix_novatel_publisher_ecef.h b/include/subscriber_gnss_fix_novatel_publisher_ecef.h
new file mode 100644
index 0000000000000000000000000000000000000000..7d905ad1792d23fffed60bae3dc5a19980b53581
--- /dev/null
+++ b/include/subscriber_gnss_fix_novatel_publisher_ecef.h
@@ -0,0 +1,41 @@
+#ifndef SUBSCRIBER_GNSS_FIX_NOVATEL_PUBLISHER_ECEF_H
+#define SUBSCRIBER_GNSS_FIX_NOVATEL_PUBLISHER_ECEF_H
+
+/**************************
+ *      ROS includes      *
+ **************************/
+#include <ros/ros.h>
+#include "novatel_oem7_msgs/BESTPOS.h"
+
+/**************************
+ *      STD includes      *
+ **************************/
+#include <iostream>
+#include <iomanip>
+#include <queue>
+
+/**************************
+ *    WOLF-ROS includes   *
+ **************************/
+#include "subscriber_gnss_fix_publisher_ecef.h"
+
+namespace wolf
+{
+
+class SubscriberGnssFixNovatelPublisherEcef : public SubscriberGnssFixPublisherEcef
+{
+    public:
+        // Constructor
+        SubscriberGnssFixNovatelPublisherEcef(const std::string& _unique_name,
+                          const ParamsServer& _server,
+                          const SensorBasePtr _sensor_ptr);
+        WOLF_SUBSCRIBER_CREATE(SubscriberGnssFixNovatelPublisherEcef);
+
+        void initialize(ros::NodeHandle& nh, const std::string& topic) override;
+
+        void callback(const novatel_oem7_msgs::BESTPOS::ConstPtr& msg);
+};
+WOLF_REGISTER_SUBSCRIBER(SubscriberGnssFixNovatelPublisherEcef)
+
+}
+#endif
diff --git a/include/subscriber_gnss_fix_publisher_ecef.h b/include/subscriber_gnss_fix_publisher_ecef.h
new file mode 100644
index 0000000000000000000000000000000000000000..71221dae6467a43c2a4c353a967a0d73251ed61e
--- /dev/null
+++ b/include/subscriber_gnss_fix_publisher_ecef.h
@@ -0,0 +1,57 @@
+#ifndef SUBSCRIBER_GNSS_FIX_PUBLISHER_ECEF_H
+#define SUBSCRIBER_GNSS_FIX_PUBLISHER_ECEF_H
+
+/**************************
+ *      ROS includes      *
+ **************************/
+#include <ros/ros.h>
+#include <sensor_msgs/NavSatFix.h>
+#include <nav_msgs/Odometry.h>
+#include <tf/transform_listener.h>
+
+/**************************
+ *      STD includes      *
+ **************************/
+#include <iostream>
+#include <iomanip>
+#include <queue>
+
+/**************************
+ *    WOLF-ROS includes   *
+ **************************/
+#include "subscriber.h"
+
+namespace wolf
+{
+
+class SubscriberGnssFixPublisherEcef : public Subscriber
+{
+    protected:
+        ros::Publisher pub_;
+        tf::TransformListener tfl_;
+        bool ENU_defined_;
+        Eigen::Matrix3d R_enu_ecef_;
+        Eigen::Vector3d t_enu_ecef_;
+        nav_msgs::Odometry odom_msg_;
+        ros::Time last_publish_stamp_;
+        double period_;
+
+    public:
+        // Constructor
+        SubscriberGnssFixPublisherEcef(const std::string& _unique_name,
+                          const ParamsServer& _server,
+                          const SensorBasePtr _sensor_ptr);
+        WOLF_SUBSCRIBER_CREATE(SubscriberGnssFixPublisherEcef);
+
+        virtual void initialize(ros::NodeHandle& nh, const std::string& topic);
+
+        void callback(const sensor_msgs::NavSatFix::ConstPtr& msg);
+
+        void computeAndPublish(const Eigen::Vector3d& LatLonAlt, const ros::Time& stamp);
+
+        bool listenTf();
+};
+WOLF_REGISTER_SUBSCRIBER(SubscriberGnssFixPublisherEcef)
+
+}
+#endif
diff --git a/src/publisher_gnss_tf.cpp b/src/publisher_gnss_tf.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eafb1bbca4c2198999921d55993ac47bd6ed730f
--- /dev/null
+++ b/src/publisher_gnss_tf.cpp
@@ -0,0 +1,70 @@
+#include "publisher_gnss_tf.h"
+
+/**************************
+ *      ROS includes      *
+ **************************/
+#include <ros/ros.h>
+#include "tf/transform_datatypes.h"
+#include "tf_conversions/tf_eigen.h"
+
+namespace wolf
+{
+
+PublisherGnssTf::PublisherGnssTf(const std::string& _unique_name,
+                                 const ParamsServer& _server,
+                                 const ProblemPtr _problem) :
+        Publisher(_unique_name, _server, _problem)
+{
+    sensor_gnss_ = std::static_pointer_cast<SensorGnss>(_problem->getSensor(_server.getParam<std::string>(prefix_ + "/sensor_gnss_name")));
+
+    T_enu_map_.setIdentity();
+    T_enu_map_.frame_id_ = "ENU";
+    T_enu_map_.child_frame_id_ = "map";
+    T_enu_map_.stamp_ = ros::Time::now();
+
+    T_ecef_enu_.setIdentity();
+    T_ecef_enu_.frame_id_ = "ECEF";
+    T_ecef_enu_.child_frame_id_ = "ENU";
+    T_ecef_enu_.stamp_ = ros::Time::now();
+}
+
+void PublisherGnssTf::initialize(ros::NodeHandle& nh, const std::string& topic)
+{
+    //
+}
+
+void PublisherGnssTf::publishDerived()
+{
+    tf::Matrix3x3 tf_R_enu_map, tf_R_enu_ecef;
+    tf::Vector3 tf_t_enu_map, tf_t_enu_ecef;
+
+    if (not sensor_gnss_->isEnuDefined())
+        return;
+
+    //Eigen::Matrix3d ei_R_enu_ecef = sensor_gnss_->getREnuEcef();
+    //Eigen::Vector3d ei_t_enu_ecef = sensor_gnss_->gettEnuEcef();
+
+    //Eigen::Matrix3d ei_R_enu_map  = sensor_gnss_->getREnuMap();
+    //Eigen::Vector3d ei_t_enu_map  = sensor_gnss_->gettEnuMap();
+
+    //tf::matrixEigenToTF(ei_R_enu_ecef, tf_R_enu_ecef);
+    //tf::vectorEigenToTF(ei_t_enu_ecef, tf_t_enu_ecef);
+    tf::matrixEigenToTF(sensor_gnss_->getREnuEcef(), tf_R_enu_ecef);
+    tf::vectorEigenToTF(sensor_gnss_->gettEnuEcef(), tf_t_enu_ecef);
+
+    //tf::matrixEigenToTF(ei_R_enu_map,  tf_R_enu_map);
+    //tf::vectorEigenToTF(ei_t_enu_map,  tf_t_enu_map);
+    tf::matrixEigenToTF(sensor_gnss_->getREnuMap(),  tf_R_enu_map);
+    tf::vectorEigenToTF(sensor_gnss_->gettEnuMap(),  tf_t_enu_map);
+
+    T_ecef_enu_.setData(tf::Transform(tf_R_enu_ecef, tf_t_enu_ecef).inverse());
+    T_enu_map_ .setData(tf::Transform(tf_R_enu_map,  tf_t_enu_map));
+
+    T_ecef_enu_.stamp_ = ros::Time::now();
+    T_enu_map_ .stamp_ = ros::Time::now();
+
+    tfb_.sendTransform(T_ecef_enu_);
+    tfb_.sendTransform(T_enu_map_);
+}
+
+}
diff --git a/src/subscriber_gnss_fix_novatel_publisher_ecef.cpp b/src/subscriber_gnss_fix_novatel_publisher_ecef.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..85d22cf475bab2c014c4d2791f4b8c57b519597d
--- /dev/null
+++ b/src/subscriber_gnss_fix_novatel_publisher_ecef.cpp
@@ -0,0 +1,31 @@
+#include "subscriber_gnss_fix_novatel_publisher_ecef.h"
+#include "gnss_utils/utils/transformations.h"
+#include "tf/transform_datatypes.h"
+
+namespace wolf
+{
+
+// Constructor
+SubscriberGnssFixNovatelPublisherEcef::SubscriberGnssFixNovatelPublisherEcef(const std::string& _unique_name,
+                                     const ParamsServer& _server,
+                                     const SensorBasePtr _sensor_ptr)
+    : SubscriberGnssFixPublisherEcef(_unique_name, _server, _sensor_ptr)
+{
+}
+
+
+void SubscriberGnssFixNovatelPublisherEcef::initialize(ros::NodeHandle& nh, const std::string& topic)
+{
+    sub_    = nh.subscribe(topic, 1, &SubscriberGnssFixNovatelPublisherEcef::callback, this);
+    pub_    = nh.advertise<nav_msgs::Odometry>(topic+"_ECEF", 1);
+}
+
+void SubscriberGnssFixNovatelPublisherEcef::callback(const novatel_oem7_msgs::BESTPOS::ConstPtr& _msg)
+{
+    computeAndPublish(Eigen::Vector3d(_msg->lat * M_PI / 180.0,
+                                      _msg->lon * M_PI / 180.0,
+                                      _msg->hgt),
+                      _msg->header.stamp);
+}
+
+}
diff --git a/src/subscriber_gnss_fix_publisher_ecef.cpp b/src/subscriber_gnss_fix_publisher_ecef.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..90f8729d931ffab4b221e9cb01b85fbb3be85708
--- /dev/null
+++ b/src/subscriber_gnss_fix_publisher_ecef.cpp
@@ -0,0 +1,77 @@
+#include "subscriber_gnss_fix_publisher_ecef.h"
+#include "gnss_utils/utils/transformations.h"
+#include "tf/transform_datatypes.h"
+#include "tf_conversions/tf_eigen.h"
+
+namespace wolf
+{
+
+// Constructor
+SubscriberGnssFixPublisherEcef::SubscriberGnssFixPublisherEcef(const std::string& _unique_name,
+                                     const ParamsServer& _server,
+                                     const SensorBasePtr _sensor_ptr)
+    : Subscriber(_unique_name, _server, _sensor_ptr)
+    , ENU_defined_(false)
+{
+    period_ = _server.getParam<double>(prefix_ + "/period");
+    odom_msg_.header.frame_id = "ENU";
+    odom_msg_.pose.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(0,M_PI/2,0);
+}
+
+
+void SubscriberGnssFixPublisherEcef::initialize(ros::NodeHandle& nh, const std::string& topic)
+{
+    sub_    = nh.subscribe(topic, 1, &SubscriberGnssFixPublisherEcef::callback, this);
+    pub_    = nh.advertise<nav_msgs::Odometry>(topic+"_ECEF", 1);
+}
+
+void SubscriberGnssFixPublisherEcef::callback(const sensor_msgs::NavSatFix::ConstPtr& _msg)
+{
+    computeAndPublish(Eigen::Vector3d(_msg->latitude * M_PI / 180.0,
+                                      _msg->longitude * M_PI / 180.0,
+                                      _msg->altitude),
+                      _msg->header.stamp);
+}
+
+void SubscriberGnssFixPublisherEcef::computeAndPublish(const Eigen::Vector3d& LatLonAlt, const ros::Time& stamp)
+{
+    // listen TF ECEF-ENU once
+    if (not ENU_defined_)
+        if (not listenTf())
+            return;
+
+    if ((stamp - last_publish_stamp_).toSec() < period_)
+        return;
+
+    // ECEF-ENU is defined
+    Eigen::Vector3d fix_ENU = R_enu_ecef_ * GnssUtils::latLonAltToEcef(LatLonAlt) + t_enu_ecef_;
+
+    // Fill Odometry msg
+    odom_msg_.header.stamp = stamp;
+
+    odom_msg_.pose.pose.position.x = fix_ENU(0);
+    odom_msg_.pose.pose.position.y = fix_ENU(1);
+    odom_msg_.pose.pose.position.z = fix_ENU(2);
+
+    pub_.publish(odom_msg_);
+
+    last_publish_stamp_ = stamp;
+}
+
+bool SubscriberGnssFixPublisherEcef::listenTf()
+{
+    tf::StampedTransform T_ecef2enu;
+    if ( tfl_.waitForTransform("ENU", "ECEF", ros::Time(0), ros::Duration(0.01)) )
+    {
+        tfl_.lookupTransform("ENU", "ECEF", ros::Time(0), T_ecef2enu);
+
+        tf::matrixTFToEigen(T_ecef2enu.getBasis(), R_enu_ecef_);
+        tf::vectorTFToEigen(T_ecef2enu.getOrigin(), t_enu_ecef_);
+
+        ENU_defined_ = true;
+        return true;
+    }
+    return false;
+}
+
+}