00001
00009
00010
00011
00012
00013
00014
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
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
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
00173
00174
00175
00176
00177 time_ref_pub = nh.advertise<sensor_msgs::TimeReference>("time_reference", 10);
00178
00179
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
00187 if (!conn_timesync.isZero()) {
00188
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
00223 const bool fcu_time_valid = mtime.time_unix_usec > 1234567890ULL * 1000000;
00224
00225 if (fcu_time_valid) {
00226
00227 auto time_unix = boost::make_shared<sensor_msgs::TimeReference>();
00228 ros::Time time_ref(
00229 mtime.time_unix_usec / 1000000,
00230 (mtime.time_unix_usec % 1000000) * 1000);
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) {
00258 time_offset_ns = offset_ns;
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
00278 mavlink_message_t msg;
00279
00280 uint64_t time_unix_usec = ros::Time::now().toNSec() / 1000;
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 };
00308
00309 PLUGINLIB_EXPORT_CLASS(mavplugin::SystemTimePlugin, mavplugin::MavRosPlugin)