forked from philhartung/node-ptpv2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ptp.js
194 lines (155 loc) · 4.86 KB
/
ptp.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
var dgram = require('dgram');
//ptp settings
var ptp_domain = 0;
var ptpMaster = '';
var sync = false;
var addr = '127.0.0.1';
var cb = function(){};
var minSyncInterval = 10000; // in ms
//PTPv2
var ptpClientEvent = dgram.createSocket({ type: 'udp4', reuseAddr: true });
var ptpClientGeneral = dgram.createSocket({ type: 'udp4', reuseAddr: true });
var ptpMulticastAddrs = ['224.0.1.129', '224.0.1.130', '224.0.1.131', '224.0.1.132'];
//vars
var t1, ts1, t2, ts2;
var offset = [0, 0];
var sync_seq;
var req_seq = 0;
var lastSync = 0;
//functions
//creates ptp delay_req buffer
var ptp_delay_req = function(){
var length = 52;
var buffer = Buffer.alloc(length);
req_seq = (req_seq + 1) % 0x10000;
buffer.writeUInt8(1, 0);
buffer.writeUInt8(2, 1);
buffer.writeUInt16BE(length, 2);
buffer.writeUInt16BE(req_seq, 30);
return buffer;
}
var getCorrectedTime = function(){
var time = process.hrtime();
var timeS = time[0] - offset[0];
var timeNS = time[1] - offset[1];
return [timeS, timeNS];
}
//export functions
//calculated ptp time
exports.ptp_time = function(){
var time = process.hrtime();
var timeS = time[0] - offset[0];
var timeNS = time[1] - offset[1];
return [timeS, timeNS];
}
exports.is_synced = function(){
return sync;
}
exports.ptp_master = function(){
return ptpMaster;
}
exports.init = function(interface, domain, callback){
addr = interface ? interface : '127.0.0.1';
ptp_domain = domain ? domain : 0;
cb = callback ? callback : function(){};
ptpClientEvent.bind(319);
ptpClientGeneral.bind(320);
}
//event msg client
ptpClientEvent.on('listening', function() {
ptpClientEvent.addMembership(ptpMulticastAddrs[ptp_domain], addr);
});
ptpClientEvent.on('message', function(buffer, remote) {
var recv_ts = getCorrectedTime();//safe timestamp for ts1
//check buffer length
if(buffer.length < 31)
return;
//read values from buffer
var type = buffer.readUInt8(0) & 0x0f;
var version = buffer.readUInt8(1);
var length = buffer.readUInt16BE(2);
var domain = buffer.readUInt8(4);
var flags = buffer.readUInt16BE(6);
var source = buffer.toString('hex', 20, 28).match(/.{1,2}/g).join('-')+':0';
var sourceAlt = buffer.toString('hex', 20, 28).match(/.{1,2}/g).join(':');
var sequence = buffer.readUInt16BE(30);
if(version != 2 || domain != ptp_domain)//check for version 2 and domain 0
return;
if(type != 0)//only process sync messages
return;
//do we have a new ptp master?
if(source != ptpMaster){
ptpMaster = source;
sync = false;
}
//save sequence number
sync_seq = sequence;
//check if master is two step or not
if((flags & 0x0200) == 0x0200){
//two step, wait for follow_up msg for accurate t1
ts1 = recv_ts;
}else if(Date.now() - lastSync > minSyncInterval){
//got accurate t1 (no follow_up msg)
ts1 = recv_ts;
//read t1 timestamp
var tsS = (buffer.readUInt16BE(34) << 4) + buffer.readUInt32BE(36);
var tsNS = buffer.readUInt32BE(40);
t1 = [tsS, tsNS];
//send delay_req
ptpClientEvent.send(ptp_delay_req(), 319, ptpMulticastAddrs[ptp_domain], function(){
t2 = getCorrectedTime();
});
t2 = getCorrectedTime();
}
});
//general msg Client
ptpClientGeneral.on('listening', function() {
ptpClientGeneral.addMembership(ptpMulticastAddrs[ptp_domain], addr);
});
ptpClientGeneral.on('message', function(buffer, remote) {
//safe timestamp for ts2
var recv_ts = getCorrectedTime();
//check buffer length
if(buffer.length < 31)
return;
//read values from buffer
var type = buffer.readUInt8(0) & 0x0f;
var version = buffer.readUInt8(1);
var length = buffer.readUInt16BE(2);
var domain = buffer.readUInt8(4);
var flags = buffer.readUInt16BE(6);
var source = buffer.toString('hex', 20, 28).match(/.{1,2}/g).join('-')+':0';
var sequence = buffer.readUInt16BE(30);
//check for version 2 and domain
if(version != 2 || domain != ptp_domain)
return;
if(type == 0x08 && sync_seq == sequence && Date.now() - lastSync > minSyncInterval){ //follow up msg with current seq
//read t1 timestamp
var tsS = (buffer.readUInt16BE(34) << 4) + buffer.readUInt32BE(36);
var tsNS = buffer.readUInt32BE(40);
t1 = [tsS, tsNS];
//send delay_req
ptpClientEvent.send(ptp_delay_req(), 319, ptpMulticastAddrs[ptp_domain], function(){
t2 = getCorrectedTime();
});
t2 = getCorrectedTime();
}else if(type == 0x09 && req_seq == sequence){ //delay_rsp msg
//read ts2 timestamp
var tsS = (buffer.readUInt16BE(34) << 4) + buffer.readUInt32BE(36);
var tsNS = buffer.readUInt32BE(40);
ts2 = [tsS, tsNS];
//calc offset
let delta = (0.5 * (ts1[0] - t1[0] - ts2[0] + t2[0])) * 1000000000 + 0.5 * (ts1[1] - t1[1] - ts2[1] + t2[1]);
let deltaSplit = [0, 0];
deltaSplit[1] = (delta % 1000000000);
deltaSplit[0] = Math.round((delta - deltaSplit[1]) / 1000000000);
offset[0] += deltaSplit[0];
offset[1] += deltaSplit[1];
lastSync = Date.now();
//check if the clock was synced before
if(!sync){
sync = true;
cb();
}
}
});