From 3e6e1f5c2b042accca72346273fc1755dd7670fa Mon Sep 17 00:00:00 2001
From: Julian Oes <julian@oes.ch>
Date: Mon, 15 Oct 2018 15:00:29 +0200
Subject: [PATCH] POSIX: use lockstep_scheduler to fake time

This integrates the lockstep_scheduler, so that the system time is set
by the mavlink HIL_SENSOR message.

This means that the speed factor is removed and the speed is entirely
given by the simulator.
---
 platforms/posix/src/px4_layer/drv_hrt.cpp   |  78 ++++++++-------
 src/drivers/drv_hrt.h                       |   7 +-
 src/include/visibility.h                    |   1 +
 src/lib/cdev/posix/cdev_platform.cpp        |   8 +-
 src/modules/simulator/simulator_mavlink.cpp | 105 ++++----------------
 5 files changed, 74 insertions(+), 125 deletions(-)

diff --git a/platforms/posix/src/px4_layer/drv_hrt.cpp b/platforms/posix/src/px4_layer/drv_hrt.cpp
index 407c9a1673..cdf6c30a54 100644
--- a/platforms/posix/src/px4_layer/drv_hrt.cpp
+++ b/platforms/posix/src/px4_layer/drv_hrt.cpp
@@ -43,14 +43,13 @@
 #include <px4_workqueue.h>
 #include <px4_tasks.h>
 #include <drivers/drv_hrt.h>
+#include <lockstep_scheduler/lockstep_scheduler.h>
 #include <semaphore.h>
 #include <time.h>
 #include <string.h>
-#include <inttypes.h>
 #include <errno.h>
 #include "hrt_work.h"
 
-#define SPEED_FACTOR 1
 
 static struct sq_queue_s	callout_queue;
 
@@ -73,6 +72,8 @@ static hrt_abstime _delay_interval = 0;
 static hrt_abstime max_time = 0;
 pthread_mutex_t _hrt_mutex = PTHREAD_MUTEX_INITIALIZER;
 
+static LockstepScheduler lockstep_scheduler;
+
 static void
 hrt_call_invoke(void);
 
@@ -81,6 +82,12 @@ _hrt_absolute_time_internal(void);
 
 __EXPORT hrt_abstime hrt_reset(void);
 
+
+hrt_abstime hrt_absolute_time_offset(void)
+{
+	return px4_timestart_monotonic;
+}
+
 static void hrt_lock(void)
 {
 	px4_sem_wait(&_hrt_lock);
@@ -191,7 +198,7 @@ hrt_abstime hrt_absolute_time(void)
 	return ret;
 }
 
