-
Notifications
You must be signed in to change notification settings - Fork 3
/
masterclock.js
149 lines (124 loc) · 4.5 KB
/
masterclock.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
Copyright 2015 Norut Northern Research Institute
Author : Ingar Mæhlum Arntzen
This file is part of the Timingsrc module.
Timingsrc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Timingsrc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Timingsrc. If not, see <http://www.gnu.org/licenses/>.
*/
/*
MASTER CLOCK
MasterClock is the reference clock used by TimingObjects.
It is implemented using performance.now,
but is skewed and rate-adjusted relative to this local clock.
This allows it to be used as a master clock in a distributed system,
where synchronization is generally relative to some other clock than the local clock.
The master clock may need to be adjusted in time, for instance as a response to
varying estimation of clock skew or drift. The master clock supports an adjust primitive for this purpose.
What policy is used for adjusting the master clock may depend on the circumstances
and is out of scope for the implementation of the MasterClock.
This policy is implemented by the timing object. This policy may or may not
provide monotonicity.
A change event is emitted every time the masterclock is adjusted.
Vector values define
- position : absolute value of the clock in seconds
- velocity : how many seconds added per second (1.0 exactly - or very close)
- timestamp : timstamp from local system clock (performance) in seconds. Defines point in time where position and velocity are valid.
If initial vector is not provided, default value is
{position: now, velocity: 1.0, timestamp: now};
implying that master clock is equal to local clock.
*/
const timeoututils = require('./timeoututils')
const eventify = require('./eventify')
'use strict'
if (typeof window === 'undefined') {
window = {}
}
// Need a polyfill for performance,now as Safari on ios doesn't have it...
(function () {
if ('performance' in window === false) {
window.performance = {}
window.performance.offset = new Date().getTime()
}
if ('now' in window.performance === false) {
window.performance.now = function now () {
return new Date().getTime() - window.performance.offset
}
}
})()
// local clock in seconds
var localClock = {
now: function () { return window.performance.now() / 1000.0 }
}
var calculateVector = function (vector, tsSec) {
if (tsSec === undefined) tsSec = localClock.now()
var deltaSec = tsSec - vector.timestamp
return {
position: vector.position + vector.velocity * deltaSec,
velocity: vector.velocity,
timestamp: tsSec
}
}
var MasterClock = function (options) {
var now = localClock.now()
options = options || {}
this._vector = { position: now, velocity: 1.0, timestamp: now }
// event support
eventify.eventifyInstance(this)
this.eventifyDefineEvent('change') // define change event (no init-event)
// adjust
this.adjust(options)
}
eventify.eventifyPrototype(MasterClock.prototype)
/*
ADJUST
- could also accept timestamp for velocity if needed?
- given skew is relative to local clock
- given rate is relative to local clock
*/
MasterClock.prototype.adjust = function (options) {
options = options || {}
var now = localClock.now()
var nowVector = this.query(now)
if (options.skew === undefined && options.rate === undefined) {
return
}
this._vector = {
position: (options.skew !== undefined) ? now + options.skew : nowVector.position,
velocity: (options.rate !== undefined) ? options.rate : nowVector.velocity,
timestamp: nowVector.timestamp
}
this.eventifyTriggerEvent('change')
}
/*
NOW
- calculates the value of the clock right now
- shorthand for query
*/
MasterClock.prototype.now = function () {
return calculateVector(this._vector, localClock.now()).position
}
/*
QUERY
- calculates the state of the clock right now
- result vector includes position and velocity
*/
MasterClock.prototype.query = function (now) {
return calculateVector(this._vector, now)
}
/*
Timeout support
*/
MasterClock.prototype.setTimeout = function (callback, delay, options) {
return timeoututils.setTimeout(this, callback, delay, options)
}
module.exports = {
MasterClock
}