From b40ee02383f0a98807148ed9aaec5f1f72854deb Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Mon, 24 Mar 2014 16:24:40 +0400 Subject: [PATCH] GlobalTimeSyncSlave message validation test --- .../test/protocol/global_time_sync_slave.cpp | 111 ++++++++++++++++++ libuavcan/test/transport/can/can.hpp | 4 +- 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/libuavcan/test/protocol/global_time_sync_slave.cpp b/libuavcan/test/protocol/global_time_sync_slave.cpp index 69ec4fc7b5..edfecf43b3 100644 --- a/libuavcan/test/protocol/global_time_sync_slave.cpp +++ b/libuavcan/test/protocol/global_time_sync_slave.cpp @@ -163,3 +163,114 @@ TEST(GlobalTimeSyncSlave, Basic) ASSERT_FALSE(gtss.isActive()); ASSERT_FALSE(gtss.getMasterNodeID().isValid()); } + + +#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || (BYTE_ORDER != LITTLE_ENDIAN) +# error "This test cannot be executed on this platform" +#endif + +static uavcan::Frame makeSyncMsg(uavcan::uint64_t usec, uavcan::NodeID snid, uavcan::TransferID tid) +{ + uavcan::Frame frame(uavcan::protocol::GlobalTimeSync::DefaultDataTypeID, uavcan::TransferTypeMessageBroadcast, + snid, uavcan::NodeID::Broadcast, 0, tid, true); + EXPECT_EQ(8, frame.setPayload(reinterpret_cast(&usec), 8)); // Assuming little endian + return frame; +} + +static void broadcastSyncMsg(CanIfaceMock& iface, uavcan::uint64_t usec, uavcan::NodeID snid, uavcan::TransferID tid) +{ + const uavcan::Frame frame = makeSyncMsg(usec, snid, tid); + uavcan::CanFrame can_frame; + ASSERT_TRUE(frame.compile(can_frame)); + iface.pushRx(can_frame); +} + + +TEST(GlobalTimeSyncSlave, Validation) +{ + SystemClockMock slave_clock; + slave_clock.monotonic = 1000000; + slave_clock.preserve_utc = true; + + CanDriverMock slave_can(3, slave_clock); + for (int i = 0; i < slave_can.getNumIfaces(); i++) + { + slave_can.ifaces.at(i).enable_utc_timestamping = true; + } + + TestNode node(slave_can, slave_clock, 64); + + uavcan::GlobalTimeSyncSlave gtss(node); + uavcan::Publisher gts_pub(node); + + ASSERT_LE(0, gtss.start()); + ASSERT_FALSE(gtss.isActive()); + ASSERT_FALSE(gtss.getMasterNodeID().isValid()); + ASSERT_EQ(0, slave_clock.utc); + + /* + * Update/adjust/update + */ + broadcastSyncMsg(slave_can.ifaces.at(0), 0, 8, 0); // Locked on this + broadcastSyncMsg(slave_can.ifaces.at(1), 2000, 8, 0); // Ignored + ASSERT_LE(0, node.spin(uavcan::MonotonicDuration::fromMSec(1))); + + broadcastSyncMsg(slave_can.ifaces.at(0), 1000, 8, 1); // Adjust 1000 ahead + broadcastSyncMsg(slave_can.ifaces.at(1), 2000, 8, 1); // Ignored + ASSERT_LE(0, node.spin(uavcan::MonotonicDuration::fromMSec(1))); + + ASSERT_TRUE(gtss.isActive()); + ASSERT_EQ(8, gtss.getMasterNodeID().get()); + ASSERT_EQ(1000, slave_clock.utc); + + broadcastSyncMsg(slave_can.ifaces.at(0), 2000, 8, 2); // Update + ASSERT_LE(0, node.spin(uavcan::MonotonicDuration::fromMSec(1))); + + ASSERT_EQ(1000, slave_clock.utc); + std::cout << slave_clock.monotonic << std::endl; + + /* + * TID jump simulates a frame loss + */ + broadcastSyncMsg(slave_can.ifaces.at(0), 3000, 8, 4); // Adjustment skipped - expected TID 3, update instead + ASSERT_LE(0, node.spin(uavcan::MonotonicDuration::fromMSec(1))); + + ASSERT_TRUE(gtss.isActive()); + ASSERT_EQ(8, gtss.getMasterNodeID().get()); + ASSERT_EQ(1000, slave_clock.utc); + std::cout << slave_clock.monotonic << std::endl; + + /* + * Valid adjustment - continuing from TID 4 + */ + broadcastSyncMsg(slave_can.ifaces.at(0), 3000, 8, 5); // Slave UTC was 1000, master reports 3000 --> shift ahead + broadcastSyncMsg(slave_can.ifaces.at(1), 2000, 8, 5); + ASSERT_LE(0, node.spin(uavcan::MonotonicDuration::fromMSec(1))); + + ASSERT_TRUE(gtss.isActive()); + ASSERT_EQ(8, gtss.getMasterNodeID().get()); + ASSERT_EQ(3000, slave_clock.utc); + std::cout << slave_clock.monotonic << std::endl; + + /* + * Update, then very long delay with correct TID + */ + broadcastSyncMsg(slave_can.ifaces.at(0), 2000, 8, 6); // Valid update, slave UTC is 3000 + broadcastSyncMsg(slave_can.ifaces.at(1), 2000, 8, 6); + ASSERT_LE(0, node.spin(uavcan::MonotonicDuration::fromMSec(1))); + + slave_clock.monotonic += 5000000; + + broadcastSyncMsg(slave_can.ifaces.at(0), 5000, 8, 7); // Adjustment skipped + broadcastSyncMsg(slave_can.ifaces.at(1), 2000, 8, 7); + ASSERT_LE(0, node.spin(uavcan::MonotonicDuration::fromMSec(1))); + + broadcastSyncMsg(slave_can.ifaces.at(0), 5000, 8, 0); // Valid adjustment now + broadcastSyncMsg(slave_can.ifaces.at(1), 2000, 8, 0); + ASSERT_LE(0, node.spin(uavcan::MonotonicDuration::fromMSec(1))); + + ASSERT_TRUE(gtss.isActive()); + ASSERT_EQ(8, gtss.getMasterNodeID().get()); + ASSERT_EQ(5000, slave_clock.utc); + std::cout << slave_clock.monotonic << std::endl; +} diff --git a/libuavcan/test/transport/can/can.hpp b/libuavcan/test/transport/can/can.hpp index ff3112813c..ae60901f6d 100644 --- a/libuavcan/test/transport/can/can.hpp +++ b/libuavcan/test/transport/can/can.hpp @@ -42,6 +42,7 @@ public: bool rx_failure; uint64_t num_errors; uavcan::ISystemClock& iclock; + bool enable_utc_timestamping; CanIfaceMock(uavcan::ISystemClock& iclock) : writeable(true) @@ -49,6 +50,7 @@ public: , rx_failure(false) , num_errors(0) , iclock(iclock) + , enable_utc_timestamping(false) { } void pushRx(const uavcan::CanFrame& frame) @@ -131,7 +133,7 @@ public: out_frame = frame.frame; out_ts_monotonic = frame.time; } - out_ts_utc = uavcan::UtcTime(); + out_ts_utc = enable_utc_timestamping ? iclock.getUtc() : uavcan::UtcTime(); return 1; }