-__EXPORT hrt_abstime hrt_reset(void)
+hrt_abstime hrt_reset(void)
 {
 #ifndef __PX4_QURT
 	px4_timestart_monotonic = 0;
@@ -203,7 +210,7 @@ __EXPORT hrt_abstime hrt_reset(void)
 /*
  * Convert a timespec to absolute time.
  */
-hrt_abstime ts_to_abstime(struct timespec *ts)
+hrt_abstime ts_to_abstime(const struct timespec *ts)
 {
 	hrt_abstime	result;
 
@@ -594,61 +601,62 @@ int px4_clock_gettime(clockid_t clk_id, struct timespec *tp)
 	// Don't do any offseting on the Linux side on the Snapdragon.
 	return clock_gettime(clk_id, &tp);
 #else
-	struct timespec actual_tp;
-	const int ret = clock_gettime(clk_id, &actual_tp);
-
-	if (ret != 0) {
-		return ret;
-	}
-
-	const uint64_t actual_usec = ts_to_abstime(&actual_tp);
-
 
 	if (clk_id == CLOCK_MONOTONIC) {
-		if (px4_timestart_monotonic == 0) {
-			px4_timestart_monotonic = actual_usec;
-		}
 
-		abstime_to_ts(tp, (actual_usec - px4_timestart_monotonic) * SPEED_FACTOR);
+		const uint64_t abstime = lockstep_scheduler.get_absolute_time();
+		abstime_to_ts(tp, abstime - px4_timestart_monotonic);
+		return 0;
 
 	} else {
-		abstime_to_ts(tp, actual_usec);
+		return system_clock_gettime(clk_id, tp);
 	}
 
-	//static unsigned counter = 0;
-	//if (counter++ % 1000000 == 0) {
-	//	PX4_INFO("clk_id: %d, actual_tp.tv_sec: %llu, actual_tp.tv_nsec: %llu, px4_timestart: %llu: 0x%x",
-	//		 clk_id, actual_tp.tv_sec, actual_tp.tv_nsec, px4_timestart[clk_id], &px4_timestart[clk_id]);
-	//}
-
-	return 0;
 #endif
 }
 
-int px4_clock_settime(clockid_t clk_id, const struct timespec *tp)
+int px4_clock_settime(clockid_t clk_id, const struct timespec *ts)
 {
-	// not implemented
-	return 0;
+	if (clk_id == CLOCK_REALTIME) {
+		return system_clock_settime(clk_id, ts);
+
+	} else {
+		const uint64_t time_us = ts_to_abstime(ts);
+
+		if (px4_timestart_monotonic == 0) {
+			px4_timestart_monotonic = time_us;
+		}
+
+		lockstep_scheduler.set_absolute_time(time_us);
+		return 0;
+	}
 }
 
 
 int px4_usleep(useconds_t usec)
 {
-	return system_usleep(usec / SPEED_FACTOR);
+	if (px4_timestart_monotonic == 0) {
+		// Until the time is set by the simulator, we fallback to the normal
+		// usleep;
+		return system_usleep(usec);
+	}
+
+	const uint64_t time_finished = lockstep_scheduler.get_absolute_time() + usec;
+
+	return lockstep_scheduler.usleep_until(time_finished);
 }
 
 unsigned int px4_sleep(unsigned int seconds)
 {
 	useconds_t usec = seconds * 1000000;
-	return system_usleep(usec / SPEED_FACTOR);
+	return px4_usleep(usec);
 }
 
 int px4_pthread_cond_timedwait(pthread_cond_t *cond,
 			       pthread_mutex_t *mutex,
-			       const struct timespec *abstime)
+			       const struct timespec *ts)
 {
-	struct timespec new_abstime;
-	new_abstime.tv_sec = abstime->tv_sec / SPEED_FACTOR;
-	new_abstime.tv_nsec = abstime->tv_nsec / SPEED_FACTOR;
-	return pthread_cond_timedwait(cond, mutex, &new_abstime);
+	const uint64_t time_us = ts_to_abstime(ts);
+	const uint64_t scheduled = time_us + px4_timestart_monotonic;
+	return lockstep_scheduler.cond_timedwait(cond, mutex, scheduled);
 }
diff --git a/src/drivers/drv_hrt.h b/src/drivers/drv_hrt.h
index ac0694aba9..c50494afce 100644
--- a/src/drivers/drv_hrt.h
+++ b/src/drivers/drv_hrt.h
@@ -85,7 +85,7 @@ __EXPORT extern hrt_abstime hrt_absolute_time(void);
 /**
  * Convert a timespec to absolute time.
  */
-__EXPORT extern hrt_abstime ts_to_abstime(struct timespec *ts);
+__EXPORT extern hrt_abstime ts_to_abstime(const struct timespec *ts);
 
 /**
  * Convert absolute time to a timespec.
@@ -191,6 +191,11 @@ __EXPORT extern void	hrt_stop_delay(void);
  */
 __EXPORT extern void	hrt_stop_delay_delta(hrt_abstime delta);
 
+
+__EXPORT extern hrt_abstime hrt_reset(void);
+
+__EXPORT extern hrt_abstime hrt_absolute_time_offset(void);
+
 #endif
 
 __END_DECLS
diff --git a/src/include/visibility.h b/src/include/visibility.h
index 91445679dd..05b0ea5f83 100644
--- a/src/include/visibility.h
+++ b/src/include/visibility.h
@@ -97,6 +97,7 @@
 // We don't poison usleep and sleep on NuttX because it is used in dependencies
 // like uavcan and we don't need to fake time on the real system.
 #include <unistd.h>
+#include <time.h>
 #define system_usleep usleep
 #define system_sleep sleep
 
