From 3f2c08432d3b0c4c6025c65ef3e726ae649f470f Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 4 Mar 2020 15:11:50 +0100 Subject: [PATCH] timeserver.java added (as found in nodered json) --- src/Timeserver/timeserver.java | 88 ++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/Timeserver/timeserver.java diff --git a/src/Timeserver/timeserver.java b/src/Timeserver/timeserver.java new file mode 100644 index 00000000..0896b23f --- /dev/null +++ b/src/Timeserver/timeserver.java @@ -0,0 +1,88 @@ +/* LoRaWAN Timeserver + +construct 7 byte timesync_answer from gateway timestamp and node's time_sync_req + +byte meaning +1 sequence number (taken from node's time_sync_req) +2 timezone in 15 minutes steps +3..6 current second (from epoch time 1970) +7 1/250ths fractions of current second + +*/ + +function timecompare(a, b) { + + const timeA = a.time; + const timeB = b.time; + + let comparison = 0; + if (timeA > timeB) { + comparison = 1; + } else if (timeA < timeB) { + comparison = -1; + } + return comparison; +} + +let confidence = 2000; // max millisecond diff gateway time to server time + +// guess if we have received a valid time_sync_req command +if (msg.payload.payload_raw.length != 1) + return; + +var deviceMsg = { payload: msg.payload.dev_id }; +var seqNo = msg.payload.payload_raw[0]; +var seqNoMsg = { payload: seqNo }; +var gateway_list = msg.payload.metadata.gateways; + +// filter all gateway timestamps that have milliseconds part (which we assume have a ".") +var gateways = gateway_list.filter(function (element) { + return (element.time.includes(".")); +}); + +var gateway_time = gateways.map(gw => { + return { + time: new Date(gw.time), + eui: gw.gtw_id, + } + }); +var server_time = new Date(msg.payload.metadata.time); + +// validate all gateway timestamps against lorawan server_time (which is assumed to be recent) +var gw_timestamps = gateway_time.filter(function (element) { + return ((element.time > (server_time - confidence) && element.time <= server_time)); +}); + +// if no timestamp left, we have no valid one and exit +if (gw_timestamps.length === 0) { + var notavailMsg = { payload: "n/a" }; + var notimeMsg = { payload: 0xff }; + var buf2 = Buffer.alloc(1); + msg.payload = new Buffer(buf2.fill(0xff)); + msg.port = 9; // Paxcounter TIMEPORT + return [notavailMsg, notavailMsg, deviceMsg, seqNoMsg, msg];} + +// sort time array in ascending order to find most recent timestamp for time answer +gw_timestamps.sort(timecompare); + +var timestamp = gw_timestamps[0].time; +var eui = gw_timestamps[0].eui; +var offset = server_time - timestamp; + +var seconds = Math.floor(timestamp/1000); +var fractions = (timestamp % 1000) / 4; + +let buf = new ArrayBuffer(7); +new DataView(buf).setUint8(0, seqNo); +// Timezone (in 15min steps) +var timezone = 8; // CET = UTC+2h +new DataView(buf).setUint8(1, timezone); +new DataView(buf).setUint32(2, seconds); +new DataView(buf).setUint8(6, fractions); + +msg.payload = new Buffer(new Uint8Array(buf)); +msg.port = 9; // Paxcounter TIMEPORT +var euiMsg = { payload: eui }; +var offsetMsg = { payload: offset }; + +return [euiMsg, offsetMsg, deviceMsg, seqNoMsg, msg]; \ No newline at end of file