Merge pull request #459 from GreyRook/add-timezone
refactor of time synchronization protocol
This commit is contained in:
		
						commit
						dd2dcf32cb
					
				@ -7,12 +7,14 @@
 | 
				
			|||||||
#include "timekeeper.h"
 | 
					#include "timekeeper.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//#define TIME_SYNC_TRIGGER 100 // threshold for time sync [milliseconds]
 | 
					//#define TIME_SYNC_TRIGGER 100 // threshold for time sync [milliseconds]
 | 
				
			||||||
#define TIME_SYNC_FRAME_LENGTH 0x05 // timeserver answer frame length [bytes]
 | 
					#define TIME_SYNC_FRAME_LENGTH 0x07 // timeserver answer frame length [bytes]
 | 
				
			||||||
#define TIME_SYNC_FIXUP 4 // calibration to fixup processing time [milliseconds]
 | 
					#define TIME_SYNC_FIXUP 4 // calibration to fixup processing time [milliseconds]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void timesync_init(void);
 | 
					void timesync_init(void);
 | 
				
			||||||
void send_timesync_req(void);
 | 
					void send_timesync_req(void);
 | 
				
			||||||
int recv_timesync_ans(const uint8_t seq_no, const uint8_t buf[], const uint8_t buf_len);
 | 
					
 | 
				
			||||||
 | 
					int recv_timesync_ans(const uint8_t buf[], uint8_t buf_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void process_timesync_req(void *taskparameter);
 | 
					void process_timesync_req(void *taskparameter);
 | 
				
			||||||
void store_time_sync_req(uint32_t t_millisec);
 | 
					void store_time_sync_req(uint32_t t_millisec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -187,7 +187,7 @@
 | 
				
			|||||||
        "type": "function",
 | 
					        "type": "function",
 | 
				
			||||||
        "z": "449c1517.e25f4c",
 | 
					        "z": "449c1517.e25f4c",
 | 
				
			||||||
        "name": "Timeserver Logic",
 | 
					        "name": "Timeserver Logic",
 | 
				
			||||||
        "func": "/* LoRaWAN Timeserver\n\nconstruct 5 byte timesync_answer from gateway timestamp and node's time_sync_req\n\nbyte    meaning\n1..4    current second (from epoch time 1970)\n5       1/250ths fractions of current second\n\nFPort = sequence number (taken from node's time_sync_req)\n\n*/\n\nfunction timecompare(a, b) {\n  \n  const timeA = a.time;\n  const timeB = b.time;\n\n  let comparison = 0;\n  if (timeA > timeB) {\n    comparison = 1;\n  } else if (timeA < timeB) {\n    comparison = -1;\n  }\n  return comparison;\n}\n\nlet confidence = 2000; // max millisecond diff gateway time to server time\n\n// guess if we have received a valid time_sync_req command\nif (msg.payload.payload_raw.length != 1)\n  return;\n\nvar deviceMsg = { payload: msg.payload.dev_id };\nvar seqNo = msg.payload.payload_raw[0];\nvar seqNoMsg = { payload: seqNo };\nvar gateway_list = msg.payload.metadata.gateways;\n\n// filter all gateway timestamps that have milliseconds part (which we assume have a \".\")\nvar gateways = gateway_list.filter(function (element) {\n  return (element.time.includes(\".\"));\n});\n\nvar gateway_time = gateways.map(gw => {\n    return {\n      time: new Date(gw.time),\n      eui: gw.gtw_id,\n      }\n  });\nvar server_time = new Date(msg.payload.metadata.time);\n\n// validate all gateway timestamps against lorawan server_time (which is assumed to be recent)\nvar gw_timestamps = gateway_time.filter(function (element) {\n  return ((element.time > (server_time - confidence) && element.time <= server_time));\n});\n\n// if no timestamp left, we have no valid one and exit\nif (gw_timestamps.length === 0) {\n    var notavailMsg = { payload: \"n/a\" };\n    var notimeMsg = { payload: 0xff };    \n    var buf2 = Buffer.alloc(1);\n    msg.payload = new Buffer(buf2.fill(0xff));\n    return [notavailMsg, notavailMsg, deviceMsg, seqNoMsg, msg];}\n\n// sort time array in ascending order to find most recent timestamp for time answer\ngw_timestamps.sort(timecompare);\n\nvar timestamp = gw_timestamps[0].time;\nvar eui = gw_timestamps[0].eui;\nvar offset = server_time - timestamp;\n\nvar seconds = Math.floor(timestamp/1000);\nvar fractions = (timestamp % 1000) / 4;\n\nlet buf = new ArrayBuffer(5);\nnew DataView(buf).setUint32(0, seconds);\nnew DataView(buf).setUint8(4, fractions);\n\nmsg.payload = new Buffer(new Uint8Array(buf));\nmsg.port = seqNo;\nvar euiMsg = { payload: eui };\nvar offsetMsg = { payload: offset };\n\nreturn [euiMsg, offsetMsg, deviceMsg, seqNoMsg, msg];",
 | 
					        "func": "/* LoRaWAN Timeserver\n\nconstruct 5 byte timesync_answer from gateway timestamp and node's time_sync_req\n\nbyte    meaning\n1..4    current second (from epoch time 1970)\n5       1/250ths fractions of current second\n\nFPort = sequence number (taken from node's time_sync_req)\n\n*/\n\nfunction timecompare(a, b) {\n  \n  const timeA = a.time;\n  const timeB = b.time;\n\n  let comparison = 0;\n  if (timeA > timeB) {\n    comparison = 1;\n  } else if (timeA < timeB) {\n    comparison = -1;\n  }\n  return comparison;\n}\n\nlet confidence = 2000; // max millisecond diff gateway time to server time\n\n// guess if we have received a valid time_sync_req command\nif (msg.payload.payload_raw.length != 1)\n  return;\n\nvar deviceMsg = { payload: msg.payload.dev_id };\nvar seqNo = msg.payload[0];\nvar seqNoMsg = { payload: seqNo };\nvar gateway_list = msg.payload.metadata.gateways;\n\n// filter all gateway timestamps that have milliseconds part (which we assume have a \".\")\nvar gateways = gateway_list.filter(function (element) {\n  return (element.time.includes(\".\"));\n});\n\nvar gateway_time = gateways.map(gw => {\n    return {\n      time: new Date(gw.time),\n      eui: gw.gtw_id,\n      }\n  });\nvar server_time = new Date(msg.payload.metadata.time);\n\n// validate all gateway timestamps against lorawan server_time (which is assumed to be recent)\nvar gw_timestamps = gateway_time.filter(function (element) {\n  return ((element.time > (server_time - confidence) && element.time <= server_time));\n});\n\n// if no timestamp left, we have no valid one and exit\nif (gw_timestamps.length === 0) {\n    var notavailMsg = { payload: \"n/a\" };\n    var notimeMsg = { payload: 0xff };    \n    var buf2 = Buffer.alloc(1);\n    msg.payload = new Buffer(buf2.fill(0xff));\n    return [notavailMsg, notavailMsg, deviceMsg, seqNoMsg, msg];}\n\n// sort time array in ascending order to find most recent timestamp for time answer\ngw_timestamps.sort(timecompare);\n\nvar timestamp = gw_timestamps[0].time;\nvar eui = gw_timestamps[0].eui;\nvar offset = server_time - timestamp;\n\nvar seconds = Math.floor(timestamp/1000);\nvar fractions = (timestamp % 1000) / 4;\n\nlet buf = new ArrayBuffer(7);\nnew DataView(buf).setUint8(0, seqNo);\n// Timezone (in 15min steps)\nvar timezone = 8; // CET = UTC+2h\nnew DataView(buf).setUint8(1, timezone);\nnew DataView(buf).setUint32(2, seconds);\nnew DataView(buf).setUint8(6, fractions);\n\nmsg.payload = new Buffer(new Uint8Array(buf));\nmsg.port = 9;\nvar euiMsg = { payload: eui };\nvar offsetMsg = { payload: offset };\n\nreturn [euiMsg, offsetMsg, deviceMsg, seqNoMsg, msg];",
 | 
				
			||||||
        "outputs": 5,
 | 
					        "outputs": 5,
 | 
				
			||||||
        "noerr": 0,
 | 
					        "noerr": 0,
 | 
				
			||||||
        "x": 350,
 | 
					        "x": 350,
 | 
				
			||||||
 | 
				
			|||||||
@ -561,8 +561,8 @@ void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#if (TIME_SYNC_LORASERVER)
 | 
					#if (TIME_SYNC_LORASERVER)
 | 
				
			||||||
    // valid timesync answer -> call timesync processor
 | 
					    // valid timesync answer -> call timesync processor
 | 
				
			||||||
    if ((port >= TIMEANSWERPORT_MIN) && (port <= TIMEANSWERPORT_MAX)) {
 | 
					    if (port == TIMEPORT) {
 | 
				
			||||||
      recv_timesync_ans(port, pMsg, nMsg);
 | 
					      recv_timesync_ans(pMsg, nMsg);
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -97,9 +97,9 @@
 | 
				
			|||||||
#define BEACONPORT                      6       // beacon alarms
 | 
					#define BEACONPORT                      6       // beacon alarms
 | 
				
			||||||
#define BMEPORT                         7       // BME680 sensor
 | 
					#define BMEPORT                         7       // BME680 sensor
 | 
				
			||||||
#define BATTPORT                        8       // battery voltage
 | 
					#define BATTPORT                        8       // battery voltage
 | 
				
			||||||
#define TIMEPORT                        9       // time query
 | 
					#define TIMEPORT                        9       // time query and response
 | 
				
			||||||
#define TIMEANSWERPORT_MIN              0xA0    // time answer, start of port range
 | 
					#define TIMEDIFFPORT                    13      // time adjust diff
 | 
				
			||||||
#define TIMEANSWERPORT_MAX              0xDF    // time answer, end of port range
 | 
					#define TIMEREQUEST_MAX_SEQNO          250    // time answer, start of port range
 | 
				
			||||||
#define SENSOR1PORT                     10      // user sensor #1
 | 
					#define SENSOR1PORT                     10      // user sensor #1
 | 
				
			||||||
#define SENSOR2PORT                     11      // user sensor #2
 | 
					#define SENSOR2PORT                     11      // user sensor #2
 | 
				
			||||||
#define SENSOR3PORT                     12      // user sensor #3
 | 
					#define SENSOR3PORT                     12      // user sensor #3
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@ typedef std::chrono::duration<long long int, std::ratio<1, 1000>>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
TaskHandle_t timeSyncReqTask = NULL;
 | 
					TaskHandle_t timeSyncReqTask = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static uint8_t time_sync_seqNo = random(TIMEANSWERPORT_MIN, TIMEANSWERPORT_MAX);
 | 
					static uint8_t time_sync_seqNo = 0;
 | 
				
			||||||
static bool timeSyncPending = false;
 | 
					static bool timeSyncPending = false;
 | 
				
			||||||
static myClock_timepoint time_sync_tx[TIME_SYNC_SAMPLES];
 | 
					static myClock_timepoint time_sync_tx[TIME_SYNC_SAMPLES];
 | 
				
			||||||
static myClock_timepoint time_sync_rx[TIME_SYNC_SAMPLES];
 | 
					static myClock_timepoint time_sync_rx[TIME_SYNC_SAMPLES];
 | 
				
			||||||
@ -93,9 +93,10 @@ void process_timesync_req(void *taskparameter) {
 | 
				
			|||||||
                        time_point_cast<milliseconds>(time_sync_tx[k]);
 | 
					                        time_point_cast<milliseconds>(time_sync_tx[k]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // wrap around seqNo, keeping it in time port range
 | 
					      // wrap around seqNo, keeping it in time port range
 | 
				
			||||||
      time_sync_seqNo = (time_sync_seqNo < TIMEANSWERPORT_MAX)
 | 
					      time_sync_seqNo++;
 | 
				
			||||||
                            ? time_sync_seqNo + 1
 | 
					      if(time_sync_seqNo > TIMEREQUEST_MAX_SEQNO) {
 | 
				
			||||||
                            : TIMEANSWERPORT_MIN;
 | 
					        time_sync_seqNo = 0;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (i < TIME_SYNC_SAMPLES - 1) {
 | 
					      if (i < TIME_SYNC_SAMPLES - 1) {
 | 
				
			||||||
        // wait until next cycle
 | 
					        // wait until next cycle
 | 
				
			||||||
@ -154,7 +155,9 @@ void store_time_sync_req(uint32_t timestamp) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// process timeserver timestamp answer, called from lorawan.cpp
 | 
					// process timeserver timestamp answer, called from lorawan.cpp
 | 
				
			||||||
int recv_timesync_ans(const uint8_t seq_no, const uint8_t buf[], const uint8_t buf_len) {
 | 
					int recv_timesync_ans(const uint8_t buf[], const uint8_t buf_len) {
 | 
				
			||||||
 | 
					  uint8_t seq_no = buf[0];
 | 
				
			||||||
 | 
					  buf++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // if no timesync handshake is pending then exit
 | 
					  // if no timesync handshake is pending then exit
 | 
				
			||||||
  if (!timeSyncPending)
 | 
					  if (!timeSyncPending)
 | 
				
			||||||
@ -177,9 +180,14 @@ int recv_timesync_ans(const uint8_t seq_no, const uint8_t buf[], const uint8_t b
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // the 5th byte contains the fractional seconds in 2^-8 second steps
 | 
					    // the 5th byte contains the fractional seconds in 2^-8 second steps
 | 
				
			||||||
    // (= 1/250th sec), we convert this to ms
 | 
					    // (= 1/250th sec), we convert this to ms
 | 
				
			||||||
    uint16_t timestamp_msec = 4 * buf[4];
 | 
					    uint16_t timestamp_msec = 4 * buf[6];
 | 
				
			||||||
    // pointers to 4 bytes containing UTC seconds since unix epoch, msb
 | 
					    // pointers to 4 bytes 4 bytes containing UTC seconds since unix epoch, msb
 | 
				
			||||||
    uint32_t timestamp_sec, *timestamp_ptr;
 | 
					    uint32_t timestamp_sec, *timestamp_ptr;
 | 
				
			||||||
 | 
					    uint32_t timezone_sec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // extract timezone from buffer (in 15min steps, one step being 15min * 60s = 900s)
 | 
				
			||||||
 | 
					    timezone_sec = buf[0]*900;
 | 
				
			||||||
 | 
					    buf++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // convert buffer to uint32_t, octet order is big endian
 | 
					    // convert buffer to uint32_t, octet order is big endian
 | 
				
			||||||
    timestamp_ptr = (uint32_t *)buf;
 | 
					    timestamp_ptr = (uint32_t *)buf;
 | 
				
			||||||
@ -187,7 +195,7 @@ int recv_timesync_ans(const uint8_t seq_no, const uint8_t buf[], const uint8_t b
 | 
				
			|||||||
    timestamp_sec = __builtin_bswap32(*timestamp_ptr);
 | 
					    timestamp_sec = __builtin_bswap32(*timestamp_ptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // construct the timepoint when message was seen on gateway
 | 
					    // construct the timepoint when message was seen on gateway
 | 
				
			||||||
    time_sync_rx[k] += seconds(timestamp_sec) + milliseconds(timestamp_msec);
 | 
					    time_sync_rx[k] += seconds(timestamp_sec+timezone_sec) + milliseconds(timestamp_msec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // we guess timepoint is recent if it newer than code compile date
 | 
					    // we guess timepoint is recent if it newer than code compile date
 | 
				
			||||||
    if (timeIsValid(myClock::to_time_t(time_sync_rx[k]))) {
 | 
					    if (timeIsValid(myClock::to_time_t(time_sync_rx[k]))) {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user