Skip to content

Commit 0aca167

Browse files
committed
WIP on time sources
Signed-off-by: Adam Dabrowski <[email protected]>
1 parent ae62f2f commit 0aca167

File tree

7 files changed

+234
-31
lines changed

7 files changed

+234
-31
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2022 Robotec.ai.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System;
16+
using System.Threading;
17+
18+
19+
namespace ROS2
20+
{
21+
22+
/// <summary>
23+
/// DateTime based clock that has resolution increased using Stopwatch.
24+
/// DateTime is used to synchronize since Stopwatch tends to drift.
25+
/// </summary>
26+
public class DotnetTimeSource : ITimeSource
27+
{
28+
private readonly double maxUnsyncedSeconds = 10;
29+
30+
private Stopwatch stopwatch = new Stopwatch();
31+
32+
private readonly object mutex = new object();
33+
34+
private double systemTimeIntervalStart = 0;
35+
36+
private double stopwatchStartTimeStamp;
37+
38+
DotnetTimeSource()
39+
{
40+
stopwatchStartTimeStamp = stopwatch.GetTimestamp();
41+
}
42+
43+
void GetTime(out int seconds, out uint nanoseconds)
44+
{
45+
lock(mutex) // Threading
46+
{
47+
double endTimestamp = Stopwatch.GetTimestamp();
48+
var durationInSeconds = (endTimestamp - stopwatchStartTimeStamp) / Stopwatch.Frequency;
49+
double timeOffset = 0;
50+
if (durationInSeconds >= maxUnsyncedSeconds)
51+
{ // acquire DateTime to sync
52+
stopwatchStartTimeStamp = Stopwatch.GetTimestamp();
53+
systemTimeIntervalStart = TimeSpan.FromTicks(DateTime.UtcNow.Ticks).TotalSeconds;
54+
}
55+
else
56+
{ // use Stopwatch offset
57+
timeOffset = durationInSeconds;
58+
}
59+
60+
TimeUtils.TimeFromTotalSeconds(systemTimeIntervalStart + timeOffset, out seconds, out nanoseconds);
61+
}
62+
}
63+
}
64+
65+
} // namespace ROS2
66+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2022 Robotec.ai.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
namespace ROS2
16+
{
17+
18+
/// <summary>
19+
/// Interace for acquiring time
20+
/// </summary>
21+
internal interface ITimeSource
22+
{
23+
void GetTime(out int seconds, out uint nanoseconds);
24+
}
25+
26+
} // namespace ROS2
Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2019-2021 Robotec.ai.
1+
// Copyright 2019-2022 Robotec.ai.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -19,57 +19,38 @@ namespace ROS2
1919
{
2020

2121
/// <summary>
22-
/// A ros2 clock wrapper class that can acquire ros2 time on demand. It does not include a publisher.
23-
/// (Not every clock needs to be associated with a publisher)
22+
/// A ros2 clock class that for interfacing between a time source (unity or ros2 system time) and ros2cs messages, structs.
2423
/// </summary>
2524
public class ROS2Clock
2625
{
27-
private int seconds;
28-
private uint nanoseconds;
29-
private ROS2.Clock clock;
26+
private ITimeSource timeSource;
3027

31-
public ROS2Clock()
32-
{
33-
clock = new ROS2.Clock();
28+
public ROS2Clock() : this(new DotnetTimeSource())
29+
{ // By default, use DotnetTimeSource
3430
}
3531

36-
~ROS2Clock()
32+
public ROS2Clock(ITimeSource ts)
3733
{
38-
clock.Dispose();
34+
timeSource = ts;
3935
}
4036

4137
public void UpdateClockMessage(ref rosgraph_msgs.msg.Clock clockMessage)
4238
{
43-
GetRosTime();
44-
clockMessage.Clock_.Sec = seconds;
45-
clockMessage.Clock_.Nanosec = nanoseconds;
39+
_timeSource.GetTime(out clockMessage.Clock_.Sec, out clockMessage.Clock_.Nanosec);
4640
}
4741

4842
public void UpdateROSClockTime(builtin_interfaces.msg.Time time)
4943
{
50-
GetRosTime();
51-
time.Nanosec = nanoseconds;
52-
time.Sec = seconds;
44+
timeSource.GetTime(out time.Sec, out time.Nanosec);
5345
}
5446

5547
public void UpdateROSTimestamp(ref ROS2.MessageWithHeader message)
5648
{
57-
GetRosTime();
49+
int seconds;
50+
uint nanoseconds;
51+
timeSource.GetTime(out seconds, out nanoseconds);
5852
message.UpdateHeaderTime(seconds, nanoseconds);
5953
}
60-
61-
private void GetRosTime()
62-
{
63-
if (!ROS2.Ros2cs.Ok())
64-
{
65-
Debug.LogWarning("Cannot update ros time, ros2 either not initialized or shut down already");
66-
return;
67-
}
68-
69-
long nanosec = (long)(clock.Now.Seconds * 1e9);
70-
seconds = (int)(nanosec / 1000000000);
71-
nanoseconds = (uint)(nanosec % 1000000000);
72-
}
7354
}
7455

7556
} // namespace ROS2
File renamed without changes.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2022 Robotec.ai.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System;
16+
17+
namespace ROS2
18+
{
19+
20+
/// <summary>
21+
/// ros2 time source (system time by default).
22+
/// </summary>
23+
public class ROS2TimeSource : ITimeSource
24+
{
25+
private ROS2.Clock clock;
26+
27+
void GetTime(out int seconds, out uint nanoseconds)
28+
{
29+
if (!ROS2.Ros2cs.Ok())
30+
{
31+
seconds = 0;
32+
nanoseconds = 0;
33+
Debug.LogWarning("Cannot acquire valid ros time, ros either not initialized or shut down already");
34+
return;
35+
}
36+
37+
if (clock == null)
38+
{ // Create clock which uses system time by default (unless use_sim_time is set in ros2)
39+
clock = new ROS2.Clock();
40+
}
41+
42+
TimeUtils.TimeFromTotalSeconds(clock.Now.Seconds, out seconds, out nanoseconds);
43+
}
44+
45+
~ROS2TimeSource()
46+
{
47+
if (clock != null)
48+
{
49+
clock.Dispose();
50+
}
51+
}
52+
}
53+
54+
} // namespace ROS2
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2022 Robotec.ai.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
namespace ROS2
16+
{
17+
18+
/// <summary>
19+
/// Interace for acquiring time
20+
/// </summary>
21+
internal static class TimeUtils
22+
{
23+
void TimeFromTotalSeconds(in double seconds, out int seconds, out uint nanoseconds)
24+
{
25+
long nanosec = (long)(seconds * 1e9);
26+
seconds = (int)(nanosec / 1000000000);
27+
nanoseconds = (uint)(nanosec % 1000000000);
28+
}
29+
}
30+
31+
} // namespace ROS2
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2022 Robotec.ai.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System;
16+
using System.Threading;
17+
using UnityEngine;
18+
19+
namespace ROS2
20+
{
21+
22+
/// <summary>
23+
/// Acquires Unity time. Note that Time API only allows main thread access,
24+
/// but this class object also stores last acquired value for other threads.
25+
/// This is done without a warning, so the class will not behave as expected
26+
/// when not used by main thread.
27+
/// </summary>
28+
public class UnityTimeSource : ITimeSource
29+
{
30+
private Thread mainThread;
31+
private double lastReadingSecs;
32+
33+
public UnityTimeSource()
34+
{
35+
mainThread = Thread.CurrentThread;
36+
}
37+
38+
void GetTime(out int seconds, out uint nanoseconds)
39+
{
40+
lastReadingSecs = mainThread.Equals(Thread.CurrentThread) ? Time.timeAsDouble : lastReadingSecs;
41+
TimeUtils.TimeFromTotalSeconds(lastReadingSecs, out seconds, out nanoseconds);
42+
}
43+
}
44+
45+
} // namespace ROS2

0 commit comments

Comments
 (0)