Skip to content

Commit ba43707

Browse files
author
AJ Keller
authored
Merge pull request #70 from pushtheworldllc/bug-time-sync
Bug time sync
2 parents d51c8f8 + e88922c commit ba43707

11 files changed

+812
-231
lines changed

README.md

Lines changed: 138 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -162,31 +162,56 @@ Time Syncing
162162
------------
163163
You must be using OpenBCI firmware version 2 in order to do time syncing. After you `.connect()` and send a `.softReset()`, you can call `.usingVersionTwoFirmware()` to get a boolean response as to if you are using `v1` or `v2`.
164164

165-
Now using firmware `v2`, the fun begins! What we set out to do was synchronize not only the board to this modules clock, but also with a global NTP server, so that you could use several different devices and all sync to the same global server. That way you can really do some serious cloud computing!
165+
Now using firmware `v2`, the fun begins! We synchronize the Board's clock with the module's time. In firmware `v2` we leverage samples with time stamps and different time stamps and unique new features to calculate a time sync strategy. Time syncing has been verified to +/- 4ms and a test report is on the way. We are still working on the synchronize of this module and an NTP server, this is an open call for any NTP experts out there! With a global NTP server you could use several different devices and all sync to the same time server. That way you can really do some serious cloud computing!
166166

167+
Keep your resync interval above 50ms. While it's important to resync every couple minutes due to drifting of clocks, please do not try to sync without getting the last sync event! We can only support one sync operation at a time!
168+
169+
Using local computer time:
167170
```js
168171
var OpenBCIBoard = require('openbci').OpenBCIBoard,
169172
ourBoard = new OpenBCIBoard({
170-
verbose:true,
171-
timeSync: true // Sync up with NTP servers in constructor
173+
verbose:true
172174
});
173175

176+
const resyncPeriodMin = 5; // re sync every five minutes
177+
const secondsInMinute = 60;
178+
var sampleRate = k.OBCISampleRate250; // Default to 250, ALWAYS verify with a call to `sampleRate` after `ready` event!
179+
var timeSyncPossible = false;
180+
174181
// Call to connect
175182
ourBoard.connect(portName).then(() => {
176183
ourBoard.on('ready',() => {
184+
// Get the sample rate after 'ready' event!
185+
sampleRate = ourBoard.sampleRate();
186+
// Find out if you can even time sync, you must be using v2 and this is only accurate after a `.softReset()` call which is called internally on `.connect()`. We parse the `.softReset()` response for the presence of firmware version 2 properties.
187+
timeSyncPossible = ourBoard.usingVersionTwoFirmware();
188+
177189
ourBoard.streamStart()
190+
.then(() => {
191+
/** Start streaming command sent to board. */
192+
})
178193
.catch(err => {
179194
console.log(`stream start: ${err}`);
180195
})
181196
});
182-
// 'synced' event contains the guts of the whole sync operation.
183-
ourBoard.on('synced',obj => {
184-
console.log('sync obj',obj);
185-
});
197+
198+
// PTW recommends sample driven
186199
ourBoard.on('sample',sample => {
187-
// Resynchronize every 100 samples
188-
if (sample.sampleNumber % 100 === 0) {
189-
ourBoard.syncClocks();
200+
// Resynchronize every every 5 minutes
201+
if (sample._count % (sampleRate * resyncPeriodMin * secondsInMinute) === 0) {
202+
ourBoard.syncClocksFull()
203+
.then(syncObj => {
204+
// Sync was successful
205+
if (syncObj.valid) {
206+
// Log the object to check it out!
207+
console.log(`syncObj`,syncObj);
208+
209+
// Sync was not successful
210+
} else {
211+
// Retry it
212+
console.log(`Was not able to sync, please retry?`);
213+
}
214+
});
190215
}
191216

192217
if (sample.timeStamp) { // true after the first sync
@@ -351,7 +376,9 @@ Board optional configurations.
351376
* `None` - Do not inject line noise.
352377
* `simulatorSampleRate` {Number} - The sample rate to use for the simulator. Simulator will set to 125 if `simulatorDaisyModuleAttached` is set `true`. However, setting this option overrides that setting and this sample rate will be used. (Default is `250`)
353378
* `simulatorSerialPortFailure` {Boolean} - Simulates not being able to open a serial connection. Most likely due to a OpenBCI dongle not being plugged in.
354-
* `timeSync` - {Boolean} Syncs the module up with an SNTP time server. Syncs the board on startup with the SNTP time. Adds a time stamp to the AUX channels.
379+
* `sntpTimeSync` - {Boolean} Syncs the module up with an SNTP time server and uses that as single source of truth instead of local computer time. (Default `true`)
380+
* `sntpTimeSyncHost` - {String} The sntp server to use, can be either sntp or ntp (Defaults `pool.ntp.org`).
381+
* `sntpTimeSyncPort` - {Number} The port to access the sntp server (Defaults `123`)
355382
* `verbose` {Boolean} - Print out useful debugging events
356383
357384
**Note, we have added support for either all lowercase OR camel case for the options, use whichever style you prefer.**
@@ -493,7 +520,7 @@ A Number, specifies which channel you want to test.
493520
494521
**_Returns_** a promise that resolves a single channel impedance object.
495522
496-
Example:
523+
**Example**
497524
```js
498525
var OpenBCIBoard = require('openbci').OpenBCIBoard;
499526
var ourBoard = new OpenBCIBoard();
@@ -535,7 +562,7 @@ A Number, specifies which channel you want to test.
535562
536563
**_Returns_** a promise that resolves a single channel impedance object.
537564
538-
Example:
565+
**Example**
539566
```js
540567
var OpenBCIBoard = require('openbci').OpenBCIBoard;
541568
var ourBoard = new OpenBCIBoard();
@@ -577,7 +604,7 @@ A Number, specifies which channel you want to test.
577604
578605
**_Returns_** a promise that resolves a single channel impedance object.
579606
580-
Example:
607+
**Example**
581608
```js
582609
var OpenBCIBoard = require('openbci').OpenBCIBoard;
583610
var ourBoard = new OpenBCIBoard();
@@ -787,21 +814,9 @@ Stateful method for querying the current offset only when the last one is too ol
787814
788815
**_Returns_** a promise with the time offset
789816
790-
### .sntpGetServerTime()
791-
792-
Get time from the SNTP server. Must have internet connection!
793-
794-
**_Returns_** a promise fulfilled with time object
795-
796-
### .sntpNow()
797-
798-
This function gets SNTP time since Jan 1, 1970, if we call this after a successful `.sntpStart()` this time will be synced, or else we will just get the current computer time, the case if there is no internet.
799-
800-
**_Returns_** time since UNIX epoch in ms.
801-
802817
### .sntpStart()
803818
804-
This starts the SNTP server and gets it to remain in sync with the SNTP server;
819+
This starts the SNTP server and gets it to remain in sync with the SNTP server.
805820
806821
**_Returns_** a promise if the module was able to sync with NTP server.
807822
@@ -841,6 +856,96 @@ Send the command to tell the board to start the syncing protocol. Must be connec
841856
842857
**_Returns_** {Promise} resolves if the command was sent to the write queue, rejects if unable.
843858
859+
### .syncClocksFull()
860+
861+
Send the command to tell the board to start the syncing protocol. Must be connected, streaming and using v2 firmware. Uses the `synced` event to ensure multiple syncs don't overlap.
862+
863+
**Note, this functionality requires OpenBCI Firmware Version 2.0**
864+
865+
**_Returns_** {Promise} resolves if `synced` event is emitted, rejects if not connected or using firmware v2. Resolves with a synced object:
866+
```javascript
867+
{
868+
boardTime: 0, // The time contained in the time sync set packet.
869+
correctedTransmissionTime: false, // If the confirmation and the set packet arrive in the same serial flush we have big problem! This will be true in this case. See source code for full explanation.
870+
timeSyncSent: 0, // The time the `<` was sent to the Dongle.
871+
timeSyncSentConfirmation: 0, // The time the `<` was sent to the Board; It's really the time `,` was received from the Dongle.
872+
timeSyncSetPacket: 0, // The time the set packet was received from the Board.
873+
timeRoundTrip: 0, // Simply timeSyncSetPacket - timeSyncSent.
874+
timeTransmission: 0, // Estimated time it took for time sync set packet to be sent from Board to Driver.
875+
timeOffset: 0, // The map (or translation) from boardTime to module time.
876+
valid: false // If there was an error in the process, valid will be false and no time sync was done. It's important to resolve this so we can perform multiple promise syncs as show in the example below.
877+
}
878+
```
879+
880+
**Example**
881+
882+
```javascript
883+
var OpenBCIBoard = require('openbci').OpenBCIBoard,
884+
ourBoard = new OpenBCIBoard({
885+
verbose:true
886+
});
887+
888+
var portName = /* INSERT PORT NAME HERE */;
889+
var samples = []; // Array to store time synced samples into
890+
var timeSyncActivated = false;
891+
892+
ourBoard.connect(portName)
893+
.then(() => {
894+
ourBoard.on('ready',() => {
895+
ourBoard.streamStart()
896+
.then(() => {
897+
/** Could also call `.syncClocksFull()` here */
898+
})
899+
.catch(err => {
900+
console.log(`Error starting stream ${err}`);
901+
})
902+
});
903+
ourBoard.on('sample',sample => {
904+
/** If we are not synced, then do that! */
905+
if (timeSyncActivated === false) {
906+
timeSyncActivated = true;
907+
ourBoard.syncClocksFull()
908+
.then(syncObj => {
909+
if (syncObj.valid) {
910+
console.log('1st sync done');
911+
}
912+
return ourBoard.syncClocksFull();
913+
})
914+
.then(syncObj => {
915+
if (syncObj.valid) {
916+
console.log('2nd sync done');
917+
}
918+
return ourBoard.syncClocksFull();
919+
})
920+
.then(syncObj => {
921+
if (syncObj.valid) {
922+
console.log('3rd sync done');
923+
}
924+
return ourBoard.syncClocksFull();
925+
})
926+
.then(syncObj => {
927+
if (syncObj.valid) {
928+
console.log('4th sync done');
929+
930+
}
931+
/* Do awesome time syncing stuff */
932+
})
933+
.catch(err => {
934+
console.log(`sync err ${err}`);
935+
});
936+
}
937+
if (startLoggingSamples && sample.hasOwnProperty("timeStamp") && sample.hasOwnProperty("boardTime")) {
938+
/** If you only want to log samples with time stamps */
939+
samples.push(sample);
940+
}
941+
});
942+
})
943+
.catch(err => {
944+
console.log(`connect ${err}`);
945+
});
946+
947+
```
948+
844949
### .testSignal(signal)
845950
846951
Apply the internal test signal to all channels.
@@ -859,6 +964,12 @@ A String indicating which test signal to apply
859964
860965
**_Returns_** a promise, if the commands were sent to write buffer.
861966
967+
### .time()
968+
969+
Uses `._sntpNow()` time when sntpTimeSync specified in options, or else use Date.now() for time.
970+
971+
**_Returns_** time since UNIX epoch in ms.
972+
862973
### .usingVersionTwoFirmware()
863974
864975
Convenience method to determine if you can use firmware v2.x.x capabilities.

changelog.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
# 1.1.0
2+
3+
### New Features
4+
5+
* Add function `.time()` which should be used in time syncing
6+
* Add function `.syncClocksFull()` which should be used for immediate consecutive time syncs
7+
* Synced object can be emitted on `synced` event. Check `valid` property for if the sync was done
8+
* Add detailed description of object returned on `synced` event to README.md
9+
10+
### Breaking Changes
11+
12+
* Changed option named `timeSync` to `sntpTimeSync`
13+
* Removed function called `.sntpNow()` because it was replaced by `.time()`
14+
15+
### Bug Fixes
16+
17+
* Time sync working
18+
* Module could not work with local time
19+
120
# 1.0.1
221

322
### New Features

0 commit comments

Comments
 (0)