Comparar commits

...

7 Commits

Autor SHA1 Mensagem Data
AJ Keller 6fd9ddf01b Merge pull request #86 from aj-ptw/time-sync-example
Add example for time syncing
2016-09-29 16:15:06 -04:00
AJ Keller fe2abac07b Add example for time syncing 2016-09-29 16:10:16 -04:00
AJ Keller 2528d56ac9 Update package.json 2016-09-29 14:55:25 -04:00
AJ Keller 18b137b498 Merge pull request #85 from aj-ptw/fix-constants-constructor
Fix constants not set in constructor
2016-09-29 14:52:50 -04:00
AJ Keller 470ee81928 Fix constants not set in constructor 2016-09-29 14:44:59 -04:00
AJ Keller 3c46100e1d Merge pull request #84 from aj-ptw/dropped-packet-alert
Add dropped packet detection
2016-09-29 10:26:50 -04:00
AJ Keller 2c0b2e37d2 Add dropped packet detection 2016-09-29 10:22:02 -04:00
11 arquivos alterados com 271 adições e 7 exclusões
+6 -1
Ver Arquivo
@@ -895,6 +895,7 @@ Send the command to tell the board to start the syncing protocol. Must be connec
timeRoundTrip: 0, // Simply timeSyncSetPacket - timeSyncSent.
timeTransmission: 0, // Estimated time it took for time sync set packet to be sent from Board to Driver.
timeOffset: 0, // The map (or translation) from boardTime to module time.
timeOffsetMaster: 0, // The map (or translation) from boardTime to module time averaged over time syncs.
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.
}
```
@@ -1040,13 +1041,17 @@ ourBoard.write('o');
Emitted when the serial connection to the board is closed.
### <a name="event-close"></a> .on('droppedPacket', callback)
Emitted when a packet (or packets) are dropped. Returns an array.
### <a name="event-error"></a> .on('error', callback)
Emitted when there is an on the serial port.
### <a name="event-impedance-array"></a> .on('impedanceArray', callback)
Emitted when there is a new impedanceArray available.
Emitted when there is a new impedanceArray available. Returns an array.
### <a name="event-query"></a> .on('query', callback)
+18
Ver Arquivo
@@ -1,3 +1,21 @@
# 1.3.2
### Enhancements
* Added master time offset `timeOffsetMaster` to `syncObj` which is a running average across sync attempts.
# 1.3.1
### Bug Fixes
* Fixed bug where `connected` and `streaming` were not set in constructor
# 1.3.0
### New Features
* Add dropped packet detection, new event `droppedPacket` can be added to get an array of dropped packet numbers in the case of the dropped packet event.
# 1.2.3
### Enhancements
+19
Ver Arquivo
@@ -0,0 +1,19 @@
{
"name": "timesync",
"version": "1.0.0",
"description": "Time sync example",
"main": "timeSync.js",
"scripts": {
"start": "node timeSync.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"time",
"sync"
],
"author": "AJ Keller",
"license": "MIT",
"dependencies": {
"openbci": "^1.3.1"
}
}
+93
Ver Arquivo
@@ -0,0 +1,93 @@
/**
* This is an example of time syncing every second for one minute. Used with a
* real board.
* To run:
* change directory to this file `cd examples/timeSync`
* do `npm install`
* then `npm start`
*/
var OpenBCIBoard = require('openbci').OpenBCIBoard;
var ourBoard = new OpenBCIBoard({});
const resyncPeriodMin = 5; // re sync every five minutes
const secondsInMinute = 60;
var sampleRate = 250; // Default to 250, ALWAYS verify with a call to `.sampleRate()` after 'ready' event!
var timeSyncPossible = false;
ourBoard.autoFindOpenBCIBoard().then(portName => {
if(portName) {
/**
* Connect to the board with portName
* i.e. ourBoard.connect(portName).....
*/
// Call to connect
ourBoard.connect(portName).then(() => {
console.log(`connected`);
})
.catch(err => {
console.log(`connect: ${err}`);
});
} else {
/**Unable to auto find OpenBCI board*/
}
});
var readyFunc = () => {
// Get the sample rate after 'ready'
sampleRate = ourBoard.sampleRate();
// 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.
timeSyncPossible = ourBoard.usingVersionTwoFirmware();
if (timeSyncPossible) {
ourBoard.streamStart()
.catch(err => {
console.log(`stream start: ${err}`);
});
} else {
killFunc();
}
}
var killFunc = () => {
ourBoard.disconnect()
.then(() => {
process.kill();
});
}
var sampleFunc = sample => {
// Resynchronize every every second
if (sample._count % (sampleRate * 1) === 0) {
ourBoard.syncClocksFull()
.then(syncObj => {
// Sync was successful
if (syncObj.valid) {
// Log the object to check it out!
console.log(`timeOffset`,syncObj.timeOffsetMaster);
} else {
// Retry it
console.log(`Was not able to sync... retry!`);
}
});
}
if (sample.timeStamp) { // true after the first successful sync
if (sample.timeStamp < 10 * 60 * 60 * 1000) { // Less than 10 hours
console.log(`Bad time sync ${sample.timeStamp}`);
}
}
// Stop after one minute
if (sample._count > sampleRate * 60) {
killFunc();
}
}
// Subscribe to your functions
ourBoard.on('ready',readyFunc);
ourBoard.on('sample',sampleFunc);
+12 -1
Ver Arquivo
@@ -145,6 +145,9 @@ function OpenBCIFactory() {
this.accelArray = [0,0,0]; // X, Y, Z
this.channelSettingsArray = k.channelSettingsArrayInit(k.numberOfChannelsForBoardType(this.options.boardType));
this.writeOutArray = new Array(100);
// Booleans
this.connected = false;
this.streaming = false;
// Buffers
this.buffer = null;
this.masterBuffer = masterBufferMaker();
@@ -185,9 +188,10 @@ function OpenBCIFactory() {
this.curParsingMode = k.OBCIParsingReset;
this.commandsToWrite = 0;
this.impedanceArray = openBCISample.impedanceArray(k.numberOfChannelsForBoardType(this.options.boardType));
this.writeOutDelay = k.OBCIWriteIntervalDelayMSShort;
this.previousSampleNumber = -1;
this.sampleCount = 0;
this.timeOfPacketArrival = 0;
this.writeOutDelay = k.OBCIWriteIntervalDelayMSShort;
// Strings
// NTP
@@ -1735,6 +1739,11 @@ function OpenBCIFactory() {
OpenBCIBoard.prototype._processQualifiedPacket = function(rawDataPacketBuffer) {
if (!rawDataPacketBuffer) return;
if (rawDataPacketBuffer.byteLength !== k.OBCIPacketSize) return;
var missedPacketArray = openBCISample.droppedPacketCheck(this.previousSampleNumber, rawDataPacketBuffer[k.OBCIPacketPositionSampleNumber]);
if (missedPacketArray) {
this.emit('droppedPacket', missedPacketArray);
}
this.previousSampleNumber = rawDataPacketBuffer[k.OBCIPacketPositionSampleNumber];
var packetType = openBCISample.getRawPacketType(rawDataPacketBuffer[k.OBCIPacketPositionStopByte]);
switch (packetType) {
case k.OBCIStreamPacketStandardAccel:
@@ -1924,6 +1933,8 @@ function OpenBCIFactory() {
this.sync.timeOffsetMaster = this.sync.curSyncObj.timeOffset;
}
this.sync.curSyncObj.timeOffsetMaster = this.sync.timeOffsetMaster;
if (this.options.verbose) {
console.log(`Master offset ${this.sync.timeOffsetMaster} ms`);
}
+5
Ver Arquivo
@@ -199,6 +199,9 @@ const OBCISimulatorLineNoiseNone = 'None';
const OBCISampleRate125 = 125;
const OBCISampleRate250 = 250;
/** Max sample number */
const OBCISampleNumberMax = 255;
/** Packet Size */
const OBCIPacketSize = 33;
@@ -729,6 +732,8 @@ module.exports = {
/** Possible Sample Rates */
OBCISampleRate125,
OBCISampleRate250,
/** Max sample number */
OBCISampleNumberMax,
/** Packet Size */
OBCIPacketSize,
/** Notable Bytes */
+37 -1
Ver Arquivo
@@ -522,7 +522,42 @@ var sampleModule = {
isTimeSyncSetConfirmationInBuffer,
makeTailByteFromPacketType,
isStopByte,
newSyncObject
newSyncObject,
/**
* @description Checks to make sure the previous sample number is one less
* then the new sample number. Takes into account sample numbers wrapping
* around at 255.
* @param `previousSampleNumber` {Number} - An integer number of the previous
* sample number.
* @param `newSampleNumber` {Number} - An integer number of the new sample
* number.
* @returns {Array} - Returns null if there is no dropped packets, otherwise,
* or on a missed packet, an array of their packet numbers is returned.
*/
droppedPacketCheck: (previousSampleNumber, newSampleNumber) => {
if (previousSampleNumber === k.OBCISampleNumberMax && newSampleNumber === 0) {
return null;
}
if (newSampleNumber - previousSampleNumber === 1) {
return null;
}
var missedPacketArray = [];
if (previousSampleNumber > newSampleNumber) {
var numMised = k.OBCISampleNumberMax - previousSampleNumber;
for (var i = 0; i < numMised; i++) {
missedPacketArray.push(previousSampleNumber + i + 1);
}
previousSampleNumber = -1;
}
for (var i = 1; i < (newSampleNumber - previousSampleNumber); i++) {
missedPacketArray.push(previousSampleNumber + i);
}
return missedPacketArray;
}
};
module.exports = sampleModule;
@@ -551,6 +586,7 @@ function newSyncObject() {
timeRoundTrip: 0,
timeTransmission: 0,
timeOffset: 0,
timeOffsetMaster: 0,
valid: false
}
}
+1 -1
Ver Arquivo
@@ -1,6 +1,6 @@
{
"name": "openbci",
"version": "1.2.3",
"version": "1.3.2",
"description": "The official Node.js SDK for the OpenBCI Biosensor Board.",
"main": "openBCIBoard",
"scripts": {
+5
Ver Arquivo
@@ -765,6 +765,11 @@ describe('OpenBCIConstants', function() {
assert.equal(250, k.OBCISampleRate250);
});
});
describe('Max sample number',function() {
it('should be 255',function () {
assert.equal(255, k.OBCISampleNumberMax);
});
});
describe("Radio Channel Limits", function() {
it("should get the right channel number max",function () {
expect(k.OBCIRadioChannelMax).to.be.equal(25);
+40 -3
Ver Arquivo
@@ -5,9 +5,9 @@ var assert = require('assert');
var openBCISample = require('../openBCISample');
var sinon = require('sinon');
var chai = require('chai'),
expect = chai.expect,
should = chai.should(),
expect = chai.expect;
expect = chai.expect,
assert = chai.assert;
var chaiAsPromised = require("chai-as-promised");
var sinonChai = require("sinon-chai");
@@ -1352,6 +1352,9 @@ describe('openBCISample',function() {
it("should have property timeOffset",function() {
expect(syncObj).to.have.property("timeOffset",0);
});
it("should have property timeOffsetMaster",function() {
expect(syncObj).to.have.property("timeOffsetMaster",0);
});
it("should have property timeRoundTrip",function() {
expect(syncObj).to.have.property("timeRoundTrip",0);
});
@@ -1373,7 +1376,41 @@ describe('openBCISample',function() {
it("should have property boardTime",function() {
expect(syncObj).to.have.property("boardTime",0);
});
})
});
describe('#droppedPacketCheck',function() {
it("should return an array of missed packet numbers",function() {
previous = 0;
current = previous + 2;
assert.sameMembers(openBCISample.droppedPacketCheck(previous, current), [1], "dropped one packet");
previous = 0;
current = previous + 4;
assert.sameMembers(openBCISample.droppedPacketCheck(previous, current), [1,2,3], "dropped three packets");
previous = 255;
current = 2;
assert.sameMembers(openBCISample.droppedPacketCheck(previous, current), [0,1], "dropped two packets on wrap edge!");
previous = 254;
current = 2;
assert.sameMembers(openBCISample.droppedPacketCheck(previous, current), [255,0,1], "dropped three packets on wrap!");
previous = 250;
current = 1;
assert.sameMembers(openBCISample.droppedPacketCheck(previous, current), [251,252,253,254,255,0], "dropped a bunch of packets on wrap!");
});
it("should roll over when 255 was previous and current is 0", function() {
previous = 255;
current = 0;
expect(openBCISample.droppedPacketCheck(previous, current)).to.be.null;
});
it("should return null when previous is one less then new sample number", function() {
previous = 0;
current = previous + 1;
expect(openBCISample.droppedPacketCheck(previous, current)).to.be.null;
});
});
});
+35
Ver Arquivo
@@ -82,6 +82,8 @@ describe('openbci-sdk',function() {
expect(board.options.verbose).to.be.false;
expect(board.sampleRate()).to.equal(250);
expect(board.numberOfChannels()).to.equal(8);
expect(board.connected).to.be.false;
expect(board.streaming).to.be.false;
});
it('should be able to set ganglion mode', () => {
var board = new openBCIBoard.OpenBCIBoard({
@@ -1010,6 +1012,39 @@ describe('openbci-sdk',function() {
funcSpyTimeSyncedAccel.should.not.have.been.called;
funcSpyTimeSyncedRawAux.should.not.have.been.called;
});
it("should emit a dropped packet on dropped packet",function(done) {
// Set to default state
ourBoard.previousSampleNumber = -1;
var sampleNumber0 = openBCISample.samplePacket(0);
ourBoard.once('droppedPacket',() => {
done();
});
var sampleNumber2 = openBCISample.samplePacket(2);
// Call the function under test
ourBoard._processDataBuffer(sampleNumber0);
ourBoard._processDataBuffer(sampleNumber2);
});
it("should emit a dropped packet on dropped packet with edge",function(done) {
// Set to default state
var count = 0;
ourBoard.previousSampleNumber = 253;
var buf1 = openBCISample.samplePacket(254);
var countFunc = arr => {
count++;
};
ourBoard.on('droppedPacket', countFunc);
var buf2 = openBCISample.samplePacket(0);
var buf3 = openBCISample.samplePacket(1);
// Call the function under test
ourBoard._processDataBuffer(buf1);
ourBoard._processDataBuffer(buf2);
ourBoard._processDataBuffer(buf3);
setTimeout(() => {
ourBoard.removeListener('droppedPacket', countFunc);
expect(count).to.equal(1);
done();
}, 10)
});
});
describe("#_processPacketTimeSyncSet", function() {