123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- /*
- * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
- */
- #include <gtest/gtest.h>
- #include <uavcan/node/publisher.hpp>
- #include <uavcan/protocol/global_time_sync_slave.hpp>
- #include "helpers.hpp"
- TEST(GlobalTimeSyncSlave, Basic)
- {
- InterlinkedTestNodesWithClockMock nodes(64, 65);
- SystemClockMock& slave_clock = nodes.clock_a;
- SystemClockMock& master_clock = nodes.clock_b;
- slave_clock.advance(1000000);
- master_clock.advance(1000000);
- master_clock.monotonic_auto_advance = slave_clock.monotonic_auto_advance = 1000;
- master_clock.preserve_utc = slave_clock.preserve_utc = true;
- slave_clock.utc = 0; // Not set yet
- uavcan::GlobalDataTypeRegistry::instance().reset();
- uavcan::DefaultDataTypeRegistrator<uavcan::protocol::GlobalTimeSync> _reg1;
- uavcan::GlobalTimeSyncSlave gtss(nodes.a);
- uavcan::Publisher<uavcan::protocol::GlobalTimeSync> gts_pub(nodes.b);
- ASSERT_LE(0, gtss.start());
- ASSERT_FALSE(gtss.isActive());
- ASSERT_FALSE(gtss.getMasterNodeID().isValid());
- /*
- * Empty broadcast
- * The slave must only register the timestamp and adjust nothing
- */
- uavcan::protocol::GlobalTimeSync gts;
- gts.previous_transmission_timestamp_usec = 0;
- gts_pub.broadcast(gts);
- gts.previous_transmission_timestamp_usec = master_clock.utc;
- nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(10));
- ASSERT_EQ(0, slave_clock.utc);
- ASSERT_EQ(1000000, master_clock.utc);
- std::cout << "Master mono=" << master_clock.monotonic << " utc=" << master_clock.utc << std::endl;
- std::cout << "Slave mono=" << slave_clock.monotonic << " utc=" << slave_clock.utc << std::endl;
- ASSERT_FALSE(gtss.isActive());
- ASSERT_FALSE(gtss.getMasterNodeID().isValid());
- /*
- * Follow-up broadcast with proper time
- * Slave must adjust now
- */
- gts_pub.broadcast(gts);
- gts.previous_transmission_timestamp_usec = master_clock.utc;
- nodes.spinBoth(uavcan::MonotonicDuration());
- ASSERT_EQ(1000000, slave_clock.utc);
- ASSERT_EQ(1000000, master_clock.utc);
- std::cout << "Master mono=" << master_clock.monotonic << " utc=" << master_clock.utc << std::endl;
- std::cout << "Slave mono=" << slave_clock.monotonic << " utc=" << slave_clock.utc << std::endl;
- master_clock.utc += 1000000;
- slave_clock.utc += 1000000;
- ASSERT_TRUE(gtss.isActive());
- ASSERT_EQ(nodes.b.getNodeID(), gtss.getMasterNodeID());
- /*
- * Next follow-up, slave is synchronized now
- * Will update
- */
- gts_pub.broadcast(gts);
- gts.previous_transmission_timestamp_usec = master_clock.utc;
- nodes.spinBoth(uavcan::MonotonicDuration());
- ASSERT_EQ(2000000, slave_clock.utc);
- ASSERT_EQ(2000000, master_clock.utc);
- master_clock.utc += 1000000;
- slave_clock.utc += 1000000;
- ASSERT_TRUE(gtss.isActive());
- ASSERT_EQ(nodes.b.getNodeID(), gtss.getMasterNodeID());
- /*
- * Next follow-up, slave is synchronized now
- * Will adjust
- */
- gts_pub.broadcast(gts);
- gts.previous_transmission_timestamp_usec = master_clock.utc;
- nodes.spinBoth(uavcan::MonotonicDuration());
- ASSERT_EQ(3000000, slave_clock.utc);
- ASSERT_EQ(3000000, master_clock.utc);
- master_clock.utc += 1000000;
- slave_clock.utc += 1000000;
- ASSERT_EQ(4000000, slave_clock.utc);
- ASSERT_EQ(4000000, master_clock.utc);
- ASSERT_TRUE(gtss.isActive());
- ASSERT_EQ(nodes.b.getNodeID(), gtss.getMasterNodeID());
- /*
- * Another master
- * This one has higher priority, so it will be preferred
- */
- SystemClockMock master2_clock(100);
- master2_clock.monotonic_auto_advance = 1000;
- master2_clock.preserve_utc = true;
- PairableCanDriver master2_can(master2_clock);
- master2_can.others.insert(&nodes.can_a);
- TestNode master2_node(master2_can, master2_clock, 8);
- uavcan::Publisher<uavcan::protocol::GlobalTimeSync> gts_pub2(master2_node);
- /*
- * Update step, no adjustment yet
- */
- gts.previous_transmission_timestamp_usec = 0;
- gts_pub2.broadcast(gts);
- gts.previous_transmission_timestamp_usec = master2_clock.utc;
- nodes.spinBoth(uavcan::MonotonicDuration());
- ASSERT_EQ(4000000, slave_clock.utc);
- ASSERT_EQ(100, master2_clock.utc);
- master2_clock.utc += 1000000;
- ASSERT_TRUE(gtss.isActive());
- ASSERT_EQ(master2_node.getNodeID(), gtss.getMasterNodeID());
- /*
- * Adjustment
- */
- gts_pub2.broadcast(gts);
- nodes.spinBoth(uavcan::MonotonicDuration());
- ASSERT_EQ(100, slave_clock.utc);
- ASSERT_TRUE(gtss.isActive());
- ASSERT_EQ(master2_node.getNodeID(), gtss.getMasterNodeID());
- /*
- * Another master will be ignored now
- */
- gts.previous_transmission_timestamp_usec = 99999999;
- // Update
- gts_pub.broadcast(gts);
- nodes.spinBoth(uavcan::MonotonicDuration());
- ASSERT_EQ(100, slave_clock.utc);
- // Adjust
- gts_pub.broadcast(gts);
- nodes.spinBoth(uavcan::MonotonicDuration());
- ASSERT_EQ(100, slave_clock.utc);
- ASSERT_TRUE(gtss.isActive());
- ASSERT_EQ(master2_node.getNodeID(), gtss.getMasterNodeID());
- /*
- * Timeout
- */
- slave_clock.advance(100000000);
- 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, tid);
- frame.setStartOfTransfer(true);
- frame.setEndOfTransfer(true);
- EXPECT_EQ(7, frame.setPayload(reinterpret_cast<uint8_t*>(&usec), 7)); // 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 (uint8_t 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<uavcan::protocol::GlobalTimeSync> 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(10)));
- 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(10)));
- 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(10)));
- 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(10)));
- 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(10)));
- 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(10)));
- 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(10)));
- broadcastSyncMsg(slave_can.ifaces.at(0), 5000, 8, 8); // Valid adjustment now
- broadcastSyncMsg(slave_can.ifaces.at(1), 2000, 8, 8);
- ASSERT_LE(0, node.spin(uavcan::MonotonicDuration::fromMSec(10)));
- ASSERT_TRUE(gtss.isActive());
- ASSERT_EQ(8, gtss.getMasterNodeID().get());
- ASSERT_EQ(5000, slave_clock.utc);
- std::cout << slave_clock.monotonic << std::endl;
- }
- TEST(GlobalTimeSyncSlave, Suppression)
- {
- SystemClockMock slave_clock;
- slave_clock.monotonic = 1000000;
- slave_clock.preserve_utc = true;
- CanDriverMock slave_can(3, slave_clock);
- for (uint8_t 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<uavcan::protocol::GlobalTimeSync> gts_pub(node);
- ASSERT_LE(0, gtss.start());
- ASSERT_EQ(0, slave_clock.utc);
- gtss.suppress(true);
- 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(10)));
- 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(10)));
- ASSERT_TRUE(gtss.isActive());
- ASSERT_EQ(8, gtss.getMasterNodeID().get());
- ASSERT_EQ(0, slave_clock.utc); // The clock shall not be asjusted
- }
|