ptpv2
Version:
IEEE1588:2008 PTPv2 client implementation
195 lines (155 loc) • 4.97 kB
JavaScript
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();
}
}
});