Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
clock_publisher.cpp
1 // Copyright (c) 2024, Open Navigation LLC
2 // Copyright (c) 2026, Dexory (Tony Najjar)
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #include "nav2_loopback_sim/clock_publisher.hpp"
17 
18 #include <chrono>
19 #include <memory>
20 #include <stdexcept>
21 
22 namespace nav2_loopback_sim
23 {
24 
26  nav2::LifecycleNode::WeakPtr node,
27  double speed_factor)
28 : node_(node),
29  logger_(rclcpp::get_logger("nav2_loopback_sim")),
30  speed_factor_(speed_factor),
31  sim_time_(0, 0, RCL_ROS_TIME),
32  last_wall_time_(std::chrono::steady_clock::now())
33 {
34  auto shared_node = node_.lock();
35  if (!shared_node) {
36  throw std::runtime_error("Node expired during ClockPublisher construction");
37  }
38  logger_ = shared_node->get_logger();
39  clock_pub_ = rclcpp::create_publisher<rosgraph_msgs::msg::Clock>( // nosemgrep
40  shared_node->get_node_topics_interface(), "/clock", 10);
41 }
42 
44 {
45  last_wall_time_ = std::chrono::steady_clock::now();
46  resetTimer();
47  RCLCPP_INFO(
48  logger_,
49  "Sim clock publisher started (resolution: %.3fs, speed: %.2fx)",
50  kResolution, speed_factor_);
51 }
52 
54 {
55  if (timer_) {
56  timer_->cancel();
57  timer_.reset();
58  }
59 }
60 
61 void ClockPublisher::setSpeedFactor(double speed_factor)
62 {
63  if (speed_factor <= 0.0) {
64  RCLCPP_WARN(
65  logger_,
66  "Ignoring non-positive speed_factor %.2f", speed_factor);
67  return;
68  }
69  speed_factor_ = speed_factor;
70  if (timer_) {
71  resetTimer();
72  }
73  RCLCPP_INFO(
74  logger_,
75  "Clock speed factor changed to %.2fx", speed_factor_);
76 }
77 
79 {
80  if (timer_) {
81  timer_->cancel();
82  timer_.reset();
83  }
84  auto shared_node = node_.lock();
85  if (!shared_node) {
86  return;
87  }
88  double wall_period = std::max(kResolution / speed_factor_, kMinWallPeriod);
89  timer_ = rclcpp::create_wall_timer(
90  std::chrono::duration<double>(wall_period),
91  std::bind(&ClockPublisher::timerCallback, this),
92  nullptr,
93  shared_node->get_node_base_interface().get(),
94  shared_node->get_node_timers_interface().get());
95  if (kResolution / speed_factor_ < kMinWallPeriod) {
96  RCLCPP_WARN(
97  logger_,
98  "Wall period clamped to %.1fms (requested %.3fms from resolution=%.3f, speed=%.1f)",
99  kMinWallPeriod * 1000.0, (kResolution / speed_factor_) * 1000.0,
100  kResolution, speed_factor_);
101  }
102 }
103 
105 {
106  auto now_wall = std::chrono::steady_clock::now();
107  double wall_dt = std::chrono::duration<double>(now_wall - last_wall_time_).count();
108  last_wall_time_ = now_wall;
109 
110  sim_time_ += rclcpp::Duration::from_seconds(wall_dt * speed_factor_);
111 
112  auto msg = std::make_unique<rosgraph_msgs::msg::Clock>();
113  msg->clock = sim_time_;
114  clock_pub_->publish(std::move(msg));
115 }
116 
117 } // namespace nav2_loopback_sim
void start()
Start publishing /clock.
void setSpeedFactor(double speed_factor)
Update the simulation speed factor.
void resetTimer()
(Re)create the wall timer based on current speed_factor
void stop()
Stop publishing /clock.
ClockPublisher(nav2::LifecycleNode::WeakPtr node, double speed_factor=1.0)
Construct a ClockPublisher.
void timerCallback()
Wall-timer callback that advances sim time and publishes /clock.