diff --git a/src/lib/cdev/posix/cdev_platform.cpp b/src/lib/cdev/posix/cdev_platform.cpp
index 8418f6c4ca..5202dd2b50 100644
--- a/src/lib/cdev/posix/cdev_platform.cpp
+++ b/src/lib/cdev/posix/cdev_platform.cpp
@@ -384,19 +384,19 @@ extern "C" {
 
 				// Get the current time
 				struct timespec ts;
-				// FIXME: check if QURT should probably be using CLOCK_MONOTONIC
-				px4_clock_gettime(CLOCK_REALTIME, &ts);
+				// px4_sem_timedwait is implemented using CLOCK_MONOTONIC.
+				px4_clock_gettime(CLOCK_MONOTONIC, &ts);
 
 				// Calculate an absolute time in the future
 				const unsigned billion = (1000 * 1000 * 1000);
-				unsigned tdiff = timeout;
-				uint64_t nsecs = ts.tv_nsec + (tdiff * 1000 * 1000);
+				uint64_t nsecs = ts.tv_nsec + (timeout * 1000 * 1000);
 				ts.tv_sec += nsecs / billion;
 				nsecs -= (nsecs / billion) * billion;
 				ts.tv_nsec = nsecs;
 
 				// Execute a blocking wait for that time in the future
 				errno = 0;
+
 				ret = px4_sem_timedwait(&sem, &ts);
 #ifndef __PX4_DARWIN
 				ret = errno;
diff --git a/src/modules/simulator/simulator_mavlink.cpp b/src/modules/simulator/simulator_mavlink.cpp
index d58430daf2..3dea43e879 100644
--- a/src/modules/simulator/simulator_mavlink.cpp
+++ b/src/modules/simulator/simulator_mavlink.cpp
@@ -49,8 +49,6 @@
 
 #include <limits>
 
-extern "C" __EXPORT hrt_abstime hrt_reset(void);
-
 #define SEND_INTERVAL 	20
 #define UDP_PORT 	14560
 
@@ -79,7 +77,7 @@ using namespace simulator;
 
 void Simulator::pack_actuator_message(mavlink_hil_actuator_controls_t &msg, unsigned index)
 {
-	msg.time_usec = hrt_absolute_time();
+	msg.time_usec = hrt_absolute_time() + hrt_absolute_time_offset();
 
 	bool armed = (_vehicle_status.arming_state == vehicle_status_s::ARMING_STATE_ARMED);
 
@@ -286,69 +284,27 @@ void Simulator::handle_message(mavlink_message_t *msg, bool publish)
 			mavlink_hil_sensor_t imu;
 			mavlink_msg_hil_sensor_decode(msg, &imu);
 
-			bool compensation_enabled = (imu.time_usec > 0);
-
 			// set temperature to a decent value
 			imu.temperature = 32.0f;
 
 			struct timespec ts;
-			// clock_gettime(CLOCK_MONOTONIC, &ts);
-			// uint64_t host_time = ts_to_abstime(&ts);
-
-			hrt_abstime curr_sitl_time = hrt_absolute_time();
-			hrt_abstime curr_sim_time = imu.time_usec;
-
-			if (compensation_enabled && _initialized
-			    && _last_sim_timestamp > 0 && _last_sitl_timestamp > 0
-			    && _last_sitl_timestamp < curr_sitl_time
-			    && _last_sim_timestamp < curr_sim_time) {
-
-				px4_clock_gettime(CLOCK_MONOTONIC, &ts);
-				uint64_t timestamp = ts_to_abstime(&ts);
-
-				perf_set_elapsed(_perf_sim_delay, timestamp - curr_sim_time);
-				perf_count(_perf_sim_interval);
-
-				int64_t dt_sitl = curr_sitl_time - _last_sitl_timestamp;
-				int64_t dt_sim = curr_sim_time - _last_sim_timestamp;
-
-				double curr_factor = ((double)dt_sim / (double)dt_sitl);
-
-				if (curr_factor < 5.0) {
-					_realtime_factor = _realtime_factor * 0.99 + 0.01 * curr_factor;
-				}
-
-				// calculate how much the system needs to be delayed
-				int64_t sysdelay = dt_sitl - dt_sim;
-
-				unsigned min_delay = 200;
-
-				if (dt_sitl < 1e5
-				    && dt_sim < 1e5
-				    && sysdelay > min_delay + 100) {
+			abstime_to_ts(&ts, imu.time_usec);
+			px4_clock_settime(CLOCK_MONOTONIC, &ts);
 
-					// the correct delay is exactly the scale between
-					// the last two intervals
-					px4_sim_start_delay();
-					hrt_start_delay();
+			hrt_abstime now_us = hrt_absolute_time();
 
-					unsigned exact_delay = sysdelay / _realtime_factor;
-					unsigned usleep_delay = (sysdelay - min_delay) / _realtime_factor;
+#if 0
+			// This is just for to debug missing HIL_SENSOR messages.
+			static hrt_abstime last_time = 0;
+			hrt_abstime diff = now_us - last_time;
+			float step = diff / 4000.0f;
 
-					// extend by the realtime factor to avoid drift
-					px4_usleep(usleep_delay);
-					hrt_stop_delay_delta(exact_delay);
-					px4_sim_stop_delay();
-				}
+			if (step > 1.1f || step < 0.9f) {
+				PX4_INFO("diff: %llu, step: %.2f", diff, step);
 			}
 
-			hrt_abstime now = hrt_absolute_time();
-
-			_last_sitl_timestamp = curr_sitl_time;
-			_last_sim_timestamp = curr_sim_time;
-
-			// correct timestamp
-			imu.time_usec = now;
+			last_time = now_us;
+#endif
 
 			if (publish) {
 				publish_sensor_topics(&imu);
@@ -363,21 +319,23 @@ void Simulator::handle_message(mavlink_message_t *msg, bool publish)
 
 				bool armed = (_vehicle_status.arming_state == vehicle_status_s::ARMING_STATE_ARMED);
 
-				if (!armed || batt_sim_start == 0 || batt_sim_start > now) {
-					batt_sim_start = now;
+				if (!armed || batt_sim_start == 0 || batt_sim_start > now_us) {
+					batt_sim_start = now_us;
 				}
 
 				float ibatt = -1.0f; // no current sensor in simulation
 				const float minimum_percentage = 0.5f; // change this value if you want to simulate low battery reaction
 
 				/* Simulate the voltage of a linearly draining battery but stop at the minimum percentage */
-				float battery_percentage = (now - batt_sim_start) / discharge_interval_us;
+				float battery_percentage = 1.0f - (now_us - batt_sim_start) / discharge_interval_us;
+
 				battery_percentage = math::min(battery_percentage, minimum_percentage);
 				float vbatt = math::gradual(battery_percentage, 0.f, 1.f, _battery.full_cell_voltage(), _battery.empty_cell_voltage());
 				vbatt *= _battery.cell_count();
 
 				const float throttle = 0.0f; // simulate no throttle compensation to make the estimate predictable
-				_battery.updateBatteryStatus(now, vbatt, ibatt, true, true, 0, throttle, armed, &_battery_status);
+				_battery.updateBatteryStatus(now_us, vbatt, ibatt, true, true, 0, throttle, armed, &_battery_status);
+
 
 				// publish the battery voltage
 				int batt_multi;
@@ -734,17 +692,12 @@ void Simulator::pollForMAVLinkMessages(bool publish, int udp_port)
 	PX4_INFO("Waiting for initial data on UDP port %i. Please start the flight simulator to proceed..", udp_port);
 	fflush(stdout);
 
-	uint64_t pstart_time = 0;
-
 	bool no_sim_data = true;
 
 	while (!px4_exit_requested() && no_sim_data) {
 		pret = ::poll(&fds[0], fd_count, 100);
 
 		if (fds[0].revents & POLLIN) {
-			if (pstart_time == 0) {
-				pstart_time = hrt_system_time();
-			}
 
 			len = recvfrom(_fd, _buf, sizeof(_buf), 0, (struct sockaddr *)&_srcaddr, &_addrlen);
 			// send hearbeat
@@ -764,7 +717,7 @@ void Simulator::pollForMAVLinkMessages(bool publish, int udp_port)
 						// have a message, handle it
 						handle_message(&msg, publish);
 
-						if (msg.msgid != 0 && (hrt_system_time() - pstart_time > 1000000)) {
+						if (msg.msgid != 0) {
 							PX4_INFO("Got initial simulation data, running sim..");
 							no_sim_data = false;
 						}
@@ -778,9 +731,6 @@ void Simulator::pollForMAVLinkMessages(bool publish, int udp_port)
 		return;
 	}
 
-	// reset system time
-	(void)hrt_reset();
-
 	// subscribe to topics
 	for (unsigned i = 0; i < (sizeof(_actuator_outputs_sub) / sizeof(_actuator_outputs_sub[0])); i++) {
 		_actuator_outputs_sub[i] = orb_subscribe_multi(ORB_ID(actuator_outputs), i);
@@ -794,8 +744,6 @@ void Simulator::pollForMAVLinkMessages(bool publish, int udp_port)
 
 	mavlink_status_t udp_status = {};
 
-	bool sim_delay = false;
-
 	const unsigned max_wait_ms = 4;
 
 	//send MAV_CMD_SET_MESSAGE_INTERVAL for HIL_STATE_QUATERNION ground truth
@@ -817,23 +765,10 @@ void Simulator::pollForMAVLinkMessages(bool publish, int udp_port)
 
 		//timed out
 		if (pret == 0) {
-			if (!sim_delay) {
-				// we do not want to spam the console by default
-				// PX4_WARN("mavlink sim timeout for %d ms", max_wait_ms);
-				sim_delay = true;
-				px4_sim_start_delay();
-				hrt_start_delay();
-			}
 
 			continue;
 		}
 
-		if (sim_delay) {
-			sim_delay = false;
-			hrt_stop_delay();
-			px4_sim_stop_delay();
-		}
-
 		// this is undesirable but not much we can do
 		if (pret < 0) {
 			PX4_WARN("simulator mavlink: poll error %d, %d", pret, errno);
-- 
GitLab