sys_time.cpp
Go to the documentation of this file.
00001 
00009 /*
00010  * Copyright 2014,2015 Vladimir Ermakov, M.H.Kabir.
00011  *
00012  * This file is part of the mavros package and subject to the license terms
00013  * in the top-level LICENSE file of the mavros repository.
00014  * https://github.com/mavlink/mavros/tree/master/LICENSE.md
00015  */
00016 
00017 #include <mavros/mavros_plugin.h>
00018 #include <pluginlib/class_list_macros.h>
00019 
00020 #include <sensor_msgs/TimeReference.h>
00021 #include <std_msgs/Duration.h>
00022 
00023 namespace mavplugin {
00029 class TimeSyncStatus : public diagnostic_updater::DiagnosticTask
00030 {
00031 public:
00032         TimeSyncStatus(const std::string &name, size_t win_size) :
00033                 diagnostic_updater::DiagnosticTask(name),
00034                 window_size_(win_size),
00035                 min_freq_(0.01),
00036                 max_freq_(10),
00037                 tolerance_(0.1),
00038                 times_(win_size),
00039                 seq_nums_(win_size),
00040                 last_dt(0),
00041                 dt_sum(0),
00042                 last_ts(0),
00043                 offset(0)
00044         {
00045                 clear();
00046         }
00047 
00048         void clear() {
00049                 lock_guard lock(mutex);
00050                 ros::Time curtime = ros::Time::now();
00051                 count_ = 0;
00052                 dt_sum = 0;
00053 
00054                 for (int i = 0; i < window_size_; i++)
00055                 {
00056                         times_[i] = curtime;
00057                         seq_nums_[i] = count_;
00058                 }
00059 
00060                 hist_indx_ = 0;
00061         }
00062 
00063         void tick(int64_t dt, uint64_t timestamp_ns, int64_t time_offset_ns) {
00064                 lock_guard lock(mutex);
00065                 count_++;
00066                 last_dt = dt;
00067                 dt_sum += dt;
00068                 last_ts = timestamp_ns;
00069                 offset = time_offset_ns;
00070         }
00071 
00072         void set_timestamp(uint64_t timestamp_ns) {
00073                 lock_guard lock(mutex);
00074                 last_ts = timestamp_ns;
00075         }
00076 
00077         void run(diagnostic_updater::DiagnosticStatusWrapper &stat) {
00078                 lock_guard lock(mutex);
00079                 ros::Time curtime = ros::Time::now();
00080                 int curseq = count_;
00081                 int events = curseq - seq_nums_[hist_indx_];
00082                 double window = (curtime - times_[hist_indx_]).toSec();
00083                 double freq = events / window;
00084                 seq_nums_[hist_indx_] = curseq;
00085                 times_[hist_indx_] = curtime;
00086                 hist_indx_ = (hist_indx_ + 1) % window_size_;
00087 
00088                 if (events == 0) {
00089                         stat.summary(2, "No events recorded.");
00090                 }
00091                 else if (freq < min_freq_ * (1 - tolerance_)) {
00092                         stat.summary(1, "Frequency too low.");
00093                 }
00094                 else if (freq > max_freq_ * (1 + tolerance_)) {
00095                         stat.summary(1, "Frequency too high.");
00096                 }
00097                 else {
00098                         stat.summary(0, "Normal");
00099                 }
00100 
00101                 stat.addf("Timesyncs since startup", "%d", count_);
00102                 stat.addf("Frequency (Hz)", "%f", freq);
00103                 stat.addf("Last dt (ms)", "%0.6f", last_dt / 1e6);
00104                 stat.addf("Mean dt (ms)", "%0.6f", (count_) ? dt_sum / count_ / 1e6 : 0.0);
00105                 stat.addf("Last system time (s)", "%0.9f", last_ts / 1e9);
00106                 stat.addf("Time offset (s)", "%0.9f", offset / 1e9);
00107         }
00108 
00109 private:
00110         int count_;
00111         std::vector<ros::Time> times_;
00112         std::vector<int> seq_nums_;
00113         int hist_indx_;
00114         std::recursive_mutex mutex;
00115         const size_t window_size_;
00116         const double min_freq_;
00117         const double max_freq_;
00118         const double tolerance_;
00119         int64_t last_dt;
00120         int64_t dt_sum;
00121         uint64_t last_ts;
00122         int64_t offset;
00123 };
00124 
00125 
00129 class SystemTimePlugin : public MavRosPlugin {
00130 public:
00131         SystemTimePlugin() :
00132                 nh("~"),
00133                 uas(nullptr),
00134                 dt_diag("Time Sync", 10),
00135                 time_offset_ns(0),
00136                 offset_avg_alpha(0)
00137         { };
00138 
00139         void initialize(UAS &uas_)
00140         {
00141                 double conn_system_time_d;
00142                 double conn_timesync_d;
00143 
00144                 ros::Duration conn_system_time;
00145                 ros::Duration conn_timesync;
00146 
00147                 uas = &uas_;
00148 
00149                 if (nh.getParam("conn/system_time_rate", conn_system_time_d) && conn_system_time_d != 0.0) {
00150                         conn_system_time = ros::Duration(ros::Rate(conn_system_time_d));
00151                 }
00152                 else if (nh.getParam("conn/system_time", conn_system_time_d)) {
00153                         // XXX deprecated parameter
00154                         ROS_WARN_NAMED("time", "TM: parameter `~conn/system_time` deprecated, "
00155                                 "please use `~conn/system_time_rate` instead!");
00156                         conn_system_time = ros::Duration(conn_system_time_d);
00157                 }
00158 
00159                 if (nh.getParam("conn/timesync_rate", conn_timesync_d) && conn_timesync_d != 0.0) {
00160                         conn_timesync = ros::Duration(ros::Rate(conn_timesync_d));
00161                 }
00162                 else if (nh.getParam("conn/timesync", conn_timesync_d)) {
00163                         // XXX deprecated parameter
00164                         ROS_WARN_NAMED("time", "TM: parameter `~conn/timesync` deprecated, "
00165                                 "please use `~conn/timesync_rate` instead!");
00166                         conn_timesync = ros::Duration(conn_timesync_d);
00167                 }
00168 
00169                 nh.param<std::string>("time/time_ref_source", time_ref_source, "fcu");
00170                 nh.param("time/timesync_avg_alpha", offset_avg_alpha, 0.6);
00171                 /*
00172                  * alpha for exponential moving average. The closer alpha is to 1.0,
00173                  * the faster the moving average updates in response to new offset samples (more jitter)
00174                  * We need a significant amount of smoothing , more so for lower message rates like 1Hz
00175                  */
00176 
00177                 time_ref_pub = nh.advertise<sensor_msgs::TimeReference>("time_reference", 10);
00178 
00179                 // timer for sending system time messages
00180                 if (!conn_system_time.isZero()) {
00181                         sys_time_timer = nh.createTimer(conn_system_time,
00182                                         &SystemTimePlugin::sys_time_cb, this);
00183                         sys_time_timer.start();
00184                 }
00185 
00186                 // timer for sending timesync messages
00187                 if (!conn_timesync.isZero()) {
00188                         // enable timesync diag only if that feature enabled
00189                         UAS_DIAG(uas).add(dt_diag);
00190 
00191                         timesync_timer = nh.createTimer(conn_timesync,
00192                                         &SystemTimePlugin::timesync_cb, this);
00193                         timesync_timer.start();
00194                 }
00195         }
00196 
00197         const message_map get_rx_handlers() {
00198                 return {
00199                                MESSAGE_HANDLER(MAVLINK_MSG_ID_SYSTEM_TIME, &SystemTimePlugin::handle_system_time),
00200                                MESSAGE_HANDLER(MAVLINK_MSG_ID_TIMESYNC, &SystemTimePlugin::handle_timesync),
00201                 };
00202         }
00203 
00204 private:
00205         ros::NodeHandle nh;
00206         UAS *uas;
00207         ros::Publisher time_ref_pub;
00208 
00209         ros::Timer sys_time_timer;
00210         ros::Timer timesync_timer;
00211 
00212         TimeSyncStatus dt_diag;
00213 
00214         std::string time_ref_source;
00215         int64_t time_offset_ns;
00216         double offset_avg_alpha;
00217 
00218         void handle_system_time(const mavlink_message_t *msg, uint8_t sysid, uint8_t compid) {
00219                 mavlink_system_time_t mtime;
00220                 mavlink_msg_system_time_decode(msg, &mtime);
00221 
00222                 // date -d @1234567890: Sat Feb 14 02:31:30 MSK 2009
00223                 const bool fcu_time_valid = mtime.time_unix_usec > 1234567890ULL * 1000000;
00224 
00225                 if (fcu_time_valid) {
00226                         // continious publish for ntpd
00227                         auto time_unix = boost::make_shared<sensor_msgs::TimeReference>();
00228                         ros::Time time_ref(
00229                                         mtime.time_unix_usec / 1000000,                 // t_sec
00230                                         (mtime.time_unix_usec % 1000000) * 1000);       // t_nsec
00231 
00232                         time_unix->header.stamp = ros::Time::now();
00233                         time_unix->time_ref = time_ref;
00234                         time_unix->source = time_ref_source;
00235 
00236                         time_ref_pub.publish(time_unix);
00237                 }
00238                 else {
00239                         ROS_WARN_THROTTLE_NAMED(60, "time", "TM: Wrong FCU time.");
00240                 }
00241         }
00242 
00243         void handle_timesync(const mavlink_message_t *msg, uint8_t sysid, uint8_t compid) {
00244                 mavlink_timesync_t tsync;
00245                 mavlink_msg_timesync_decode(msg, &tsync);
00246 
00247                 uint64_t now_ns = ros::Time::now().toNSec();
00248 
00249                 if (tsync.tc1 == 0) {
00250                         send_timesync_msg(now_ns, tsync.ts1);
00251                         return;
00252                 }
00253                 else if (tsync.tc1 > 0) {
00254                         int64_t offset_ns = (tsync.ts1 + now_ns - tsync.tc1 * 2) / 2;
00255                         int64_t dt = time_offset_ns - offset_ns;
00256 
00257                         if (std::abs(dt) > 10000000) {          // 10 millisecond skew
00258                                 time_offset_ns = offset_ns;     // hard-set it.
00259                                 uas->set_time_offset(time_offset_ns);
00260 
00261                                 dt_diag.clear();
00262                                 dt_diag.set_timestamp(tsync.tc1);
00263 
00264                                 ROS_WARN_THROTTLE_NAMED(10, "time", "TM: Clock skew detected (%0.9f s). "
00265                                                 "Hard syncing clocks.", dt / 1e9);
00266                         }
00267                         else {
00268                                 average_offset(offset_ns);
00269                                 dt_diag.tick(dt, tsync.tc1, time_offset_ns);
00270 
00271                                 uas->set_time_offset(time_offset_ns);
00272                         }
00273                 }
00274         }
00275 
00276         void sys_time_cb(const ros::TimerEvent &event) {
00277                 // For filesystem only
00278                 mavlink_message_t msg;
00279 
00280                 uint64_t time_unix_usec = ros::Time::now().toNSec() / 1000;     // nano -> micro
00281 
00282                 mavlink_msg_system_time_pack_chan(UAS_PACK_CHAN(uas), &msg,
00283                                 time_unix_usec,
00284                                 0
00285                                 );
00286                 UAS_FCU(uas)->send_message(&msg);
00287         }
00288 
00289         void timesync_cb(const ros::TimerEvent &event) {
00290                 send_timesync_msg( 0, ros::Time::now().toNSec());
00291         }
00292 
00293         void send_timesync_msg(uint64_t tc1, uint64_t ts1) {
00294                 mavlink_message_t msg;
00295 
00296                 mavlink_msg_timesync_pack_chan(UAS_PACK_CHAN(uas), &msg,
00297                                 tc1,
00298                                 ts1
00299                                 );
00300                 UAS_FCU(uas)->send_message(&msg);
00301         }
00302 
00303         inline void average_offset(int64_t offset_ns) {
00304                 time_offset_ns = (offset_avg_alpha * offset_ns) + (1.0 - offset_avg_alpha) * time_offset_ns;
00305         }
00306 };
00307 };      // namespace mavplugin
00308 
00309 PLUGINLIB_EXPORT_CLASS(mavplugin::SystemTimePlugin, mavplugin::MavRosPlugin)


mavros
Author(s): Vladimir Ermakov
autogenerated on Sat Jun 8 2019 19:55:19