Comparar commits

..

49 Commits

Autor SHA1 Mensagem Data
AJ Keller b24d682e49 Bump version to match npm 2016-06-24 14:41:45 -04:00
Andy Heusser b36a7b3409 Merge pull request #67 from pushtheworldllc/dev
Fix constructor in readme.md Closes #66
2016-06-24 14:37:37 -04:00
AJ Keller cfa8f4bdc8 Fix constructor in readme.md Closes #66 2016-06-24 14:31:04 -04:00
AJ Keller ee66c49ff0 Update changelog.md
Forgot to update change log on merge
2016-06-21 21:21:14 -04:00
Andy Heusser abfb37a38e Merge pull request #65 from pushtheworldllc/dev-serialport-upgrade
Dev serialport upgrade
2016-06-21 17:14:40 -04:00
AJ Keller 48be1a01db ENH add more travis versions 2016-06-21 07:36:50 -04:00
AJ Keller 8bbc011ead ENH bump serialport to 3.0.0 2016-06-21 07:33:38 -04:00
Teon L Brooks cc839b3b32 Merge pull request #62 from pushtheworldllc/dev-sim-accel
Add accel data to simulator closes #61
2016-06-10 00:04:22 -04:00
AJ Keller e2cd5d1dd7 Add accel data to simulator closes #61 2016-06-09 21:44:22 -04:00
AJ Keller 682074dff7 Update README.md 2016-06-02 22:34:45 -04:00
AJ Keller 0af11eabe5 Update README.md 2016-06-02 22:34:05 -04:00
AJ Keller 31eea736dd Merge pull request #60 from pushtheworldllc/dev
ADD SD card support functions
2016-06-02 22:16:31 -04:00
AJ Keller 8cafa3f7ff ADD SD Card Support Functions 2016-06-02 22:12:17 -04:00
AJ Keller 971787720e Merge pull request #57 from pushtheworldllc/bug-ci-fail
Removed auto test dependent on ntp with travis fail
2016-05-01 12:31:22 -04:00
AJ Keller c267cca924 Removed auto test dependent on ntp with travis fail 2016-05-01 12:28:01 -04:00
AJ Keller 3a90e5ab96 Auto test for hot patch 2016-05-01 11:14:20 -04:00
AJ Keller a18e17c7ca Hot patch for line noise 2016-05-01 11:13:17 -04:00
AJ Keller 5da869f059 Merge pull request #56 from pushtheworldllc/enh-bio-like-sim
ENH simulator looking more brain like
2016-05-01 11:07:00 -04:00
AJ Keller e32ec71c47 TGFAT 2016-04-30 19:57:48 -04:00
AJ Keller 5ee44ae531 TGFAT 2016-04-30 19:44:42 -04:00
AJ Keller b7f8c135b9 ENH simulator looking better 2016-04-30 19:31:11 -04:00
Teon L Brooks 8aa69a18dd Merge pull request #48 from pushtheworldllc/bug-rawDataPacket
FIX rawDataPacket not being emitted
2016-04-26 00:31:48 -04:00
AJ Keller 617e111812 FIX rawDataPacket not being emitted 2016-04-25 10:42:10 -04:00
Teon L Brooks d8c74d5b30 Merge pull request #45 from pushtheworldllc/ntp
ENH/ADD sntp time sync methods
2016-04-24 14:55:22 -04:00
AJ Keller aaf9f08c24 ENH/ADD sntp time sync methods 2016-04-24 11:39:36 -04:00
Teon L Brooks 37c622fdf4 Merge pull request #42 from pushtheworldllc/dev
0.3.1
2016-04-13 10:19:34 -04:00
AJ Keller 1d9927a7d7 0.3.1 2016-04-13 07:16:37 -04:00
Andy Heusser 1eda6df549 Update package.json 2016-04-12 21:13:45 -04:00
Teon L Brooks 88f973d349 Merge pull request #32 from pushtheworldllc/dev
0.3.0 - ENH Continuous Impedance Testing ADD NTP features
2016-04-10 20:39:04 -04:00
AJ Keller afcf1de6e2 ENH README.md and minor changes for PR
ENH fixes for PR

ENH README.md and changelog.md

ENH README.md

ENH README.md updates

updates

Added comments in ._impedanceTestSetChannel()

Fixins
2016-04-10 20:34:20 -04:00
AJ Keller caeeaf7dbd ENH addressing PR #32 comments 2016-04-09 19:43:48 -04:00
AJ Keller 6474391d1e Fixes #33 and #34 2016-04-09 14:34:54 -04:00
AJ Keller 169422e77e Small updates... 2016-04-03 19:10:47 -04:00
AJ Keller 21438f5a37 ENH Readme, ChangeLog, removed outdated funtions. 2016-04-03 17:55:24 -04:00
AJ Keller 2f5a31e481 ENH single channel impedance tests now use Goertzel algorithm. 2016-04-03 17:09:57 -04:00
AJ Keller 04f6fbae14 ENH Buffer 2016-04-02 20:50:49 -04:00
AJ Keller 8a22f3f11b Impedance testing up and running in continuous mode 2016-03-30 17:34:26 -04:00
AJ Keller 3ab00cfaf5 Working on goertzel... 2016-03-30 14:01:05 -04:00
AJ Keller 29b713b7f1 ADD sntp to the dependencies list.s 2016-03-28 16:31:27 -04:00
AJ Keller 74339c126b ENH overhauled raw packet to sample. 2016-03-28 16:27:48 -04:00
AJ Keller 465b3cc3a5 Saving, working on new packet to sample conversion implementation. 2016-03-24 08:12:25 -04:00
AJ Keller 920eab9ed1 Fixing issues and such... 2016-03-23 20:16:24 -04:00
AJ Keller 3a6d87ca90 ADD Sntp module for Sntp support, ADD Sntp functions. 2016-03-23 08:08:16 -04:00
AJ Keller 6a0e1c609f saving... 2016-02-27 17:38:29 -05:00
AJ Keller cd7d388e69 ENH Impedance Testing 2016-02-20 17:34:20 -05:00
AJ Keller 0ef2add033 Starting time sync stuff 2016-02-09 11:37:07 -05:00
Teon L Brooks 8f24aa55b7 Merge pull request #24 from pushtheworldllc/dev
ADD Auto test
2016-02-08 01:34:10 -05:00
AJ Keller 92242c87b2 ADD Automatic Impedance Testing, ADD Code Coverage, ENH Simulator 2016-02-07 13:06:46 -05:00
Teon L Brooks 655513e757 Update README.md 2016-02-01 17:53:06 -05:00
17 arquivos alterados com 4286 adições e 1551 exclusões
+4
Ver Arquivo
@@ -36,3 +36,7 @@ myOutput.txt
# Local npm builds for testing end in .tgz
*.tgz
# For git
*.orig
+10
Ver Arquivo
@@ -1,3 +1,13 @@
language: node_js
node_js:
- "4.0"
- "4.1"
- "4.2"
- "5.11.0"
- "6.0"
- "6.1"
- "6.2"
install:
- npm install --all
script:
- npm run test-cov
+500 -131
Ver Arquivo
@@ -1,12 +1,59 @@
[![Stories in Ready](https://badge.waffle.io/OpenBCI/openbci-js-sdk.png?label=ready&title=Ready)](https://waffle.io/OpenBCI/openbci-js-sdk)
[![Join the chat at https://gitter.im/OpenBCI/openbci-js-sdk](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/OpenBCI/openbci-js-sdk?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Build Status](https://travis-ci.org/OpenBCI/openbci-js-sdk.svg?branch=master)](https://travis-ci.org/OpenBCI/openbci-js-sdk)
[![codecov.io](https://codecov.io/github/OpenBCI/openbci-js-sdk/coverage.svg?branch=master)](https://codecov.io/github/OpenBCI/openbci-js-sdk?branch=master)
# openbci-sdk
# OpenBCI Node.js SDK
An NPM module for OpenBCI ~ written with love by [Push The World!](www.pushtheworldllc.com)
A Node.js module for OpenBCI ~ written with love by [Push The World!](http://www.pushtheworldllc.com)
## Working with the Module
We are proud to support all functionality of the OpenBCI 8 Channel board (16 channel coming soon) and are actively developing and maintaining this module.
The purpose of this module is to **get connected** and **start streaming** as fast as possible.
## TL;DR
#### Install via npm:
```
npm install openbci-sdk
```
#### Get connected and start streaming
```js
var OpenBCIBoard = require('openbci-sdk').OpenBCIBoard;
var ourBoard = new OpenBCIBoard();
ourBoard.connect(portName)
.then(function() {
ourBoard.on('ready',function() {
ourBoard.streamStart();
ourBoard.on('sample',function(sample) {
/** Work with sample */
for (var i = 0; i < ourBoard.numberOfChannels(); i++) {
console.log("Channel " + (i + 1) + ": " + sample.channelData[i].toFixed(8) + " Volts.");
// prints to the console
// "Channel 1: 0.00001987 Volts."
// "Channel 2: 0.00002255 Volts."
// ...
// "Channel 8: -0.00001875 Volts."
}
});
});
})
```
Want to know if the module really works? Check out some projects and organizations using it:
* [_OpenEXP_](https://github.com/openexp/OpenEXP): an open-source desktop app for running experiments and collecting behavioral and physiological data.
* [_Thinker_](http://www.pushtheworldllc.com/#!thinker/uc1fn): a project building the world's first brainwave-word database.
* [_NeuroJS_](https://github.com/NeuroJS): a community dedicated to Neuroscience research using JavaScript, they have several great examples.
Still not satisfied it works?? Check out this [detailed report](http://s132342840.onlinehome.us/pushtheworld/files/voltageVerificationTestPlanAndResults.pdf) that scientifically validates the output voltages of this module.
How are you still doubting and not using this already? Fine, go look at some of the [400 **_automatic_** tests](https://codecov.io/github/OpenBCI/openbci-js-sdk?branch=master) written for it!
## General Overview
Initialization
--------------
@@ -21,11 +68,37 @@ var ourBoard = new OpenBCIBoard.OpenBCIBoard();
For initializing with options, such as verbose print outs:
```js
var ourBoard = require('openbci-sdk').OpenBCIBoard({
var OpenBCIBoard = require('openbci-sdk').OpenBCIBoard;
var ourBoard = new OpenBCIBoard({
verbose: true
});
```
Or if you don't have a board and want to use synthetic data:
```js
var OpenBCIBoard = require('openbci-sdk').OpenBCIBoard;
var ourBoard = new OpenBCIBoard({
simulate: true
});
```
Another useful way to start the simulator:
```js
var openBCIBoard = require('openbci-sdk');
var k = openBCIBoard.OpenBCIConstants;
var ourBoard = openBCIBoard.OpenBCIBoard();
ourBoard.connect(k.OBCISimulatorPortName) // This will set `simulate` to true
.then(function(boardSerial) {
ourBoard.on('ready',function() {
/** Start streaming, reading registers, what ever your heart desires */
});
}).catch(function(err) {
/** Handle connection errors */
});
```
'ready' event
------------
@@ -33,7 +106,8 @@ You MUST wait for the 'ready' event to be emitted before streaming/talking with
so installing the 'sample' listener and writing before the ready event might result in... nothing at all.
```js
var ourBoard = new require('openbci-sdk').OpenBCIBoard();
var OpenBCIBoard = require('openbci-sdk').OpenBCIBoard;
var ourBoard = new OpenBCIBoard();
ourBoard.connect(portName).then(function(boardSerial) {
ourBoard.on('ready',function() {
/** Start streaming, reading registers, what ever your heart desires */
@@ -47,12 +121,14 @@ Sample properties:
------------------
* `startByte` (`Number` should be `0xA0`)
* `sampleNumber` (a `Number` between 0-255)
* `channelData` (channel data indexed starting at 1 [1,2,3,4,5,6,7,8] filled with floating point `Numbers`)
* `channelData` (channel data indexed at 0 filled with floating point `Numbers` in Volts)
* `auxData` (aux data indexed starting at 0 [0,1,2] filled with floating point `Numbers`)
* `stopByte` (`Number` should be `0xC0`)
The power of this module is in using the sample emitter, to be provided with samples to do with as you wish.
You can also start the simulator by sending `.connect(portName)` with `portName` equal to `'OpenBCISimulator'`.
To get a 'sample' event, you need to:
-------------------------------------
1. Call `.connect(serialPortName)`
@@ -60,8 +136,9 @@ To get a 'sample' event, you need to:
3. In callback for 'ready' emitter, call `streamStart()`
4. Install the 'sample' event emitter
```js
var ourBoard = new require('openbci-sdk').OpenBCIBoard();
ourBoard.connect(portName).then(function(boardSerial) {
var OpenBCIBoard = require('openbci-sdk').OpenBCIBoard;
var ourBoard = new OpenBCIBoard();
ourBoard.connect(portName).then(function() {
ourBoard.on('ready',function() {
ourBoard.streamStart();
ourBoard.on('sample',function(sample) {
@@ -78,103 +155,125 @@ var ourBoard = new require('openbci-sdk').OpenBCIBoard();
ourBoard.streamStop().then(ourBoard.disconnect());
```
To start the simulator test samples:
----------
1. Call `.simulatorStart()`
2. In callback for 'ready' emitter, call `streamStart()`
3. Install the 'sample' event emitter
```js
var ourBoard = new require('openbci-sdk').OpenBCIBoard();
ourBoard.simulatorStart().then(function() {
ourBoard.on('sample',function(sample) {
/** Work with sample */
});
}).catch(function(err) {
console.log('Error [simulator]: ' + err);
});
```
To stop the simulator:
```js
ourBoard.simulatorStop()
```
Impedance (signal quality)
--------------------------
Measuring impedance is a vital tool in ensuring great data is collected.
* **Good** impedance is < 5k Ohms
* **Ok** impedance is 5 to 10k Ohms
* **Bad** impedance is > 10k Ohms
To test for impedance we must apply a known test signal to which either the P, N, or both channels and then apply a little math.
When you start measuring for electrode impedance's, a new additional property called `impedanceArray` (indexed 1,2,3,4,5,6,7,8), can be found on the sample object from the emitted by 'sample'.
**Note: You must be streaming in order to measure impedance's. Takes up to 2 seconds to start measuring impedances.**
To start measuring all channels impedance's:
```js
var ourBoard = new require('openbci-sdk').OpenBCIBoard();
ourBoard.connect(portName).then(function(boardSerial) {
ourBoard.on('ready',function() {
ourBoard.impedanceTestStartAll().then(() => {
ourBoard.streamStart();
});
ourBoard.on('sample',function(sample) {
/** Do normal Sample actions */
if(sample.impedanceArray) {
/** Work with impedance array */
}
});
});
}).catch(function(err) {
/** Handle connection errors */
});
```
To stop measuring impedance's:
```js
ourBoard.impedanceTestStopAll();
```
To apply the test signal to a specific input of a specific channel (i.e. Channel 2 test signal applied to P input and not N input):
```js
ourBoard.impedanceTestStartChannel(2,true,false);
```
To stop applying the test signal to a specific channel (i.e. Stop applying signal to channel 2):
```js
ourBoard.impedanceTestStopChannel(2);
```
To stop calculating impedance's every time there is a new sample:
```js
ourBoard.impedanceTestCalculatingStop();
```
You would call `.impedanceTestCalculatingStop()` if you were measuring the impedance for a specified channel. You do NOT need to call `.impedanceTestCalculatingStop()` after calling `.impedanceTestStopAll()` because it is called for you.
Auto-finding boards
-------------------
You must have the OpenBCI board connected to the PC before trying to automatically find it.
If a port is not automatically found, then a list of ports will be returned, this would be a
good place to present a drop down picker list to the user, so they may manually select the
serial port name.
If a port is not automatically found, then call `.listPorts()` to get a list of all serial ports this would be a good place to present a drop down picker list to the user, so they may manually select the serial port name.
```js
var ourBoard = new require('openbci-sdk').OpenBCIBoard();
ourBoard.autoFindOpenBCIBoard().then((value) => {
if(Array.isArray(value)) {
/**Unable to auto find OpenBCI board*/
} else {
var OpenBCIBoard = require('openbci-sdk').OpenBCIBoard;
var ourBoard = new OpenBCIBoard();
ourBoard.autoFindOpenBCIBoard().then(portName => {
if(portName) {
/**
* Connect to the board with portName
* i.e. ourBoard.connect(portName).....
*/
} else {
/**Unable to auto find OpenBCI board*/
}
});
```
Note: `.autoFindOpenBCIBoard()` will return the name of the Simulator if you instantiate with option `simulate: true`.
Auto Test - (Using impedance to determine signal quality)
---------------------------------------------------------
Measuring impedance is a vital tool in ensuring great data is collected.
**_IMPORTANT!_** Measuring impedance takes time, so *only test what you must*
Your OpenBCI board will have electrodes hooked up to either a P input, N input or in some cases both inputs.
To test specific inputs of channels:
1. Connect to board.
2. Start streaming.
3. Install the 'impedanceArray' emitter
4. Call `.impedanceTestChannels()` with your configuration array
A configuration array looks like, for an 8 channel board, `['-','N','n','p','P','-','b','b']`
Where there are the same number of elements as channels and each element can be either:
* `p` or `P` (only test P input)
* `n` or `N` (only test N input)
* `b` or `B` (test both inputs) (takes 66% longer to run then previous two `p` or `n`)
* `-` (ignore channel)
Without further ado, here is an example:
```js
var OpenBCIBoard = require('openbci-sdk').OpenBCIBoard;
var ourBoard = new OpenBCIBoard();
ourBoard.connect(portName).then(function(boardSerial) {
ourBoard.on('ready',function() {
ourBoard.streamStart();
ourBoard.once('impedanceArray', impedanceArray => {
/** Work with impedance Array */
});
ourBoard.impedanceTestChannels(['n','N','n','p','P','p','b','B']).catch(err => console.log(err));
});
}).catch(function(err) {
/** Handle connection errors */
});
```
But wait! What is this `impedanceArray`? An Array of Objects, for each object:
```js
[{
channel: 1,
P: {
raw: -1,
text: 'init'
},
N: {
raw: -1,
text: 'init'
}
},
{
// Continues for each channel up to the amount of channels on board (8 or 16)
},...];
```
Where:
* *channel* is the channel number (`impedanceArray[0]` is channel 1, `impedanceArray[6]` is channel 7)
* *P* is the P input data (Note: P is capitalized)
* *raw* is an impedance value resulting from the Goertzel algorithm.
* *text* is a text interpretation of the `average`
* **Good** impedance is < 5k Ohms
* **Ok** impedance is 5 to 10k Ohms
* **Bad** impedance is > 10k Ohms
* **None** impedance is > 1M Ohms
* *N* is the N input data (Note: N is capitalized) (see above for what N object consists of)
To run an impedance test on all inputs, one channel at a time:
1. Connect to board
2. Start streaming
3. Install the 'impedanceObject'
4. Call `.impedanceTestAllChannels()`
**Note: Takes up to 5 seconds to start measuring impedances. There is an unknown number of samples taken. Not always 60!**
For example:
```js
var OpenBCIBoard = require('openbci-sdk').OpenBCIBoard;
var ourBoard = new OpenBCIBoard();
ourBoard.connect(portName).then(function(boardSerial) {
ourBoard.streamStart();
ourBoard.on('impedanceArray', impedanceArray => {
/** Work with impedance */
});
ourBoard.impedanceTestAllChannels();
}
```
See Reference Guide for a complete list of impedance tests.
Reference Guide
---------------
## Methods
@@ -187,9 +286,22 @@ Create new instance of an OpenBCI board.
Board optional configurations.
* `boardType` Specifies type of OpenBCI board (3 possible boards)
* `default` - 8 Channel OpenBCI board (Default)
* `daisy` - 8 Channel board with Daisy Module
(NOTE: THIS IS IN-OP AT THIS TIME DUE TO NO ACCESS TO ACCESSORY BOARD)
* `ganglion` - 4 Channel board
(NOTE: THIS IS IN-OP TIL RELEASE OF GANGLION BOARD 07/2016)
* `baudRate` Baud Rate, defaults to 115200. Manipulating this is allowed if firmware on board has been previously configured.
* `daisy` Daisy chain board is connected to the OpenBCI board. (NOTE: THIS IS IN-OP AT THIS TIME DUE TO NO ACCESS TO ACCESSORY BOARD)
* `verbose` To output more messages to the command line.
* `simulate` Full functionality, just synthetic data.
* `simulatorSampleRate` - The sample rate to use for the simulator (Default is `250`)
* `simulatorAlpha` - {Boolean} - Inject and 10Hz alpha wave in Channels 1 and 2 (Default `true`)
* `simulatorLineNoise` - Injects line noise on channels.
* `60Hz` - 60Hz line noise (Default) (ex. __United States__)
* `50Hz` - 50Hz line noise (ex. __Europe__)
* `None` - Do not inject line noise.
* `sntp` - 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. NOTE: (NOT FULLY IMPLEMENTED) [DO NOT USE]
**Note, we have added support for either all lowercase OR camelcase of the options, use whichever style you prefer.**
@@ -199,7 +311,7 @@ Automatically find an OpenBCI board.
**Note: This will always return an Array of `COM` ports on Windows**
**_Returns_** a promise, fulfilled with a `portName` such as `/dev/tty.*` on Mac/Linux or an 'Array' of all the serial ports.
**_Returns_** a promise, fulfilled with a `portName` such as `/dev/tty.*` on Mac/Linux or `OpenBCISimulator` if `this.options.simulate === true`.
### .channelOff(channelNumber)
@@ -269,7 +381,7 @@ The essential precursor method to be called initially to establish a serial conn
The system path of the OpenBCI board serial port to open. For example, `/dev/tty` on Mac/Linux or `COM1` on Windows.
**_Returns_** a promise, fulfilled by a successful serial connection to the board, containing the serial port object that was opened. The promise will be rejected at any time if the serial port has an 'error' or 'close' event emitted.
**_Returns_** a promise, fulfilled by a successful serial connection to the board, the promise will be rejected at any time if the serial port has an 'error' or 'close' event emitted.
### .debugSession()
@@ -293,49 +405,176 @@ A number specifying which channel you want to get data on. Only 1-8 at this time
**_Returns_** a promise, fulfilled if the command was sent to the board and the `.processBytes()` function is ready to reach for the specified channel.
### .impedanceTestStartAll()
### .impedanceTestAllChannels()
To apply test signals to all the channels and all inputs on an OpenBCI board.
To apply test signals to the channels on the OpenBCI board used to test for impedance. This can take a little while to actually run (<8 seconds)!
**Note, you must be connected in order to set the test commands. Also this method can take up to 2 seconds to send all commands!**
Don't forget to install the `impedanceArray` emitter to receive the impendances!
**_Returns_** a promise, fulfilled once all the commands are sent to the board.
**Note, you must be connected in order to set the test commands. Also this method can take up to 5 seconds to send all commands!**
### .impedanceTestStopAll()
**_Returns_** a promise upon completion of test.
To stop applying test signals to all the channels and inputs on an OpenBCI board.
### .impedanceTestChannels(arrayOfCommands)
**Note, you must be connected in order to set the test commands. Also this method can take up to 2 seconds to send all commands!**
**_arrayOfCommands_**
**_Returns_** a promise, fulfilled once all the commands are sent to the board.
The array of configurations where there are the same number of elements as channels and each element can be either:
* `p` or `P` (only test P input)
* `n` or `N` (only test N input)
* `b` or `B` (test both inputs) (takes 66% longer to run then previous two `p` or `n`)
* `-` (ignore channel)
### .impedanceTestStartChannel(channelNumber,pInput,nInput)
Don't forget to install the `impedanceArray` emitter to receive the impendances!
To apply the impedance test signal to an input for any given channel.
**Note, you must be connected in order to set the test commands. Also this method can take up to 5 seconds to send all commands!**
**_channelNumber_**
**_Returns_** a promise upon completion of test.
A number specifying which channel you want to get apply the test signal to. Only 1-8 at this time.
### .impedanceTestChannel(channelNumber)
**_pInput_**
Run a complete impedance test on a single channel, applying the test signal individually to P & N inputs.
A bool true if you want to apply the test signal to the P input, false to not apply the test signal.
**_channelNumber_**
**_nInput_**
A Number, specifies which channel you want to test.
A bool true if you want to apply the test signal to the N input, false to not apply the test signal.
**_Returns_** a promise that resolves a single channel impedance object.
### .impedanceTestCalculatingStart()
Example:
```js
var OpenBCIBoard = require('openbci-sdk').OpenBCIBoard;
var ourBoard = new OpenBCIBoard();
ourBoard.connect(portName).then(function(boardSerial) {
ourBoard.on('ready',function() {
ourBoard.streamStart();
ourBoard.impedanceTestChannel(1)
.then(impedanceObject => {
/** Do something with impedanceObject! */
})
.catch(err => console.log(err));
});
}).catch(function(err) {
/** Handle connection errors */
});
```
Where an impedance for this method call would look like:
```js
{
channel: 1,
P: {
raw: 2394.45,
text: 'good'
},
N: {
raw: 7694.45,
text: 'ok'
}
}
```
To start calculating impedance's every time there is a new sample.
### .impedanceTestChannelInputP(channelNumber)
**Note, this is automatically called by `.impedanceTestStartAll()` and `.impedanceTestStartChannel()`**
Run impedance test on a single channel, applying the test signal only to P input.
### .impedanceTestCalculatingStop()
**_channelNumber_**
To stop calculating impedance's every time there is a new sample.
A Number, specifies which channel you want to test.
**Note, this is automatically called by `.impedanceTestStopAll()`**
**_Returns_** a promise that resolves a single channel impedance object.
Example:
```js
var OpenBCIBoard = require('openbci-sdk').OpenBCIBoard;
var ourBoard = new OpenBCIBoard();
ourBoard.connect(portName).then(function(boardSerial) {
ourBoard.on('ready',function() {
ourBoard.streamStart();
ourBoard.impedanceTestChannelInputP(1)
.then(impedanceObject => {
/** Do something with impedanceObject! */
})
.catch(err => console.log(err));
});
}).catch(function(err) {
/** Handle connection errors */
});
```
Where an impedance for this method call would look like:
```js
{
channel: 1,
P: {
raw: 2394.45,
text: 'good'
},
N: {
raw: -1,
text: 'init'
}
}
```
### .impedanceTestChannelInputN(channelNumber)
Run impedance test on a single channel, applying the test signal only to N input.
**_channelNumber_**
A Number, specifies which channel you want to test.
**_Returns_** a promise that resolves a single channel impedance object.
Example:
```js
var OpenBCIBoard = require('openbci-sdk').OpenBCIBoard;
var ourBoard = new OpenBCIBoard();
ourBoard.connect(portName).then(function(boardSerial) {
ourBoard.on('ready',function() {
ourBoard.streamStart();
ourBoard.impedanceTestChannelInputN(1)
.then(impedanceObject => {
/** Do something with impedanceObject! */
})
.catch(err => console.log(err));
});
}).catch(function(err) {
/** Handle connection errors */
});
```
Where an impedance for this method call would look like:
```js
{
channel: 1,
P: {
raw: -1,
text: 'init'
},
N: {
raw: 7694.45,
text: 'ok'
}
}
```
### .impedanceTestContinuousStart()
Sends command to turn on impedances for all channels and continuously calculate their impedances.
**_Returns_** a promise, that fulfills when all the commands are sent to the internal write buffer
### .impedanceTestContinuousStop()
Sends command to turn off impedances for all channels and stop continuously calculate their impedances.
**_Returns_** a promise, that fulfills when all the commands are sent to the internal write buffer
### .listPorts()
List available ports so the user can choose a device when not automatically found.
**_Returns_** a promise, fulfilled with a list of available serial ports.
### .numberOfChannels()
@@ -371,21 +610,81 @@ Get the current sample rate.
**_Returns_** a number, the current sample rate.
### .simulatorStart()
### .sdStart(recordingDuration)
To start simulating an OpenBCI board.
Start logging to the SD card. If you are not streaming when you send this command, then you should expect to get a success or failure message followed by and end of transmission `$$$`.
**_recordingDuration_**
The duration you want to log SD information for. Opens a new SD file to write into. Limited to:
* `14sec` - 14 seconds
* `5min` - 5 minutes
* `15min` - 15 minutes
* `30min` - 30 minutes
* `1hour` - 1 hour
* `2hour` - 2 hour
* `4hour` - 4 hour
* `12hour` - 12 hour
* `24hour` - 24 hour
**Note: You must have the proper type of SD card inserted into the board for logging to work.**
**_Returns_** resolves if the command was added to the write queue.
### .sdStop()
Stop logging to the SD card and close any open file. If you are not streaming when you send this command, then you should expect to get a success or failure message followed by and end of transmission `$$$`. The success message contains a lot of useful information about what happened when writing to the SD card.
**_Returns_** resolves if the command was added to the write queue.
### .simulatorEnable()
To enter simulate mode. Must call `.connect()` after.
**Note, must be called after the constructor.**
**_Returns_** a promise, if the simulator was able to start.
**_Returns_** a promise, fulfilled if able to enter simulate mode, reject if not.
### .simulatorStop()
### .simulatorDisable()
To stop simulating an OpenBCI board.
To leave simulate mode.
**Note, must be called after the constructor.**
**_Returns_** a promise, if the simulator was able to stop.
**_Returns_** a promise, fulfilled if able to stop simulate mode, reject if not.
### .sntp
Extends the popular STNP package on [npmjs](https://www.npmjs.com/package/sntp)
### .sntpGetOffset()
Stateful method for querying the current offset only when the last one is too old. (defaults to daily)
**_Returns_** a promise with the time offset
### .sntpGetServerTime()
Get time from the SNTP server. Must have internet connection!
**_Returns_** a promise fulfilled with time object
### .sntpNow()
This function gets SNTP time since Jan 1, 1970, if we call this after a successful `.sntpStart()` this time will be sycned, or else we will just get the current computer time, the case if there is no internet.
**_Returns_** time since UNIX epoch in ms.
### .sntpStart()
This starts the SNTP server and gets it to remain in sync with the SNTP server;
**_Returns_** a promise if the module was able to sync with ntp server.
### .sntpStop()
Stops the SNTP from updating
### .softReset()
@@ -401,7 +700,7 @@ Sends a start streaming command to the board.
**Note, You must have called and fulfilled `.connect()` AND observed a `'ready'` emitter before calling this method.**
**_Returns_** a promise, fulfilled if the command was sent to the write queue.
**_Returns_** a promise, fulfilled if the command was sent to the write queue, rejected if unable.
### .streamStop()
@@ -409,9 +708,27 @@ Sends a stop streaming command to the board.
**Note, You must have called and fulfilled `.connect()` AND observed a `'ready'` emitter before calling this method.**
**_Returns_** a promise, fulfilled if the command was sent to the write queue.
**_Returns_** a promise, fulfilled if the command was sent to the write queue, rejected if unable.
### .write(data)
### .testSignal(signal)
Apply the internal test signal to all channels.
**_signal_**
A String indicating which test signal to apply
* `dc` - Connect to DC signal
* `ground` - Connect to internal GND (VDD - VSS)
* `pulse1xFast` - Connect to test signal 1x Amplitude, fast pulse
* `pulse1xSlow` - Connect to test signal 1x Amplitude, slow pulse
* `pulse2xFast` - Connect to test signal 2x Amplitude, fast pulse
* `pulse2xFast` - Connect to test signal 2x Amplitude, slow pulse
* `none` - Reset to default
**_Returns_** a promise, if the commands were sent to write buffer.
### .write(dataToWrite)
Send commands to the board. Due to the OpenBCI board firmware, a 10ms spacing **must** be observed between every command sent to the board. This method handles the timing and spacing between characters by adding characters to a global write queue and pulling from it every 10ms.
@@ -419,7 +736,7 @@ Send commands to the board. Due to the OpenBCI board firmware, a 10ms spacing **
Either a single character or an Array of characters
**_Returns_** a promise, fulfilled if the board has been connected and `dataToWrite` has been added to the write queue
**_Returns_** a promise, fulfilled if the board has been connected and `dataToWrite` has been added to the write queue, rejected if there were any problems.
**Example**
@@ -445,27 +762,79 @@ ourBoard.write('o');
## Events
### .on('close', callback)
Emitted when the serial connection to the board is closed.
### .on('error', callback)
Emitted when there is an on the serial port.
### .on('impedanceArray', callback)
Emitted when there is a new impedanceArray available.
### .on('query', callback)
Emitted resulting in a call to `.getChannelSettings()` with the channelSettingsObject
### .on('rawDataPacket', callback)
Emitted when there is a new raw data packet available.
### .on('ready', callback)
Emitted when the board is in a ready to start streaming state.
### .on('sample', callback)
Emitted when there is a new sample available.
Emitted when there is a new sample available.
## Properties
### connected
A bool, true if connected to an OpenBCI board, false if not.
### streaming
A bool, true if streaming data from an OpenBCI board, false if not.
## Useful Constants
To use the constants file simply:
```js
var openBCIBoard = require('openbci-sdk');
var k = openBCIBoard.OpenBCIConstants;
console.log(k.OBCISimulatorPortName); // prints OpenBCISimulator to the console.
```
### .OBCISimulatorPortName
The name of the simulator port.
## Dev Notes
Running
-------
1. Plug usb module into serial port
1. Turn OpenBCI device on
1. Type `npm start` into the terminal in the project directory
1. `npm install -D`
2. Plug usb module into serial port
3. Turn OpenBCI device on
4. Type `npm start` into the terminal in the project directory
Testing
-------
```
npm test
```
```
## Contribute to the library
1. Fork it!
2. Create your feature branch: `git checkout -b my-new-feature`
3. Make changes and ensure tests all pass. (`npm test`)
4. Commit your changes: `git commit -m 'Add some feature'`
5. Push to the branch: `git push origin my-new-feature`
6. Submit a pull request :D
## License
MIT
+95
Ver Arquivo
@@ -0,0 +1,95 @@
# 0.3.8
### Bug Fixes
* Fixed readme.md
# 0.3.7
### New Features
* Upgrade dependencies
* Update Travis
# 0.3.6
### New Features
* Simulator now has accelerometer data
# 0.3.5
### New Features
* SD card support! Now logging to an SD card is easier than ever.
### Bug Fixes
* Sample rate does not return correct sample rate for custom rate on simulator. #58
# 0.3.4
### New Features
* Simulator made to look more like brainwave data to the user. Implemented a 1/f filter. Defaults to injecting 60Hz line noise with two channels of alpha (10Hz) boost.
### Github Issues Addressed
* [https://github.com/OpenBCI/openbci-js-sdk/issues/44](#44)
# 0.3.3
### Bug Fixes
* `rawDataPacket` not being emitted
# 0.3.2
### Work In Progress
* SNTP Time Synchronization
### Bug Fixes
* updates to README.me and comments to change ntp to sntp, because the two are similar, but not the same and we do not want to be misleading
* Extended [Stnp](https://www.npmjs.com/package/sntp) to main openBCIBoard.js
* Add `.sntpNow()` function to get ntp time.
# 0.3.1
### Bug Fixes
* Bumped serialport version
# 0.3.0
### New Features
* Test Signals with ADS1299 using `.testSignal()`
* Continuous impedance testing, where each sample gets an `impedances` object that is an array of impedances for each
channel.
* OpenBCI Radio Test File
* Added Sntp npm module with helper functions
* Removed stopByte and startByte from sampleObjects
### Breaking Changes
* Changed simulator name to `OpenBCISimulator`
* Changed name of function `simulatorOn` to `simulatorEnable`
* Changed name of function `simulatorOff` to `simulatorDisable`
### Work In Progress
* NTP Time Synchronization
* Goertzel algorithm to get voltage for impedance calculation
### Bug fixes
* Impedance calculations
* Readme updates
* Serial buffer had the chance to become permanently unaligned, optimized and completely transformed and refactored the way bytes are processed.
* Changes to gain of channels not working correctly.
* Node 5 compatibility
### Github Issues Addressed
* #25, #26, #27, #29, #30, #31, #33, #34
Ver Arquivo
Arquivo binário não exibido.
+980 -515
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+266 -18
Ver Arquivo
@@ -40,7 +40,10 @@ const kOBCIChannelOn_14 = 'Y';
const kOBCIChannelOn_15 = 'U';
const kOBCIChannelOn_16 = 'I';
/** Test Signal Control Commands */
/** Test Signal Control Commands
* 1x - Voltage will be 1 * (VREFP - VREFN) / 2.4 mV
* 2x - Voltage will be 2 * (VREFP - VREFN) / 2.4 mV
*/
const kOBCITestSignalConnectToDC = 'p';
const kOBCITestSignalConnectToGround = '0';
const kOBCITestSignalConnectToPulse1xFast = '=';
@@ -123,6 +126,17 @@ const kOBCISDLogForMin30 = 'F';
const kOBCISDLogForSec14 = 'a';
const kOBCISDLogStop = 'j';
/** SD Card String Commands */
const kOBCIStringSDHour1 = '1hour';
const kOBCIStringSDHour2 = '2hour';
const kOBCIStringSDHour4 = '4hour';
const kOBCIStringSDHour12 = '12hour';
const kOBCIStringSDHour24 = '24hour';
const kOBCIStringSDMin5 = '5min';
const kOBCIStringSDMin15 = '15min';
const kOBCIStringSDMin30 = '30min';
const kOBCIStringSDSec14 = '14sec';
/** Stream Data Commands */
const kOBCIStreamStart = 'b';
const kOBCIStreamStop = 's';
@@ -150,11 +164,25 @@ const kOBCIFilterEnable = 'f';
/** Triggers */
const kOBCITrigger = '`';
/** Sync Clocks */
const kOBCISyncClockServerData = '<';
const kOBCISyncClockStart = '>';
const kOBCISyncClockStop = '.';
/** Possible number of channels */
const kOBCINumberOfChannelsDaisy = 16;
const kOBCINumberOfChannelsDefault = 8;
const kOBCINumberOfChannelsGanglion = 4;
/** Possible OpenBCI board types */
const kOBCIBoardDaisy = 'daisy';
const kOBCIBoardDefault = 'default';
const kOBCIBoardGanglion = 'ganglion';
/** Possible Simulator Line Noise injections */
const kOBCISimulatorLineNoiseHz60 = '60Hz';
const kOBCISimulatorLineNoiseHz50 = '50Hz';
const kOBCISimulatorLineNoiseNone = 'None';
/** Possible Sample Rates*/
const kOBCISampleRate125 = 125;
const kOBCISampleRate250 = 250;
@@ -162,6 +190,18 @@ const kOBCISampleRate250 = 250;
/** Packet Size */
const kOBCIPacketSize = 33;
/** OpenBCI V3 Standard Packet Positions */
/**
* 0:[startByte] | 1:[sampleNumber] | 2:[Channel-1.1] | 3:[Channel-1.2] | 4:[Channel-1.3] | 5:[Channel-2.1] | 6:[Channel-2.2] | 7:[Channel-2.3] | 8:[Channel-3.1] | 9:[Channel-3.2] | 10:[Channel-3.3] | 11:[Channel-4.1] | 12:[Channel-4.2] | 13:[Channel-4.3] | 14:[Channel-5.1] | 15:[Channel-5.2] | 16:[Channel-5.3] | 17:[Channel-6.1] | 18:[Channel-6.2] | 19:[Channel-6.3] | 20:[Channel-7.1] | 21:[Channel-7.2] | 22:[Channel-7.3] | 23:[Channel-8.1] | 24:[Channel-8.2] | 25:[Channel-8.3] | 26:[Aux-1.1] | 27:[Aux-1.2] | 28:[Aux-2.1] | 29:[Aux-2.2] | 30:[Aux-3.1] | 31:[Aux-3.2] | 32:StopByte
*/
const kOBCIPacketPositionStartByte = 0; // first byte
const kOBCIPacketPositionStopByte = 32; // [32]
const kOBCIPacketPositionStartAux = 26; // [26,27]:Aux 1 | [28,29]:Aux 2 | [30,31]:Aux 3
const kOBCIPacketPositionStopAux = 31; // - - - [30,31]:Aux 3 | 32: Stop byte
const kOBCIPacketPositionChannelDataStart = 2; // 0:startByte | 1:sampleNumber | [2:4] | [5:7] | [8:10] | [11:13] | [14:16] | [17:19] | [21:23] | [24:26]
const kOBCIPacketPositionChannelDataStop = 25; // 24 bytes for channel data
const kOBCIPacketPositionSampleNumber = 1;
/** Notable Bytes */
const kOBCIByteStart = 0xA0;
const kOBCIByteStop = 0xC0;
@@ -173,13 +213,43 @@ const kErrorInvalidByteStop = "Invalid Stop Byte";
const kErrorUndefinedOrNullInput = "Undefined or Null Input";
/** Max Master Buffer Size */
const kOBCIMasterBufferSize = kOBCIPacketSize * 100;
const kOBCIMasterBufferSize = 4096;
/** Impedance Calculation Variables */
const kOBCILeadOffDriveInAmps = 0.000000006;
const kOBCILeadOffFrequencyHz = 31.5;
/** Command send delay */
const kOBCIWriteIntervalDelayMS = 10;
const kOBCIWriteIntervalDelayMSLong = 50;
const kOBCIWriteIntervalDelayMSNone = 0;
const kOBCIWriteIntervalDelayMSShort = 10;
/** Impedance */
const kOBCIImpedanceTextBad = 'bad';
const kOBCIImpedanceTextNone = 'none';
const kOBCIImpedanceTextGood = 'good';
const kOBCIImpedanceTextInit = 'init';
const kOBCIImpedanceTextOk = 'ok';
const kOBCIImpedanceThresholdGoodMin = 0;
const kOBCIImpedanceThresholdGoodMax = 5000;
const kOBCIImpedanceThresholdOkMin = 5001;
const kOBCIImpedanceThresholdOkMax = 10000;
const kOBCIImpedanceThresholdBadMin = 10001;
const kOBCIImpedanceThresholdBadMax = 1000000;
const kOBCIImpedanceSeriesResistor = 2200; // There is a 2.2 k Ohm series resistor that must be subtracted
/** Simulator */
const kOBCISimulatorPortName = 'OpenBCISimulator';
/**
* Raw data packet types/codes
*/
const kOBCIPacketTypeRawAux = 3; // 0011
const kOBCIPacketTypeStandard = 0; // 0000
const kOBCIPacketTypeTimeSynced = 1; // 0001
const kOBCIPacketTypeUserDefined = 2; // 0010
module.exports = {
/** Turning channels off */
@@ -342,6 +412,36 @@ module.exports = {
OBCITestSignalConnectToPulse1xSlow:kOBCITestSignalConnectToPulse1xSlow,
OBCITestSignalConnectToPulse2xFast:kOBCITestSignalConnectToPulse2xFast,
OBCITestSignalConnectToPulse2xSlow:kOBCITestSignalConnectToPulse2xSlow,
getTestSignalCommand: (signal) => {
return new Promise((resolve,reject) => {
switch (signal) {
case 'dc':
resolve(kOBCITestSignalConnectToDC);
break;
case 'ground':
resolve(kOBCITestSignalConnectToGround);
break;
case 'pulse1xFast':
resolve(kOBCITestSignalConnectToPulse1xFast);
break;
case 'pulse1xSlow':
resolve(kOBCITestSignalConnectToPulse1xSlow);
break;
case 'pulse2xFast':
resolve(kOBCITestSignalConnectToPulse2xFast);
break;
case 'pulse2xSlow':
resolve(kOBCITestSignalConnectToPulse2xSlow);
break;
case 'none':
resolve(kOBCIChannelDefaultAllSet);
break;
default:
reject('Invalid selection! Check your spelling.');
break;
}
})
},
/** Channel Setting Commands */
OBCIChannelCmdADCNormal:kOBCIChannelCmdADCNormal,
OBCIChannelCmdADCShorted:kOBCIChannelCmdADCShorted,
@@ -386,6 +486,15 @@ module.exports = {
OBCIChannelCmdSRB1Diconnect:kOBCIChannelCmdSRB1Diconnect,
OBCIChannelCmdSRB2Connect:kOBCIChannelCmdSRB2Connect,
OBCIChannelCmdSRB2Diconnect:kOBCIChannelCmdSRB2Diconnect,
/** Channel Settings Object */
channelSettingsObjectDefault: channelSettingsObjectDefault,
channelSettingsArrayInit: (numberOfChannels) => {
var newChannelSettingsArray = [];
for (var i = 0; i < numberOfChannels; i++) {
newChannelSettingsArray.push(channelSettingsObjectDefault(i));
}
return newChannelSettingsArray;
},
/** Channel Setting Helper Strings */
OBCIStringADCNormal:kOBCIStringADCNormal,
OBCIStringADCShorted:kOBCIStringADCShorted,
@@ -396,10 +505,10 @@ module.exports = {
OBCIStringADCBiasDrp:kOBCIStringADCBiasDrp,
OBCIStringADCBiasDrn:kOBCIStringADCBiasDrn,
/**
* Purpose: To convert a string like 'normal' to the correct command (i.e. '1')
* @description To convert a string like 'normal' to the correct command (i.e. '1')
* @param adcString
* @returns {Promise}
* Author: AJ Keller (@pushtheworldllc)
* @author AJ Keller (@pushtheworldllc)
*/
commandForADCString:commandForADCString,
/** Default Channel Settings */
@@ -421,6 +530,58 @@ module.exports = {
OBCISDLogForMin30:kOBCISDLogForMin30,
OBCISDLogForSec14:kOBCISDLogForSec14,
OBCISDLogStop:kOBCISDLogStop,
/** SD Card String Commands */
OBCIStringSDHour1:kOBCIStringSDHour1,
OBCIStringSDHour2:kOBCIStringSDHour2,
OBCIStringSDHour4:kOBCIStringSDHour4,
OBCIStringSDHour12:kOBCIStringSDHour12,
OBCIStringSDHour24:kOBCIStringSDHour24,
OBCIStringSDMin5:kOBCIStringSDMin5,
OBCIStringSDMin15:kOBCIStringSDMin15,
OBCIStringSDMin30:kOBCIStringSDMin30,
OBCIStringSDSec14:kOBCIStringSDSec14,
/**
* @description Converts a sd string into the proper setting.
* @param stringCommand {String} - The length of time you want to record to the SD for.
* @returns {Promise} The command to send to the Board, returns an error on improper `stringCommand`
*/
sdSettingForString: (stringCommand) => {
return new Promise((resolve,reject) => {
switch (stringCommand) {
case kOBCIStringSDHour1:
resolve(kOBCISDLogForHour1);
break;
case kOBCIStringSDHour2:
resolve(kOBCISDLogForHour2);
break;
case kOBCIStringSDHour4:
resolve(kOBCISDLogForHour4);
break;
case kOBCIStringSDHour12:
resolve(kOBCISDLogForHour12);
break;
case kOBCIStringSDHour24:
resolve(kOBCISDLogForHour24);
break;
case kOBCIStringSDMin5:
resolve(kOBCISDLogForMin5);
break;
case kOBCIStringSDMin15:
resolve(kOBCISDLogForMin15);
break;
case kOBCIStringSDMin30:
resolve(kOBCISDLogForMin30);
break;
case kOBCIStringSDSec14:
resolve(kOBCISDLogForSec14);
break;
default:
reject(new Error(TypeError));
break;
}
});
},
/** Stream Data Commands */
OBCIStreamStart:kOBCIStreamStart,
OBCIStreamStop:kOBCIStreamStop,
@@ -480,6 +641,20 @@ module.exports = {
OBCINumberOfChannelsDaisy:kOBCINumberOfChannelsDaisy,
OBCINumberOfChannelsDefault:kOBCINumberOfChannelsDefault,
OBCINumberOfChannelsGanglion:kOBCINumberOfChannelsGanglion,
/** Possible OpenBCI board types */
OBCIBoardDaisy:kOBCIBoardDaisy,
OBCIBoardDefault:kOBCIBoardDefault,
OBCIBoardGanglion:kOBCIBoardGanglion,
numberOfChannelsForBoardType: boardType => {
switch (boardType) {
case kOBCIBoardDaisy:
return kOBCINumberOfChannelsDaisy;
case kOBCIBoardGanglion:
return kOBCINumberOfChannelsGanglion;
default:
return kOBCINumberOfChannelsDefault;
}
},
/** Possible Sample Rates */
OBCISampleRate125:kOBCISampleRate125,
OBCISampleRate250:kOBCISampleRate250,
@@ -497,16 +672,65 @@ module.exports = {
OBCIMasterBufferSize:kOBCIMasterBufferSize,
/** Impedance Calculation Variables */
OBCILeadOffDriveInAmps:kOBCILeadOffDriveInAmps,
OBCILeadOffFrequencyHz:kOBCILeadOffFrequencyHz,
/** Channel Setter Maker */
getChannelSetter:channelSetter,
/** Impedance Setter Maker */
getImpedanceSetter:impedanceSetter,
/** Command send delay */
OBCIWriteIntervalDelayMS:kOBCIWriteIntervalDelayMS
OBCIWriteIntervalDelayMSLong:kOBCIWriteIntervalDelayMSLong,
OBCIWriteIntervalDelayMSNone:kOBCIWriteIntervalDelayMSNone,
OBCIWriteIntervalDelayMSShort:kOBCIWriteIntervalDelayMSShort,
/** Sync Clocks */
OBCISyncClockServerData:kOBCISyncClockServerData,
OBCISyncClockStart:kOBCISyncClockStart,
OBCISyncClockStop:kOBCISyncClockStop,
/** Impedance */
OBCIImpedanceTextBad:kOBCIImpedanceTextBad,
OBCIImpedanceTextGood:kOBCIImpedanceTextGood,
OBCIImpedanceTextInit:kOBCIImpedanceTextInit,
OBCIImpedanceTextOk:kOBCIImpedanceTextOk,
OBCIImpedanceTextNone:kOBCIImpedanceTextNone,
OBCIImpedanceThresholdBadMax:kOBCIImpedanceThresholdBadMax,
OBCIImpedanceSeriesResistor:kOBCIImpedanceSeriesResistor,
getTextForRawImpedance: (value) => {
if (value > kOBCIImpedanceThresholdGoodMin && value < kOBCIImpedanceThresholdGoodMax) {
return kOBCIImpedanceTextGood;
} else if (value > kOBCIImpedanceThresholdOkMin && value < kOBCIImpedanceThresholdOkMax) {
return kOBCIImpedanceTextOk;
} else if (value > kOBCIImpedanceThresholdBadMin && value < kOBCIImpedanceThresholdBadMax) {
return kOBCIImpedanceTextBad;
} else {
return kOBCIImpedanceTextNone;
}
},
/** Simulator */
OBCISimulatorPortName:kOBCISimulatorPortName,
/** Raw data packet types */
OBCIPacketTypeRawAux:kOBCIPacketTypeRawAux,
OBCIPacketTypeStandard:kOBCIPacketTypeStandard,
OBCIPacketTypeTimeSynced:kOBCIPacketTypeTimeSynced,
OBCIPacketTypeUserDefined:kOBCIPacketTypeUserDefined,
/** fun funcs */
isNumber:isNumber,
isBoolean:isBoolean,
isString:isString,
/** OpenBCI V3 Standard Packet Positions */
OBCIPacketPositionStartByte:kOBCIPacketPositionStartByte,
OBCIPacketPositionStopByte:kOBCIPacketPositionStopByte,
OBCIPacketPositionStartAux:kOBCIPacketPositionStartAux,
OBCIPacketPositionStopAux:kOBCIPacketPositionStopAux,
OBCIPacketPositionChannelDataStart:kOBCIPacketPositionChannelDataStart,
OBCIPacketPositionChannelDataStop:kOBCIPacketPositionChannelDataStop,
OBCIPacketPositionSampleNumber:kOBCIPacketPositionSampleNumber,
/** Possible Simulator Line Noise injections */
OBCISimulatorLineNoiseHz60:kOBCISimulatorLineNoiseHz60,
OBCISimulatorLineNoiseHz50:kOBCISimulatorLineNoiseHz50,
OBCISimulatorLineNoiseNone:kOBCISimulatorLineNoiseNone
};
/**
* Purpose: To add a usability abstraction layer above channel setting commands. Due to the
* @description To add a usability abstraction layer above channel setting commands. Due to the
* extensive and highly specific nature of the channel setting command chain, this
* will take several different human readable inputs and merge to one array filled
* with the correct commands, prime for sending directly to the write command.
@@ -543,22 +767,25 @@ function channelSetter(channelNumber,powerDown,gain,inputType,bias,srb2,srb1) {
if (!isNumber(channelNumber)) reject('channelNumber must be of type \'number\' ');
if (!isBoolean(powerDown)) reject('powerDown must be of type \'boolean\' ');
if (!isNumber(gain)) reject('gain must be of type \'number\' ');
if (!isString(inputTdype)) reject('inputType must be of type \'string\' ');
if (!isString(inputType)) reject('inputType must be of type \'string\' ');
if (!isBoolean(bias)) reject('bias must be of type \'boolean\' ');
if (!isBoolean(srb2)) reject('srb1 must be of type \'boolean\' ');
if (!isBoolean(srb1)) reject('srb2 must be of type \'boolean\' ');
// Set Channel Number
var p1 = commandChannelForCmd(channelNumber);
var p1 = commandChannelForCmd(channelNumber)
.catch(err => reject(err));
// Set POWER_DOWN
cmdPowerDown = powerDown ? kOBCIChannelCmdPowerOff : kOBCIChannelCmdPowerOn;
// Set Gain
var p2 = commandForGain(gain);
var p2 = commandForGain(gain)
.catch(err => reject(err));
// Set ADC string
var p3 = commandForADCString(inputType);
var p3 = commandForADCString(inputType)
.catch(err => reject(err));
// Set BIAS
cmdBias = bias ? kOBCIChannelCmdBiasInclude : kOBCIChannelCmdBiasRemove;
@@ -569,6 +796,16 @@ function channelSetter(channelNumber,powerDown,gain,inputType,bias,srb2,srb1) {
// Set SRB1
cmdSrb1 = srb1 ? kOBCIChannelCmdSRB1Connect : kOBCIChannelCmdSRB1Diconnect;
var newChannelSettingsObject = {
channelNumber:channelNumber,
powerDown: powerDown,
gain: gain,
inputType: inputType,
bias: bias,
srb2: srb2,
srb1: srb1
};
Promise.all([p1,p2,p3]).then(function(values) {
var outputArray = [
kOBCIChannelCmdSet,
@@ -582,13 +819,13 @@ function channelSetter(channelNumber,powerDown,gain,inputType,bias,srb2,srb1) {
kOBCIChannelCmdLatch
];
//console.log(outputArray);
resolve(outputArray);
resolve(outputArray,newChannelSettingsObject);
});
});
}
/**
* Purpose: To build the array of commands to send to the board to measure impedance
* @description To build the array of commands to send to the board to measure impedance
* @param channelNumber
* @param pInputApplied - Bool (true -> Test Signal Applied, false -> Test Signal Not Applied (default))
* applies the test signal to the P input
@@ -605,24 +842,24 @@ function impedanceSetter(channelNumber,pInputApplied,nInputApplied) {
if (!isBoolean(pInputApplied)) reject('pInputApplied must be of type \'boolean\' ');
if (!isBoolean(nInputApplied)) reject('nInputApplied must be of type \'boolean\' ');
// Set nInputApplied
cmdNInputApplied = nInputApplied ? kOBCIChannelImpedanceTestSignalApplied : kOBCIChannelImpedanceTestSignalAppliedNot;
// Set pInputApplied
cmdPInputApplied = pInputApplied ? kOBCIChannelImpedanceTestSignalApplied : kOBCIChannelImpedanceTestSignalAppliedNot;
// Set nInputApplied
cmdNInputApplied = nInputApplied ? kOBCIChannelImpedanceTestSignalApplied : kOBCIChannelImpedanceTestSignalAppliedNot;
// Set Channel Number
commandChannelForCmd(channelNumber).then(command => {
var outputArray = [
kOBCIChannelImpedanceSet,
command,
cmdNInputApplied,
cmdPInputApplied,
cmdNInputApplied,
kOBCIChannelImpedanceLatch
];
//console.log(outputArray);
resolve(outputArray);
});
}).catch(err => reject(err));
});
}
@@ -757,4 +994,15 @@ function commandChannelForCmd(channelNumber) {
break;
}
});
}
function channelSettingsObjectDefault(channelNumber) {
return {
channelNumber:channelNumber,
powerDown: false,
gain: 24,
inputType: kOBCIStringADCNormal,
bias: true,
srb2: true,
srb1: false
};
}
+475 -98
Ver Arquivo
@@ -1,76 +1,110 @@
'use strict';
var gaussian = require('gaussian');
var k = require('./openBCIConstants');
/** Constants for interpreting the EEG data */
// Reference voltage for ADC in ADS1299.
// Set by its hardware.
const ADS1299_VREF = 4.5;
// Assumed gain setting for ADS1299.
// Set by its Arduino code.
const ADS1299_GAIN = 24.0;
// Scale factor for aux data
const SCALE_FACTOR_ACCEL = 0.002 / Math.pow(2,4);
// Scale factor for channelData
const SCALE_FACTOR_CHANNEL = ADS1299_VREF / ADS1299_GAIN / (Math.pow(2,23) - 1);
// X, Y, Z
const ACCEL_NUMBER_AXIS = 3;
// Default ADS1299 gains array
var k = require('./openBCIConstants');
// For computing Goertzel Algorithm
// See: http://www.embedded.com/design/configurable-systems/4024443/The-Goertzel-Algorithm
// In the tutorial cited above, GOERTZEL_BLOCK_SIZE is referred to as N
const GOERTZEL_BLOCK_SIZE = 62;
const GOERTZEL_K_250 = Math.floor(0.5 + ((GOERTZEL_BLOCK_SIZE * k.OBCILeadOffFrequencyHz) / k.OBCISampleRate250));
const GOERTZEL_W_250 = ((2 * Math.PI) / GOERTZEL_BLOCK_SIZE) * GOERTZEL_K_250;
const GOERTZEL_COEFF_250 = 2 * Math.cos(GOERTZEL_W_250);
// TODO: Add support for 16 channel Daisy board
module.exports = {
convertPacketToSample: function (dataBuf) {
var self = this;
if(dataBuf === undefined || dataBuf === null) {
return;
}
var numberOfBytes = dataBuf.byteLength;
var scaleData = true;
var sampleModule = {
if (dataBuf[0] != k.OBCIByteStart) return;
if (dataBuf[32] != k.OBCIByteStop) return;
if (numberOfBytes != k.OBCIPacketSize) return;
/**
* @description This takes a 33 byte packet and converts it based on the last four bits.
* 0000 - Standard OpenBCI V3 Sample Packet
* @param dataBuf
* @param channelSettingsArray
* @returns {Promise}
*/
parseRawPacket: (dataBuf,channelSettingsArray) => {
const defaultChannelSettingsArray = k.channelSettingsArrayInit(k.OBCINumberOfChannelsDefault);
return new Promise((resolve, reject) => {
if (dataBuf === undefined || dataBuf === null) reject('Error [parseRawPacket]: dataBuf must be defined.');
// Verify proper start byte
if (dataBuf[0] != k.OBCIByteStart) reject('Error [parseRawPacket]: Invalid start byte of ' + dataBuf[0].toString(16) + ' expected ' + k.OBCIByteStart.toString(16));
// channelSettingsArray is optional, defaults to CHANNEL_SETTINGS_ARRAY_DEFAULT
channelSettingsArray = channelSettingsArray || defaultChannelSettingsArray;
// Last nibble contains packet type
var packetType = getRawPacketType(dataBuf[k.OBCIPacketPositionStopByte]);
var channelData = function () {
var out = {};
var count = 1;
for (var i = 2; i <= numberOfBytes - 10; i += 3) {
//console.log('\tDataBuf is is: ' + dataBuf.slice(i, i + 3).toString('hex'));
//var newNumber = self.interpret24bitAsInt32(dataBuf.slice(i, i + 3));
//out[count] = newNumber * SCALE_FACTOR_CHANNEL;
out[count] = scaleData ? self.interpret24bitAsInt32(dataBuf.slice(i, i + 3)) * SCALE_FACTOR_CHANNEL : self.interpret24bitAsInt32(dataBuf.slice(i, i + 3));
//console.log("in" + dataBuf.slice(i,i+3));
//console.log(out[count]);
count++;
switch (packetType) {
case k.OBCIPacketTypeUserDefined:
// Do something with the packet, maybe nothing
resolve();
break;
case k.OBCIPacketTypeTimeSynced:
// Parse the time synced packet
break;
default: //normal packet
parsePacketStandard(dataBuf,channelSettingsArray).then(sampleObject => {
resolve(sampleObject);
}).catch(err => reject(err));
break;
}
return out;
};
var auxData = function () {
var out = {};
var count = 0;
for (var i = numberOfBytes - 7; i < numberOfBytes - 1; i += 2) {
out[count] = scaleData ? self.interpret16bitAsInt32(dataBuf.slice(i, i + 2)) * SCALE_FACTOR_ACCEL : self.interpret16bitAsInt32(dataBuf.slice(i, i + 2));
count++;
}
return out;
};
return {
startByte: dataBuf[0], // byte
sampleNumber: dataBuf[1], // byte
channelData: channelData(), // multiple of 3 bytes
auxData: auxData(), // multiple of 2 bytes
stopByte: dataBuf[numberOfBytes - 1] // byte
}
});
},
debugPrettyPrint: function(sample) {
/**
* @description Mainly used by the simulator to convert a randomly generated sample into a std OpenBCI V3 Packet
* @param sample - A sample object
* @returns {Buffer}
*/
convertSampleToPacket: function(sample) {
var packetBuffer = new Buffer(k.OBCIPacketSize);
packetBuffer.fill(0);
// start byte
packetBuffer[0] = k.OBCIByteStart;
// sample number
packetBuffer[1] = sample.sampleNumber;
// channel data
for (var i = 0; i < k.OBCINumberOfChannelsDefault; i++) {
var threeByteBuffer = this.floatTo3ByteBuffer(sample.channelData[i]);
threeByteBuffer.copy(packetBuffer, 2 + (i * 3));
}
for (var j = 0; j < 3; j++) {
var twoByteBuffer = this.floatTo2ByteBuffer(sample.auxData[j]);
twoByteBuffer.copy(packetBuffer, (k.OBCIPacketSize - 1 - 6) + (i * 2));
}
// stop byte
packetBuffer[k.OBCIPacketSize - 1] = k.OBCIByteStop;
return packetBuffer;
},
debugPrettyPrint: function(sample) {
if(sample === null || sample === undefined) {
console.log('== Sample is undefined ==');
} else {
console.log('-- Sample --');
console.log('---- Start Byte: ' + sample.startByte);
console.log('---- Sample Number: ' + sample.sampleNumber);
for(var i = 1; i <= 8; i++) {
console.log('---- Channel Data ' + i + ': ' + sample.channelData[i]);
for(var i = 0; i < 8; i++) {
console.log('---- Channel Data ' + (i + 1) + ': ' + sample.channelData[i]);
}
for(var j = 0; j < 3; j++) {
console.log('---- Aux Data ' + j + ': ' + sample.auxData[j]);
@@ -78,20 +112,135 @@ module.exports = {
console.log('---- Stop Byte: ' + sample.stopByte);
}
},
impedanceCalculation: function(sampleObject) {
samplePrintHeader: function() {
return (
'All voltages in Volts!' +
'sampleNumber, channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8, aux1, aux2, aux3\n');
},
samplePrintLine: function(sample) {
return new Promise((resolve, reject) => {
if (sample === null || sample === undefined) reject('undefined sample');
resolve(
sample.sampleNumber + ',' +
sample.channelData[0].toFixed(8) + ',' +
sample.channelData[1].toFixed(8) + ',' +
sample.channelData[2].toFixed(8) + ',' +
sample.channelData[3].toFixed(8) + ',' +
sample.channelData[4].toFixed(8) + ',' +
sample.channelData[5].toFixed(8) + ',' +
sample.channelData[6].toFixed(8) + ',' +
sample.channelData[7].toFixed(8) + ',' +
sample.auxData[0].toFixed(8) + ',' +
sample.auxData[1].toFixed(8) + ',' +
sample.auxData[2].toFixed(8) + '\n'
);
});
},
/**
* @description Convert float number into three byte buffer. This is the opposite of .interpret24bitAsInt32()
* @param float - The number you want to convert
* @returns {Buffer} - 3-byte buffer containing the float
*/
floatTo3ByteBuffer: function(float) {
var intBuf = new Buffer(3); // 3 bytes for 24 bits
intBuf.fill(0); // Fill the buffer with 0s
var temp = float / ( ADS1299_VREF / 24 / (Math.pow(2,23) - 1)); // Convert to counts
temp = Math.floor(temp); // Truncate counts number
// Move into buffer
intBuf[2] = temp & 255;
intBuf[1] = (temp & (255 << 8)) >> 8;
intBuf[0] = (temp & (255 << 16)) >> 16;
return intBuf;
},
/**
* @description Convert float number into three byte buffer. This is the opposite of .interpret24bitAsInt32()
* @param float - The number you want to convert
* @returns {Buffer} - 3-byte buffer containing the float
*/
floatTo2ByteBuffer: function(float) {
var intBuf = new Buffer(2); // 2 bytes for 16 bits
intBuf.fill(0); // Fill the buffer with 0s
var temp = float / SCALE_FACTOR_ACCEL; // Convert to counts
temp = Math.floor(temp); // Truncate counts number
//console.log('Num: ' + temp);
// Move into buffer
intBuf[1] = temp & 255;
intBuf[0] = (temp & (255 << 8)) >> 8;
return intBuf;
},
/**
* @description Calculate the impedance for one channel only.
* @param sampleObject - Standard OpenBCI sample object
* @param channelNumber - Number, the channel you want to calculate impedance for.
* @returns {Promise} - Fulfilled with impedance value for the specified channel.
* @author AJ Keller
*/
impedanceCalculationForChannel: function(sampleObject,channelNumber) {
const sqrt2 = Math.sqrt(2);
return new Promise((resolve,reject) => {
if(sampleObject === undefined || sampleObject === null) reject('Sample Object cannot be null or undefined');
if(sampleObject.channelData === undefined || sampleObject.channelData === null) reject('Channel cannot be null or undefined');
if(channelNumber < 1 || channelNumber > k.OBCINumberOfChannelsDefault) reject('Channel number invalid.');
var index = channelNumber - 1;
if (sampleObject.channelData[index] < 0) {
sampleObject.channelData[index] *= -1;
}
var impedance = (sqrt2 * sampleObject.channelData[index]) / k.OBCILeadOffDriveInAmps;
//if (index === 0) console.log("Voltage: " + (sqrt2*sampleObject.channelData[index]) + " leadoff amps: " + k.OBCILeadOffDriveInAmps + " impedance: " + impedance);
resolve(impedance);
});
},
/**
* @description Calculate the impedance for all channels.
* @param sampleObject - Standard OpenBCI sample object
* @returns {Promise} - Fulfilled with impedances for the sample
* @author AJ Keller
*/
impedanceCalculationForAllChannels: function(sampleObject) {
const sqrt2 = Math.sqrt(2);
return new Promise((resolve,reject) => {
if(sampleObject === undefined || sampleObject === null) reject('Sample Object cannot be null or undefined');
if(sampleObject.channelData === undefined || sampleObject.channelData === null) reject('Channel cannot be null or undefined');
var impedanceArray = [];
for (var i = 1; i <= k.OBCINumberOfChannelsDefault; i++) {
impedanceArray[i] = (sqrt2 * sampleObject.channelData[i]) / k.OBCILeadOffDriveInAmps;
var sampleImpedances = [];
var numChannels = sampleObject.channelData.length;
for (var index = 0;index < numChannels; index++) {
if (sampleObject.channelData[index] < 0) {
sampleObject.channelData[index] *= -1;
}
var impedance = (sqrt2 * sampleObject.channelData[index]) / k.OBCILeadOffDriveInAmps;
sampleImpedances.push(impedance);
//if (index === 0) console.log("Voltage: " + (sqrt2*sampleObject.channelData[index]) + " leadoff amps: " + k.OBCILeadOffDriveInAmps + " impedance: " + impedance);
}
sampleObject.impedanceArray = impedanceArray;
sampleObject.impedances = sampleImpedances;
resolve(sampleObject);
});
},
interpret16bitAsInt32: function(twoByteBuffer) {
var prefix = 0;
if(twoByteBuffer[0] > 127) {
//console.log('\t\tNegative number');
prefix = 65535; // 0xFFFF
}
return (prefix << 16) | (twoByteBuffer[0] << 8) | twoByteBuffer[1];
},
interpret24bitAsInt32: function(threeByteBuffer) {
var prefix = 0;
@@ -103,15 +252,20 @@ module.exports = {
return (prefix << 24 ) | (threeByteBuffer[0] << 16) | (threeByteBuffer[1] << 8) | threeByteBuffer[2];
},
interpret16bitAsInt32: function(twoByteBuffer) {
var prefix = 0;
if(twoByteBuffer[0] > 127) {
//console.log('\t\tNegative number');
prefix = 65535; // 0xFFFF
impedanceArray: (numberOfChannels) => {
var impedanceArray = [];
for (var i = 0; i < numberOfChannels; i++) {
impedanceArray.push(newImpedanceObject(i+1));
}
return impedanceArray;
},
impedanceObject: newImpedanceObject,
impedanceSummarize: (singleInputObject) => {
if (singleInputObject.raw > k.OBCIImpedanceThresholdBadMax) { // The case for no load (super high impedance)
singleInputObject.text = k.OBCIImpedanceTextNone;
} else {
singleInputObject.text = k.getTextForRawImpedance(singleInputObject.raw); // Get textual impedance
}
return (prefix << 16) | (twoByteBuffer[0] << 8) | twoByteBuffer[1];
},
newSample: function() {
return {
@@ -122,68 +276,291 @@ module.exports = {
stopByte: k.OBCIByteStop
}
},
randomSample: function(numberOfChannels,sampleRateHz) {
/**
* @description Create a configurable function to return samples for a simulator. This implements 1/f filtering injection to create more brain like data.
* @param numberOfChannels
* @param sampleRateHz
* @param injectAlpha
* @param lineNoise
* @returns {Function}
*/
randomSample: function(numberOfChannels,sampleRateHz, injectAlpha,lineNoise) {
var self = this;
const distribution = gaussian(0,2);
const distribution = gaussian(0,1);
const sineWaveFreqHz10 = 10;
const sineWaveFreqHz50 = 50;
const sineWaveFreqHz60 = 60;
const pi = Math.PI;
const sqrt2 = Math.sqrt(2);
const uVolts = 1000000;
var sinePhaseRad = new Array(numberOfChannels+1); //prevent index error with '+1'
sinePhaseRad.fill(0);
var auxData = [0,0,0];
var accelCounter = 0;
// With 250Hz, every 10 samples, with 125Hz, every 5...
var samplesPerAccelRate = Math.floor(sampleRateHz / 25); // best to make this an integer
if (samplesPerAccelRate < 1) samplesPerAccelRate = 1;
// Init arrays to hold coefficients for each channel and init to 0
// This gives the 1/f filter memory on each iteration
var b0 = new Array(numberOfChannels).fill(0);
var b1 = new Array(numberOfChannels).fill(0);
var b2 = new Array(numberOfChannels).fill(0);
return function(previousSampleNumber) {
var newSample = self.newSample();
//console.log('New Sample: ' + JSON.stringify(newSample));
for(var i = 1; i <= numberOfChannels; i++) { //channels are 1 indexed
newSample.channelData[i] = distribution.ppf(Math.random())*Math.sqrt(sampleRateHz/2)/uVolts;
var whiteNoise;
for(var i = 0; i < numberOfChannels; i++) { //channels are 0 indexed
// This produces white noise
whiteNoise = distribution.ppf(Math.random()) * Math.sqrt(sampleRateHz/2)/uVolts;
switch (i) {
case 1: // scale first channel higher
newSample.channelData[i] *= 10;
break;
case 2:
sinePhaseRad[i] += 2 * pi * sineWaveFreqHz10 / sampleRateHz;
if (sinePhaseRad[i] > 2 * pi) {
sinePhaseRad[i] -= 2 * pi;
case 0: // Add 10Hz signal to channel 1... brainy
case 1:
if (injectAlpha) {
sinePhaseRad[i] += 2 * Math.PI * sineWaveFreqHz10 / sampleRateHz;
if (sinePhaseRad[i] > 2 * Math.PI) {
sinePhaseRad[i] -= 2 * Math.PI;
}
whiteNoise += (5 * Math.SQRT2 * Math.sin(sinePhaseRad[i]))/uVolts;
}
newSample.channelData[i] += (10 * sqrt2 * Math.sin(sinePhaseRad[i]))/uVolts;
break;
case 3:
sinePhaseRad[i] += 2 * pi * sineWaveFreqHz50 / sampleRateHz;
if (sinePhaseRad[i] > 2 * pi) {
sinePhaseRad[i] -= 2 * pi;
default:
if (lineNoise === k.OBCISimulatorLineNoiseHz60) {
// If we're in murica we want to add 60Hz line noise
sinePhaseRad[i] += 2 * Math.PI * sineWaveFreqHz60 / sampleRateHz;
if (sinePhaseRad[i] > 2 * Math.PI) {
sinePhaseRad[i] -= 2 * Math.PI;
}
whiteNoise += (8 * Math.SQRT2 * Math.sin(sinePhaseRad[i])) / uVolts;
} else if (lineNoise === k.OBCISimulatorLineNoiseHz50){
// add 50Hz line noise if we are not in america
sinePhaseRad[i] += 2 * Math.PI * sineWaveFreqHz50 / sampleRateHz;
if (sinePhaseRad[i] > 2 * Math.PI) {
sinePhaseRad[i] -= 2 * Math.PI;
}
whiteNoise += (8 * Math.SQRT2 * Math.sin(sinePhaseRad[i])) / uVolts;
}
newSample.channelData[i] += (50 * sqrt2 * Math.sin(sinePhaseRad[i]))/uVolts;
break;
case 4:
sinePhaseRad[i] += 2 * pi * sineWaveFreqHz60 / sampleRateHz;
if (sinePhaseRad[i] > 2 * pi) {
sinePhaseRad[i] -= 2 * pi;
}
newSample.channelData[i] += (50 * sqrt2 * Math.sin(sinePhaseRad[i]))/uVolts;
break;
}
/**
* See http://www.firstpr.com.au/dsp/pink-noise/ section "Filtering white noise to make it pink"
*/
b0[i] = 0.99765 * b0[i] + whiteNoise * 0.0990460;
b1[i] = 0.96300 * b1[i] + whiteNoise * 0.2965164;
b2[i] = 0.57000 * b2[i] + whiteNoise * 1.0526913;
newSample.channelData[i] = b0[i] + b1[i] + b2[i] + whiteNoise * 0.1848;
}
if (previousSampleNumber == 255) {
newSample.sampleNumber = 0;
} else {
newSample.sampleNumber = previousSampleNumber + 1;
}
newSample.auxData = auxData;
/**
* Sample rate of accelerometer is 25Hz... when the accelCounter hits the relative sample rate of the accel
* we will output a new accel value. The approach will be to consider that Z should be about 1 and X and Y
* should be somewhere around 0.
*/
if (accelCounter == samplesPerAccelRate) {
// Initialize a new array
var accelArray = [0,0,0];
// Calculate X
accelArray[0] = (Math.random() * 0.1 * (Math.random() > 0.5 ? -1 : 1));
// Calculate Y
accelArray[1] = (Math.random() * 0.1 * (Math.random() > 0.5 ? -1 : 1));
// Calculate Z, this is around 1
accelArray[2] = 1 - ((Math.random() * 0.4) * (Math.random() > 0.5 ? -1 : 1));
// Store the newly calculated value
newSample.auxData = accelArray;
// Reset the counter
accelCounter = 0;
} else {
// Increment counter
accelCounter++;
// Store the default value
newSample.auxData = auxData;
}
return newSample;
};
},
scaleFactorAux: SCALE_FACTOR_ACCEL,
scaleFactorChannel: SCALE_FACTOR_CHANNEL,
k:k
};
k:k,
/**
* @description Use the Goertzel algorithm to calculate impedances
* @param sample - a sample with channelData Array
* @param goertzelObj - An object that was created by a call to this.goertzelNewObject()
* @returns {Array} - Returns an array if finished computing
*/
goertzelProcessSample: (sample,goertzelObj) => {
// calculate the goertzel values for all channels
for (var i = 0; i < goertzelObj.numberOfChannels; i++) {
var q0 = GOERTZEL_COEFF_250 * goertzelObj.q1[i] - goertzelObj.q2[i] + sample.channelData[i];
goertzelObj.q2[i] = goertzelObj.q1[i];
goertzelObj.q1[i] = q0;
//console.log('Q1: ' + goertzelObj.q1[i] + ' Q2: ' + goertzelObj.q2[i]);
}
// Increment the index counter
goertzelObj.index++;
// Have we iterated more times then block size?
if (goertzelObj.index > GOERTZEL_BLOCK_SIZE) {
var impedanceArray = [];
for (var j = 0; j < goertzelObj.numberOfChannels; j++) {
// Calculate the magnitude of the voltage
var q1SQRD = goertzelObj.q1[j] * goertzelObj.q1[j];
var q2SQRD = goertzelObj.q2[j] * goertzelObj.q2[j];
var lastPart = goertzelObj.q1[j] * goertzelObj.q2[j] * GOERTZEL_COEFF_250;
//console.log('Chan ' + j + ', Q1^2: ' + q1SQRD + ', Q2^2: ' + q2SQRD + ', Last Part: ' + lastPart);
var voltage = Math.sqrt((goertzelObj.q1[j] * goertzelObj.q1[j]) + (goertzelObj.q2[j] * goertzelObj.q2[j]) - goertzelObj.q1[j] * goertzelObj.q2[j] * GOERTZEL_COEFF_250);
// Calculate the impedance r = v/i
var impedance = voltage / k.OBCILeadOffDriveInAmps;
// Push the impedance into the final array
impedanceArray.push(impedance);
// Reset the goertzel variables to get ready for the next iteration
goertzelObj.q1[j] = 0;
goertzelObj.q2[j] = 0;
}
// Reset the goertzel index counter
goertzelObj.index = 0;
// Pass out the impedance array
return impedanceArray;
} else {
// This reject is really just for debugging
return;
}
},
goertzelNewObject: (numberOfChannels) => {
// Object to help calculate the goertzel
var q1 = [];
var q2 = [];
for (var i = 0; i < numberOfChannels; i++) {
q1.push(0);
q2.push(0);
}
return {
q1: q1,
q2: q2,
index: 0,
numberOfChannels: numberOfChannels
}
},
GOERTZEL_BLOCK_SIZE:GOERTZEL_BLOCK_SIZE
};
module.exports = sampleModule;
function newImpedanceObject(channelNumber) {
return {
channel: channelNumber,
P: {
raw: -1,
text: k.OBCIImpedanceTextInit
},
N: {
raw: -1,
text: k.OBCIImpedanceTextInit
}
}
}
/**
* @description This method parses a 33 byte OpenBCI V3 packet and converts to a sample object
* @param dataBuf - 33 byte packet that has bytes:
* 0:[startByte] | 1:[sampleNumber] | 2:[Channel-1.1] | 3:[Channel-1.2] | 4:[Channel-1.3] | 5:[Channel-2.1] | 6:[Channel-2.2] | 7:[Channel-2.3] | 8:[Channel-3.1] | 9:[Channel-3.2] | 10:[Channel-3.3] | 11:[Channel-4.1] | 12:[Channel-4.2] | 13:[Channel-4.3] | 14:[Channel-5.1] | 15:[Channel-5.2] | 16:[Channel-5.3] | 17:[Channel-6.1] | 18:[Channel-6.2] | 19:[Channel-6.3] | 20:[Channel-7.1] | 21:[Channel-7.2] | 22:[Channel-7.3] | 23:[Channel-8.1] | 24:[Channel-8.2] | 25:[Channel-8.3] | 26:[Aux-1.1] | 27:[Aux-1.2] | 28:[Aux-2.1] | 29:[Aux-2.2] | 30:[Aux-3.1] | 31:[Aux-3.2] | 32:StopByte
* @param channelSettingsArray - An array of channel settings that is an Array that has shape similar to the one
* calling OpenBCIConstans.channelSettingsArrayInit(). The most important rule here is that it is
* Array of objects that have key-value pair {gain:NUMBER}
* @returns {Promise} - Fulfilled with a sample object that has form:
* {
* channelData: Array of floats
* auxData: Array of floats of accel data
* sampleNumber: a Number that is the sample
* }
*/
function parsePacketStandard(dataBuf, channelSettingsArray) {
return new Promise((resolve, reject) => {
if (dataBuf.byteLength != k.OBCIPacketSize) reject("Error [parsePacketStandard]: input buffer must be " + k.OBCIPacketSize + " bytes!");
var sampleObject = {};
// Need build the standard sample object
getAccelDataArray(dataBuf.slice(k.OBCIPacketPositionStartAux,k.OBCIPacketPositionStopAux+1))
.then(accelData => {
sampleObject.auxData = accelData;
return getChannelDataArray(dataBuf.slice(k.OBCIPacketPositionChannelDataStart,k.OBCIPacketPositionChannelDataStop+1), channelSettingsArray);
})
.then(channelSettingArray => {
sampleObject.channelData = channelSettingArray;
// Get the sample number
sampleObject.sampleNumber = dataBuf[k.OBCIPacketPositionSampleNumber];
// Get the start byte
sampleObject.startByte = dataBuf[0];
// Get the stop byte
sampleObject.stopByte = dataBuf[k.OBCIPacketPositionStopByte];
resolve(sampleObject);
})
.catch(err => {
console.log(err);
//reject(err);
});
});
}
/**
* @description Takes a buffer filled with 3 16 bit integers from an OpenBCI device and converts based on settings
* of the MPU, values are in ?
* @param dataBuf - Buffer that is 6 bytes long
* @returns {Promise} - Fulfilled with Array of floats 3 elements long
* @author AJ Keller (@pushtheworldllc)
*/
function getAccelDataArray(dataBuf) {
return new Promise(resolve => {
var accelData = [];
for (var i = 0; i < ACCEL_NUMBER_AXIS; i++) {
var index = i * 2;
accelData.push(sampleModule.interpret16bitAsInt32(dataBuf.slice(index, index + 2)) * SCALE_FACTOR_ACCEL);
}
resolve(accelData);
});
}
/**
* @description Takes a buffer filled with 24 bit signed integers from an OpenBCI device with gain settings in
* channelSettingsArray[index].gain and converts based on settings of ADS1299... spits out an
* array of floats in VOLTS
* @param dataBuf - Buffer with 24 bit signed integers, number of elements is same as channelSettingsArray.length * 3
* @param channelSettingsArray - The channel settings array, see OpenBCIConstants.channelSettingsArrayInit() for specs
* @returns {Promise} - Fulfilled with Array filled with floats for each channel's voltage in VOLTS
* @author AJ Keller (@pushtheworldllc)
*/
function getChannelDataArray(dataBuf, channelSettingsArray) {
return new Promise((resolve, reject) => {
if (!Array.isArray(channelSettingsArray)) reject('Error [getChannelDataArray]: Channel Settings must be an array!');
var channelData = [];
// Iterate through each object in the array
channelSettingsArray.forEach((channelSettingsObject, index) => {
if (!channelSettingsObject.hasOwnProperty('gain')) reject('Error [getChannelDataArray]: Invalid channel settings object at index ' + index);
if (!k.isNumber(channelSettingsObject.gain)) reject('Error [getChannelDataArray]: Property gain of channelSettingsObject not or type Number');
// Get scale factor
var scaleFactor = ADS1299_VREF / channelSettingsObject.gain / (Math.pow(2,23) - 1);
// Each number is 3 bytes, need to traverse index * 3 in the buffer
index *= 3;
// Convert the three byte signed integer and convert it
channelData.push(scaleFactor * sampleModule.interpret24bitAsInt32(dataBuf.slice(index, index + 3)));
});
resolve(channelData);
});
}
function getRawPacketType(stopByte) {
return stopByte & 0xF;
}
+224
Ver Arquivo
@@ -0,0 +1,224 @@
'use strict';
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var stream = require('stream');
var openBCISample = require('./openBCISample');
var k = openBCISample.k;
var now = require('performance-now');
function OpenBCISimulatorFactory() {
var factory = this;
var _options = {
samplerate: 250,
daisy: false,
verbose: false,
alpha: true,
lineNoise: '60Hz'
};
function OpenBCISimulator(portName, options) {
options = (typeof options !== 'function') && options || {};
var opts = {};
stream.Stream.call(this);
/** Configuring Options */
opts.sampleRate = options.sampleRate || options.samplerate || _options.samplerate;
opts.daisy = options.daisy || _options.daisy;
opts.lineNoise = options.lineNoise || options.linenoise || _options.lineNoise;
opts.alpha = options.alpha || _options.alpha;
opts.verbose = options.verbose || _options.verbose;
this.options = opts;
// Bools
this.connected = false;
this.sd = {
active:false,
startTime: 0
};
this.streaming = false;
// Buffers
this.buffer = new Buffer(500);
// Numbers
this.sampleNumber = -1; // So the first sample is 0
// Objects
this.time = {
current: 0,
start: now(),
loop: null,
ntp0: 0,
ntp1: 0,
ntp2: 0,
ntp3: 0
};
console.log('Simulator started at time: ' + this.time.start);
console.log('Time board has been running: ' + (now() - this.time.start));
// Strings
this.portName = portName || k.OBCISimulatorPortName;
// Call 'open'
setTimeout(() => {
console.log('Port name: ' + portName);
if (portName === k.OBCISimulatorPortName) {
this.emit('open');
this.connected = true;
} else {
var err = new Error('Serialport not open.');
this.emit('error',err);
}
}, 200);
}
// This allows us to use the emitter class freely outside of the module
util.inherits(OpenBCISimulator, stream.Stream);
OpenBCISimulator.prototype.flush = function() {
this.buffer.fill(0);
//if (this.options.verbose) console.log('flushed');
};
OpenBCISimulator.prototype.write = function(data,callback) {
switch (data[0]) {
case k.OBCIStreamStart:
if (!this.stream) this._startStream();
this.streaming = true;
break;
case k.OBCIStreamStop:
if (this.stream) clearInterval(this.stream); // Stops the stream
this.streaming = false;
break;
case k.OBCIMiscSoftReset:
if (this.stream) clearInterval(this.stream);
this.streaming = false;
this.emit('data', new Buffer('OpenBCI Board Simulator\nPush The World\nFirmware: v2$$$'));
break;
case k.OBCISDLogForHour1:
case k.OBCISDLogForHour2:
case k.OBCISDLogForHour4:
case k.OBCISDLogForHour12:
case k.OBCISDLogForHour24:
case k.OBCISDLogForMin5:
case k.OBCISDLogForMin15:
case k.OBCISDLogForMin30:
case k.OBCISDLogForSec14:
// If we are not streaming, then do verbose output
if (!this.streaming) {
this.emit('data', new Buffer('Wiring is correct and a card is present.\nCorresponding SD file OBCI_69.TXT\n$$$'));
}
this.sd.active = true;
this.sd.startTime = now();
break;
case k.OBCISDLogStop:
if (!this.streaming) {
if (this.SDLogActive) {
this.emit('data', new Buffer('Total Elapsed Time: ' + (now() - this.sd.startTime) + ' ms\n'));
this.emit('data', new Buffer('Max write time: ' + (Math.random()*500) + ' us\n'));
this.emit('data', new Buffer('Min write time: ' + (Math.random()*200) + ' us\n'));
this.emit('data', new Buffer('Overruns: 0\n$$$'));
} else {
this.emit('data', new Buffer('No open file to close\n$$$'));
}
}
this.SDLogActive = false;
break;
case k.OBCISyncClockStart:
setTimeout(() => {
if (this.options.verbose) console.log('Recieved sync command');
this._syncStart();
}, 10);
break;
case k.OBCISyncClockServerData:
this.time.ntp3 = this.time.current;
this._syncUp(data.slice(1));
break;
default:
break;
}
/** Handle Callback */
if (this.connected) {
callback(null,'Success!');
}
};
OpenBCISimulator.prototype.drain = function(callback) {
callback();
//if (this.options.verbose) console.log('drain');
};
OpenBCISimulator.prototype.close = function(callback) {
if (this.connected) {
this.emit('close');
}
this.connected = false;
//if (this.options.verbose) console.log('close');
callback();
};
OpenBCISimulator.prototype._startStream = function() {
var intervalInMS = 1000 / this.options.sampleRate;
if (intervalInMS < 2) intervalInMS = 2;
var generateSample = openBCISample.randomSample(k.OBCINumberOfChannelsDefault, k.OBCISampleRate250, this.options.alpha, this.options.lineNoise);
var getNewPacket = sampNumber => {
return openBCISample.convertSampleToPacket(generateSample(sampNumber));
};
this.stream = setInterval(() => {
this.emit('data', getNewPacket(this.sampleNumber));
this.sampleNumber++;
}, intervalInMS);
};
OpenBCISimulator.prototype._syncStart = function() {
this.time.ntp0 = now();
var buffer = new Buffer('$a$' + this.time.ntp0);
this.emit('data',buffer);
};
OpenBCISimulator.prototype._syncUp = function(data) {
// get the first number
console.log(data.length);
var halfwayPoint = (data.length / 2);
this.time.ntp1 = parseFloat(data.slice(0,halfwayPoint-1));
this.time.ntp2 = parseFloat(data.slice(halfwayPoint));
console.log('ntp1: ' + this.time.ntp1 + ' ntp2: ' + this.time.ntp2);
var timeSpentOnNetwork = this.time.ntp3 - this.time.ntp0 - (this.time.ntp2 - this.time.ntp1);
var transferTime = timeSpentOnNetwork / 2;
var trueTime = this.time.ntp2 + transferTime;
var delta = trueTime - this.time.ntp3;
console.log('Delta: ' + delta);
this.time.start += delta;
this.emit('data','Synced!' + '$$$');
};
factory.OpenBCISimulator = OpenBCISimulator;
}
util.inherits(OpenBCISimulatorFactory, EventEmitter);
module.exports = new OpenBCISimulatorFactory();
+12 -5
Ver Arquivo
@@ -1,11 +1,12 @@
{
"name": "openbci-sdk",
"version": "0.1.9",
"description": "A fully NodeJS based API for the OpenBCI board connecting to the hardware directly over serial",
"version": "0.3.8",
"description": "The official Node.js SDK for the OpenBCI Biosensor Board.",
"main": "openBCIBoard",
"scripts": {
"start": "node index",
"test": "mocha test"
"test": "mocha test",
"test-cov": "istanbul cover ./node_modules/mocha/bin/_mocha -- -R spec && codecov"
},
"keywords": [
"openbci",
@@ -15,7 +16,11 @@
"license": "MIT",
"dependencies": {
"gaussian": "^1.0.0",
"serialport": "^2.0.2"
"istanbul": "^0.4.2",
"performance-now": "^0.2.0",
"scientific-statistics": "^0.2.7",
"serialport": "3.1.2",
"sntp": "^2.0.0"
},
"directories": {
"test": "test"
@@ -23,9 +28,11 @@
"devDependencies": {
"chai": "^3.4.1",
"chai-as-promised": "^5.2.0",
"codecov": "^1.0.1",
"mocha": "^2.3.4",
"sandboxed-module": "^2.0.3",
"sinon": "^1.17.2"
"sinon": "^1.17.2",
"sinon-chai": "^2.8.0"
},
"repository": {
"type": "git",
-273
Ver Arquivo
@@ -1,273 +0,0 @@
/**
* Created by AJ Keller
* Date: 12/23/15
* Purpose: To unit test the OpenBCIBoard file
*/
var assert = require('assert');
var OpenBCIBoard = require('../openBCIBoard');
var OpenBCISample = OpenBCIBoard.OpenBCISample;
var k = OpenBCIBoard.OpenBCIConstants;
var chai = require('chai')
, expect = chai.expect
, should = chai.should();
var chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
var sampleSelf = function() {
return {
masterBuffer: {
buffer: new Buffer(3300),
positionRead:0,
positionWrite:0,
packetsIn:0,
packetsRead:0,
looseBytes:0
}
};
};
var sampleData;
var bciBoard;
var samplePacket = function () {
var byteSample = 0x45;
var buffy = new Buffer([0xA0,byteSample,0,0,1,0,0,2,0,0,3,0,0,4,0,0,5,0,0,6,0,0,7,0,0,8,0,0,0,1,0,2,0xC0]);
return buffy;
};
describe('OpenBCIBoard',function() {
describe('#_bufMerger',function() {
beforeEach(function() {
bciBoard = new OpenBCIBoard.OpenBCIBoard();
sampleData = [];
(function(){
for(var i = 0;i < 100;i++) {
var sample = samplePacket();
sample.sampleNumber = i;
sampleData[i] = sample;
}
}());
});
it('should write the buffer to the empty master buffer', function() {
bciBoard._bufMerger(sampleData[0]);
// test to see if buffers match
assert.equal(0,sampleData[0].compare(bciBoard.masterBuffer.buffer.slice(0, k.OBCIPacketSize)));
// test to make sure the write position was moved
assert.equal(k.OBCIPacketSize, bciBoard.masterBuffer.positionWrite);
// test to make sure we read in the right number of packets
assert.equal(bciBoard.masterBuffer.packetsIn,1);
// test to make sure there are no loose bytes
assert.equal(bciBoard.masterBuffer.looseBytes,0);
});
it('should write the tiny buffer to the empty master buffer, but not record a packet in', function() {
var tinyBufferSize = 10; //tiny because its smaller than a packet
var tinyBuffer = sampleData[0].slice(0,tinyBufferSize);
bciBoard._bufMerger(tinyBuffer);
// test to see if buffers match
assert.equal(0,tinyBuffer.compare(bciBoard.masterBuffer.buffer.slice(0, tinyBufferSize)));
// test to make sure the write position was moved
assert.equal(tinyBufferSize, bciBoard.masterBuffer.positionWrite);
// test to make sure we read in the right number of packets
assert.equal(bciBoard.masterBuffer.packetsIn,0);
// test to make sure there are 10 loose bytes
assert.equal(bciBoard.masterBuffer.looseBytes,tinyBufferSize);
});
it('should write the multi packet plus loose byte buffer to the empty master buffer', function() {
var tinyBufferSize = 10; //tiny because its smaller than a packet
var tinyBuffer = sampleData[0].slice(0,tinyBufferSize);
var buffers = [sampleData[0],sampleData[1],sampleData[2],tinyBuffer];
var totalLength = k.OBCIPacketSize * 3 + tinyBufferSize;
var multiPacketBuffer = Buffer.concat(buffers,totalLength);
bciBoard._bufMerger(multiPacketBuffer);
// test to see if buffers match
assert.equal(0,multiPacketBuffer.compare(bciBoard.masterBuffer.buffer.slice(0, totalLength)));
// test to make sure the write position was moved
assert.equal(totalLength, bciBoard.masterBuffer.positionWrite);
// test to make sure we read in the right number of packets (shall be three)
assert.equal(bciBoard.masterBuffer.packetsIn,3);
// test to make sure there are 10 loose bytes
assert.equal(bciBoard.masterBuffer.looseBytes,tinyBufferSize);
});
it('should use looseBytes in to write the multi packet to the master buffer and record an extra packet in', function() {
var tinyBufferSize = 10; //tiny because its smaller than a packet
var tinyBuffer = sampleData[0].slice(0,tinyBufferSize);
var hackedBufferSize = k.OBCIPacketSize - tinyBufferSize;
var hackedBuffer = sampleData[0].slice(tinyBufferSize);
bciBoard.masterBuffer.looseBytes = tinyBufferSize;
var buffers = [hackedBuffer,sampleData[0],sampleData[1],sampleData[2]];
var totalLength = k.OBCIPacketSize * 3 + hackedBufferSize;
var multiPacketBuffer = Buffer.concat(buffers,totalLength);
bciBoard._bufMerger(multiPacketBuffer);
// test to see if buffers match
assert.equal(0,multiPacketBuffer.compare(bciBoard.masterBuffer.buffer.slice(0, totalLength)));
// test to make sure the write position was moved
assert.equal(totalLength, bciBoard.masterBuffer.positionWrite);
// test to make sure we read in the right number of packets (shall be three)
assert.equal(bciBoard.masterBuffer.packetsIn,4);
// test to make sure there are 10 loose bytes
assert.equal(bciBoard.masterBuffer.looseBytes,0);
});
it('should write in the position of the masterBufferPositionWrite', function() {
var originalWritePosition = 69;
bciBoard.masterBuffer.positionWrite = originalWritePosition;
bciBoard._bufMerger(sampleData[0]);
// test to see if the master buffer contains the correct data
assert.equal(0,sampleData[0].compare(bciBoard.masterBuffer.buffer.slice(originalWritePosition, originalWritePosition + k.OBCIPacketSize)));
// test to see if the write position was moved properly
assert.equal(bciBoard.masterBuffer.positionWrite,originalWritePosition + k.OBCIPacketSize);
// test to make sure we read in the right number of packets
assert.equal(bciBoard.masterBuffer.packetsIn,1);
// test to make sure there are no loose bytes
assert.equal(bciBoard.masterBuffer.looseBytes,0);
});
it('should wrap the input buffer around the master buffer',function() {
var spaceRemaingInMasterBuffer = 15;
var originalWritePosition = k.OBCIMasterBufferSize - spaceRemaingInMasterBuffer;
bciBoard.masterBuffer.positionWrite = originalWritePosition;
bciBoard._bufMerger(sampleData[0]);
// test to see that the end of master buffer contains half of sample
assert.equal(0,sampleData[0].slice(0,spaceRemaingInMasterBuffer).compare(bciBoard.masterBuffer.buffer.slice(originalWritePosition)));
// test to see that the beginning of master buffer contains the second half of sampleData[0]
assert.equal(0,sampleData[0].slice(spaceRemaingInMasterBuffer).compare(bciBoard.masterBuffer.buffer.slice(0,sampleData[0].byteLength - spaceRemaingInMasterBuffer)));
// test to make sure the write position was moved correctly
assert.equal(bciBoard.masterBuffer.positionWrite,sampleData[0].byteLength - spaceRemaingInMasterBuffer);
// test to make sure we read in the right number of packets
assert.equal(bciBoard.masterBuffer.packetsIn,1);
// test to make sure there are no loose bytes
assert.equal(bciBoard.masterBuffer.looseBytes,0);
});
});
describe('#_bufPacketStripper', function() {
beforeEach(function() {
bciBoard = new OpenBCIBoard.OpenBCIBoard();
sampleData = [];
(function(){
for(var i = 0;i < 100;i++) {
var sample = samplePacket();
sample.sampleNumber = i;
sampleData[i] = sample;
}
}());
});
it('should remove a packet from the master buffer', function() {
var buffers = [sampleData[0],sampleData[1],sampleData[2]];
var totalLength = k.OBCIPacketSize * 3;
var multiPacketBuffer = Buffer.concat(buffers,totalLength);
//console.log(multiPacketBuffer);
bciBoard._bufMerger(multiPacketBuffer);
// run through three iterations of stripping packets
for (var i = 0; i < 3; i++) {
var rawPacket = bciBoard._bufPacketStripper();
var sample = OpenBCISample.convertPacketToSample(rawPacket);
//console.log(OpenBCISample.debugPrettyPrint(sample));
//console.log('Sample ' + i + ' has sample number ' + sample.sampleNumber);
assert.equal(sample.sampleNumber,69);
}
});
it('should remove a packet from the master buffer, even at wrap..', function() {
var buffers = [sampleData[0],sampleData[1],sampleData[2]];
var totalLength = k.OBCIPacketSize * 3;
bciBoard.masterBuffer.positionWrite = k.OBCIMasterBufferSize - 10;
bciBoard.masterBuffer.positionRead = bciBoard.masterBuffer.positionWrite;
var multiPacketBuffer = Buffer.concat(buffers,totalLength);
bciBoard._bufMerger(multiPacketBuffer);
// run through three iterations of stripping packets
for (var i = 0; i < 3; i++) {
var rawPacket = bciBoard._bufPacketStripper();
var sample = OpenBCISample.convertPacketToSample(rawPacket);
assert(sample.sampleNumber,i);
}
});
});
describe('#_bufAlign',function() {
beforeEach(function() {
bciBoard = new OpenBCIBoard.OpenBCIBoard();
sampleData = [];
(function(){
for(var i = 0;i < 100;i++) {
var sample = samplePacket();
sample.sampleNumber = i;
sampleData[i] = sample;
}
}());
});
it('should fix a buffer', function() {
var buffers = [sampleData[0],sampleData[1],sampleData[2]];
var totalLength = k.OBCIPacketSize * 3;
var multiPacketBuffer = Buffer.concat(buffers,totalLength);
//console.log(multiPacketBuffer);
var bytesToHackOffSample = 10;
var expectedNewPositionRead = k.OBCIPacketSize - bytesToHackOffSample;
bciBoard._bufMerger(multiPacketBuffer.slice(10,totalLength));
bciBoard._bufAlign();
assert(expectedNewPositionRead,bciBoard.masterBuffer.positionRead);
});
it('should wrap & then fix a buffer', function() {
var buffers = [sampleData[0],sampleData[1],sampleData[2]];
var totalLength = k.OBCIPacketSize * 3;
var multiPacketBuffer = Buffer.concat(buffers,totalLength);
var bytesFromEndOfBuffer = 5;
bciBoard.masterBuffer.positionRead = k.OBCIMasterBufferSize - bytesFromEndOfBuffer;
var bytesToHackOffSample = 10;
var expectedNewPositionRead = bytesToHackOffSample - bytesFromEndOfBuffer;
bciBoard._bufMerger(multiPacketBuffer.slice(10,totalLength));
bciBoard._bufAlign();
assert(expectedNewPositionRead,bciBoard.masterBuffer.positionRead);
});
});
});
function debugPrintBufferCompare(buf1,buf2) {
console.log('Comparing:\n\tBuffer 1\n\t\t' + buf1.toString('hex') + '\n\tBuffer 2\n\t\t' + buf2.toString('hex'));
}
+201 -139
Ver Arquivo
@@ -323,6 +323,86 @@ describe('OpenBCIConstants', function() {
assert.equal('j', k.OBCISDLogStop);
});
});
describe('SD card string Commands',function() {
it('logs for 1 hour', function() {
assert.equal('1hour', k.OBCIStringSDHour1);
});
it('logs for 2 hours', function() {
assert.equal('2hour', k.OBCIStringSDHour2);
});
it('logs for 4 hours', function() {
assert.equal('4hour', k.OBCIStringSDHour4);
});
it('logs for 12 hours', function() {
assert.equal('12hour', k.OBCIStringSDHour12);
});
it('logs for 24 hours', function() {
assert.equal('24hour', k.OBCIStringSDHour24);
});
it('logs for 5 minutes', function() {
assert.equal('5min', k.OBCIStringSDMin5);
});
it('logs for 15 minutes', function() {
assert.equal('15min', k.OBCIStringSDMin15);
});
it('logs for 30 minutes', function() {
assert.equal('30min', k.OBCIStringSDMin30);
});
it('logs for 14 seconds', function() {
assert.equal('14sec', k.OBCIStringSDSec14);
});
});
describe('#sdSettingForString',function() {
it('correct command for 1 hour', function() {
var expectation = k.OBCISDLogForHour1;
var result = k.sdSettingForString('1hour');
return expect(result).to.eventually.equal(expectation);
});
it('correct command for 2 hour', function() {
var expectation = k.OBCISDLogForHour2;
var result = k.sdSettingForString('2hour');
return expect(result).to.eventually.equal(expectation);
});
it('correct command for 4 hour', function() {
var expectation = k.OBCISDLogForHour4;
var result = k.sdSettingForString('4hour');
return expect(result).to.eventually.equal(expectation);
});
it('correct command for 12 hour', function() {
var expectation = k.OBCISDLogForHour12;
var result = k.sdSettingForString('12hour');
return expect(result).to.eventually.equal(expectation);
});
it('correct command for 24 hour', function() {
var expectation = k.OBCISDLogForHour24;
var result = k.sdSettingForString('24hour');
return expect(result).to.eventually.equal(expectation);
});
it('correct command for 5 min', function() {
var expectation = k.OBCISDLogForMin5;
var result = k.sdSettingForString('5min');
return expect(result).to.eventually.equal(expectation);
});
it('correct command for 15 min', function() {
var expectation = k.OBCISDLogForMin15;
var result = k.sdSettingForString('15min');
return expect(result).to.eventually.equal(expectation);
});
it('correct command for 30 min', function() {
var expectation = k.OBCISDLogForMin30;
var result = k.sdSettingForString('30min');
return expect(result).to.eventually.equal(expectation);
});
it('correct command for 14 seconds', function() {
var expectation = k.OBCISDLogForSec14;
var result = k.sdSettingForString('14sec');
return expect(result).to.eventually.equal(expectation);
});
it('Invalid command request', function() {
var result = k.sdSettingForString('taco');
return expect(result).to.be.rejected;
});
});
describe('Stream Data Commands',function() {
it('starts',function () {
assert.equal('b', k.OBCIStreamStart);
@@ -636,7 +716,7 @@ describe('OpenBCIConstants', function() {
//'channel 1, power on, gain 24, inputType normal, bias include, srb2 connect, srb1 dissconnect'
describe('channel input selection works', function() {
//this.timeout(5000);
it('channel 2', function() {
it('channel 2', function(done) {
k.getChannelSetter(2,false,24,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[1].should.equal('2');
done();
@@ -644,7 +724,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('channel 5', function() {
it('channel 5', function(done) {
k.getChannelSetter(5,false,24,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[1].should.equal('5');
done();
@@ -652,7 +732,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('channel 9', function() {
it('channel 9', function(done) {
k.getChannelSetter(9,false,24,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[1].should.equal('Q');
done();
@@ -660,7 +740,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('channel 15', function() {
it('channel 15', function(done) {
k.getChannelSetter(15,false,24,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[1].should.equal('U');
done();
@@ -668,23 +748,15 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('Invalid channel selection', function() {
k.getChannelSetter(0,false,24,'normal',true,true,false).then(function(arrayOfCommands) {
done('Should have rejected promise');
}, function(err) {
done(err);
});
it('Invalid channel selection', function(done) {
k.getChannelSetter(0,false,24,'normal',true,true,false).should.be.rejected.and.notify(done);
});
it('Invalid type', function() {
k.getChannelSetter('0',false,24,'normal',true,true,false).then(function(arrayOfCommands) {
done('Should have rejected promise');
}, function(err) {
done(err);
});
it('Invalid type', function(done) {
k.getChannelSetter('0',false,24,'normal',true,true,false).should.be.rejected.and.notify(done);
});
});
describe('power selection works', function() {
it('on', function() {
it('on', function(done) {
k.getChannelSetter(1,false,24,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[2].should.equal('0');
done();
@@ -692,7 +764,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('off', function() {
it('off', function(done) {
k.getChannelSetter(1,true,24,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[2].should.equal('1');
done();
@@ -700,16 +772,12 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('Invalid type', function() {
k.getChannelSetter(1,'taco',24,'normal',true,true,false).then(function(arrayOfCommands) {
done('Should have rejected promise');
}, function(err) {
done(err);
});
it('Invalid type', function(done) {
k.getChannelSetter(1,'taco',24,'normal',true,true,false).should.be.rejected.and.notify(done);
});
});
describe('gain selection works', function() {
it('1x', function() {
it('1x', function(done) {
k.getChannelSetter(1,false,1,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[3].should.equal('0');
done();
@@ -717,7 +785,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('2x', function() {
it('2x', function(done) {
k.getChannelSetter(1,false,2,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[3].should.equal('1');
done();
@@ -725,63 +793,55 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('4x', function() {
it('4x', function(done) {
k.getChannelSetter(1,false,4,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[3].should.equal('2');
done();
}, function(err) {
done(err);
});
});
it('6x', function(done) {
k.getChannelSetter(1,false,6,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[3].should.equal('3');
done();
}, function(err) {
done(err);
});
});
it('6x', function() {
k.getChannelSetter(1,false,6,'normal',true,true,false).then(function(arrayOfCommands) {
it('8x', function(done) {
k.getChannelSetter(1,false,8,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[3].should.equal('4');
done();
}, function(err) {
done(err);
});
});
it('8x', function() {
k.getChannelSetter(1,false,8,'normal',true,true,false).then(function(arrayOfCommands) {
it('12x', function(done) {
k.getChannelSetter(1,false,12,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[3].should.equal('5');
done();
}, function(err) {
done(err);
});
});
it('12x', function() {
k.getChannelSetter(1,false,12,'normal',true,true,false).then(function(arrayOfCommands) {
it('24x', function(done) {
k.getChannelSetter(1,false,24,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[3].should.equal('6');
done();
}, function(err) {
done(err);
});
});
it('24x', function() {
k.getChannelSetter(1,false,24,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[3].should.equal('7');
done();
}, function(err) {
done(err);
});
it('Invalid type', function(done) {
k.getChannelSetter(1,false,'24','normal',true,true,false).should.be.rejected.and.notify(done);
});
it('Invalid type', function() {
k.getChannelSetter(1,false,'24','normal',true,true,false).then(function(arrayOfCommands) {
done('Should have rejected promise');
}, function(err) {
done(err);
});
});
it('Invalid gain setting', function() {
k.getChannelSetter(1,false,5,'normal',true,true,false).then(function(arrayOfCommands) {
done('Should have rejected promise');
}, function(err) {
done(err);
});
it('Invalid gain setting', function(done) {
k.getChannelSetter(1,false,5,'normal',true,true,false).should.be.rejected.and.notify(done);
});
});
describe('input type', function() {
it('normal', function() {
it('normal', function(done) {
k.getChannelSetter(1,false,24,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[4].should.equal('0');
done();
@@ -789,7 +849,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('shorted', function() {
it('shorted', function(done) {
k.getChannelSetter(1,false,24,'shorted',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[4].should.equal('1');
done();
@@ -797,7 +857,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('biasMethod', function() {
it('biasMethod', function(done) {
k.getChannelSetter(1,false,24,'biasMethod',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[4].should.equal('2');
done();
@@ -805,7 +865,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('mvdd', function() {
it('mvdd', function(done) {
k.getChannelSetter(1,false,24,'mvdd',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[4].should.equal('3');
done();
@@ -813,7 +873,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('temp', function() {
it('temp', function(done) {
k.getChannelSetter(1,false,24,'temp',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[4].should.equal('4');
done();
@@ -821,15 +881,15 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('testsig', function() {
k.getChannelSetter(1,false,24,'testsig',true,true,false).then(function(arrayOfCommands) {
it('testsig', function(done) {
k.getChannelSetter(1,false,24,'testSig',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[4].should.equal('5');
done();
}, function(err) {
done(err);
});
});
it('biasDrp', function() {
it('biasDrp', function(done) {
k.getChannelSetter(1,false,24,'biasDrp',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[4].should.equal('6');
done();
@@ -837,7 +897,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('biasDrn', function() {
it('biasDrn', function(done) {
k.getChannelSetter(1,false,24,'biasDrn',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[4].should.equal('7');
done();
@@ -845,23 +905,15 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('Invalid setting', function() {
k.getChannelSetter(1,false,24,'taco',true,true,false).then(function(arrayOfCommands) {
done('Should have rejected promise');
}, function(err) {
done(err);
});
it('Invalid setting', function(done) {
k.getChannelSetter(1,false,24,'taco',true,true,false).should.be.rejected.and.notify(done);
});
it('Invalid type', function() {
k.getChannelSetter(1,false,24,1,true,true,false).then(function(arrayOfCommands) {
done('Should have rejected promise');
}, function(err) {
done(err);
});
it('Invalid type', function(done) {
k.getChannelSetter(1,false,24,1,true,true,false).should.be.rejected.and.notify(done);
});
});
describe('bias selection works', function() {
it('Include', function() {
it('Include', function(done) {
k.getChannelSetter(1,false,24,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[5].should.equal('1');
done();
@@ -869,7 +921,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('Remove', function() {
it('Remove', function(done) {
k.getChannelSetter(1,false,24,'normal',false,true,false).then(function(arrayOfCommands) {
arrayOfCommands[5].should.equal('0');
done();
@@ -877,16 +929,12 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('Invalid type', function() {
k.getChannelSetter(1,false,24,'normal','taco',true,false).then(function(arrayOfCommands) {
done('Should have rejected promise');
}, function(err) {
done(err);
});
it('Invalid type', function(done) {
k.getChannelSetter(1,false,24,'normal','taco',true,false).should.be.rejected.and.notify(done);
});
});
describe('SRB2 selection works', function() {
it('Connect', function() {
it('Connect', function(done) {
k.getChannelSetter(1,false,24,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[6].should.equal('1');
done();
@@ -894,7 +942,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('Disconnect', function() {
it('Disconnect', function(done) {
k.getChannelSetter(1,false,24,'normal',true,false,false).then(function(arrayOfCommands) {
arrayOfCommands[6].should.equal('0');
done();
@@ -902,16 +950,12 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('Invalid type', function() {
k.getChannelSetter(1,false,24,'normal',true,'taco',false).then(function(arrayOfCommands) {
done('Should have rejected promise');
}, function(err) {
done(err);
});
it('Invalid type', function(done) {
k.getChannelSetter(1,false,24,'normal',true,'taco',false).should.be.rejected.and.notify(done);
});
});
describe('SRB1 selection works', function() {
it('Connect', function() {
it('Connect', function(done) {
k.getChannelSetter(1,false,24,'normal',true,true,true).then(function(arrayOfCommands) {
arrayOfCommands[7].should.equal('1');
done();
@@ -919,7 +963,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('Disconnect', function() {
it('Disconnect', function(done) {
k.getChannelSetter(1,false,24,'normal',true,true,false).then(function(arrayOfCommands) {
arrayOfCommands[7].should.equal('0');
done();
@@ -927,18 +971,51 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('Invalid type', function() {
k.getChannelSetter(1,false,24,'normal',true,true,'taco').then(function(arrayOfCommands) {
done('Should have rejected promise');
}, function(err) {
done(err);
});
it('Invalid type', function(done) {
k.getChannelSetter(1, false, 24, 'normal', true, true, 'taco').should.be.rejected.and.notify(done);
});
});
});
describe('#getChannelSetter', function() {
describe('#getTestSignalCommand', function() {
it('ground', function() {
var expectation = '0';
var result = k.getTestSignalCommand('ground');
return expect(result).to.eventually.equal(expectation);
});
it('dc', function() {
var expectation = 'p';
var result = k.getTestSignalCommand('dc');
return expect(result).to.eventually.equal(expectation);
});
it('Pulse 1x Fast', function() {
var expectation = '=';
var result = k.getTestSignalCommand('pulse1xFast');
return expect(result).to.eventually.equal(expectation);
});
it('Pulse 1x Slow', function() {
var expectation = '-';
var result = k.getTestSignalCommand('pulse1xSlow');
return expect(result).to.eventually.equal(expectation);
});
it('Pulse 2x Fast', function() {
var expectation = ']';
var result = k.getTestSignalCommand('pulse2xFast');
return expect(result).to.eventually.equal(expectation);
});
it('Pulse 2x Slow', function() {
var expectation = '[';
var result = k.getTestSignalCommand('pulse2xSlow');
return expect(result).to.eventually.equal(expectation);
});
it('none', function() {
var expectation = 'd';
var result = k.getTestSignalCommand('none');
return expect(result).to.eventually.equal(expectation);
});
});
describe('#getImpedanceSetter', function() {
describe('channel input selection works', function () {
it('channel 2', function () {
it('channel 2', function (done) {
k.getImpedanceSetter(2, false, false).then(function (arrayOfCommands) {
arrayOfCommands[1].should.equal('2');
done();
@@ -946,7 +1023,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('channel 5', function () {
it('channel 5', function (done) {
k.getImpedanceSetter(5, false, false).then(function (arrayOfCommands) {
arrayOfCommands[1].should.equal('5');
done();
@@ -954,7 +1031,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('channel 9', function () {
it('channel 9', function (done) {
k.getImpedanceSetter(9, false, false).then(function (arrayOfCommands) {
arrayOfCommands[1].should.equal('Q');
done();
@@ -962,7 +1039,7 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('channel 15', function () {
it('channel 15', function (done) {
k.getImpedanceSetter(15, false, false).then(function (arrayOfCommands) {
arrayOfCommands[1].should.equal('U');
done();
@@ -970,82 +1047,67 @@ describe('OpenBCIConstants', function() {
done(err);
});
});
it('Invalid channel selection', function () {
k.getImpedanceSetter(0, false, false).then(function (arrayOfCommands) {
done('Should have rejected promise');
}, function (err) {
done(err);
});
it('Invalid channel selection', function (done) {
k.getImpedanceSetter(0, false, false).should.be.rejected.and.notify(done);
});
it('Invalid type', function () {
k.getImpedanceSetter('1', false, false).then(function (arrayOfCommands) {
done('Should have rejected promise');
}, function (err) {
done(err);
});
it('Invalid type', function (done) {
k.getImpedanceSetter('1', false, false).should.be.rejected.and.notify(done);
});
});
describe('P Input selection works', function() {
describe('P Input selection works', function(done) {
it('Test Signal Applied', function() {
k.getImpedanceSetter(0, true, false).then(function (arrayOfCommands) {
k.getImpedanceSetter(1, true, false).then(function (arrayOfCommands) {
arrayOfCommands[2].should.equal('1');
done();
}, function(err) {
done(err);
});
});
it('Test Signal Not Applied', function() {
k.getImpedanceSetter(0, false, false).then(function (arrayOfCommands) {
it('Test Signal Not Applied', function(done) {
k.getImpedanceSetter(1, false, false).then(function (arrayOfCommands) {
console.log('\n\n\narray: ' + arrayOfCommands + '\n\n\n');
arrayOfCommands[2].should.equal('0');
done();
}, function(err) {
done(err);
});
});
it('Invalid type', function() {
k.getImpedanceSetter(0, 'taco', false).then(function (arrayOfCommands) {
done('Should have rejected promise');
}, function(err) {
done(err);
});
it('Invalid type', function(done) {
k.getImpedanceSetter(1, 'taco', false).should.be.rejected.and.notify(done);
});
});
describe('N Input selection works', function() {
describe('N Input selection works', function(done) {
it('Test Signal Applied', function() {
k.getImpedanceSetter(0, true, false).then(function (arrayOfCommands) {
k.getImpedanceSetter(1, true, false).then(function (arrayOfCommands) {
arrayOfCommands[3].should.equal('1');
done();
}, function(err) {
done(err);
});
});
it('Test Signal Not Applied', function() {
k.getImpedanceSetter(0, false, false).then(function (arrayOfCommands) {
it('Test Signal Not Applied', function(done) {
k.getImpedanceSetter(1, false, false).then(function (arrayOfCommands) {
arrayOfCommands[3].should.equal('0');
done();
}, function(err) {
done(err);
});
});
it('Invalid type', function() {
k.getImpedanceSetter(0, false, 'taco').then(function (arrayOfCommands) {
done('Should have rejected promise');
}, function(err) {
done(err);
});
it('Invalid type', function(done) {
k.getImpedanceSetter(1, false, 'taco').should.be.rejected.and.notify(done);
});
});
describe('Prefix and postfix commands work', function() {
it('Set', function() {
k.getImpedanceSetter(0, true, true).then(function (arrayOfCommands) {
it('Set', function(done) {
k.getImpedanceSetter(1, true, true).then(function (arrayOfCommands) {
arrayOfCommands[0].should.equal('z');
done();
}, function(err) {
done(err);
});
});
it('Latch', function() {
k.getImpedanceSetter(0, true, true).then(function (arrayOfCommands) {
it('Latch', function(done) {
k.getImpedanceSetter(1, true, true).then(function (arrayOfCommands) {
arrayOfCommands[4].should.equal('Z');
done();
}, function(err) {
+263 -49
Ver Arquivo
@@ -2,13 +2,18 @@
* Created by ajk on 12/15/15.
*/
var assert = require('assert');
var OpenBCISample = require('../openBCISample');
var chai = require('chai')
, expect = chai.expect
, should = chai.should();
var chaiAsPromised = require("chai-as-promised");
var openBCISample = require('../openBCISample');
var sinon = require('sinon');
var chai = require('chai'),
should = chai.should(),
openBCIBoard = require('../openBCIBoard');
var k = OpenBCISample.k;
var chaiAsPromised = require("chai-as-promised");
var sinonChai = require("sinon-chai");
chai.use(chaiAsPromised);
chai.use(sinonChai);
var k = openBCISample.k;
var samplePacket = function () {
var byteSample = 0x45;
@@ -24,106 +29,315 @@ var sampleBuf = samplePacket();
describe('OpenBCISample',function() {
describe('#convertPacketToSample', function() {
it('should have the correct start byte', function() {
var sample = OpenBCISample.convertPacketToSample(sampleBuf);
assert.equal(k.OBCIByteStart,sample.startByte);
});
it('should have the correct stop byte', function() {
var sample = OpenBCISample.convertPacketToSample(sampleBuf);
assert.equal(k.OBCIByteStop,sample.stopByte);
describe('openBCISample',function() {
var channelScaleFactor = 4.5 / 24 / (Math.pow(2,23) - 1);
describe('#parseRawPacket', function() {
it('should fulfill promise', function() {
return openBCISample.parseRawPacket(sampleBuf).should.be.fulfilled;
});
it('should have the correct sample number', function() {
var sample = OpenBCISample.convertPacketToSample(sampleBuf);
assert.equal(0x45,sample.sampleNumber);
return openBCISample.parseRawPacket(sampleBuf).should.eventually.have.property('sampleNumber').equal(0x45);
});
it('all the channels should have the same number value as their index * scaleFactor', function() {
var sample = OpenBCISample.convertPacketToSample(sampleBuf);
for(var i = 1;i <= 8;i++) {
assert.equal(sample.channelData[i],OpenBCISample.scaleFactorChannel * i);
}
it('all the channels should have the same number value as their (index + 1) * scaleFactor', function(done) {
openBCISample.parseRawPacket(sampleBuf) // sampleBuf has its channel number for each 3 byte integer. See line 20...
.then(sampleObject => {
// So parse the sample we created and each value resulting from the channelData array should
// be its index + 1 (i.e. channel number) multiplied by the channel scale factor set by the
// ADS1299 for a gain of 24 (default)
sampleObject.channelData.forEach((channelValue, index) => {
assert.equal(channelValue,channelScaleFactor * (index + 1),'Channel ' + index + ' does not compute correctly');
});
done();
})
.catch(err => done(err));
});
it('all the auxs should have the same number value as their index * scaleFactor', function() {
var sample = OpenBCISample.convertPacketToSample(sampleBuf);
for(var i = 0;i < 3;i++) {
assert.equal(sample.auxData[i],OpenBCISample.scaleFactorAux * i);
}
it('all the auxs should have the same number value as their index * scaleFactor', function(done) {
openBCISample.parseRawPacket(sampleBuf)
.then(sampleObject => {
sampleObject.auxData.forEach((auxValue, index) => {
assert.equal(auxValue,openBCISample.scaleFactorAux * index,'Aux ' + index + ' does not compute correctly');
});
done();
})
.catch(err => done(err));
});
it('check to see if negative numbers work on channel data',function() {
it('check to see if negative numbers work on channel data',function(done) {
var temp = samplePacket();
//console.log(temp);
var taco = new Buffer([0x81]);
taco.copy(temp,2);
//console.log(temp);
var sample = OpenBCISample.convertPacketToSample(sampleBuf);
assert(sample.channelData[1], -8323071 * OpenBCISample.scaleFactorChannel);
openBCISample.parseRawPacket(temp)
.then(sampleObject => {
assert.equal(sampleObject.channelData[0],channelScaleFactor * -8323071,'Negative numbers not working correctly');
done();
})
.catch(err => done(err));
});
it('check to see if negative numbers work on aux data',function() {
var temp = samplePacket();
//console.log(temp);
var taco = new Buffer([0x81]);
taco.copy(temp,26);
//console.log(temp);
var sample = OpenBCISample.convertPacketToSample(temp);
//OpenBCISample.debugPrettyPrint(sample);
assert.equal(sample.auxData[0],-32512 * OpenBCISample.scaleFactorAux);
openBCISample.parseRawPacket(temp)
.then(sampleObject => {
sampleObject.auxData[0].should.be.approximately(-32512 * openBCISample.scaleFactorAux,1);
done();
})
.catch(err => done(err));
});
describe('#errorConditions', function() {
it('send non data buffer', function(done) {
openBCISample.parseRawPacket(1).should.be.rejected.and.notify(done)
//var sample = openBCISample.convertPacketToSample(1);
//assert.equal(undefined,sample);
});
it('bad start byte', function(done) {
var temp = samplePacket();
temp[0] = 69;
openBCISample.parseRawPacket(temp).should.be.rejected.and.notify(done);
//var sample = openBCISample.convertPacketToSample(temp);
//assert.equal(undefined,sample);
});
it('wrong number of bytes', function(done) {
openBCISample.parseRawPacket(new Buffer(5)).should.be.rejected.and.notify(done);
//var sample = openBCISample.convertPacketToSample(new Buffer(5));
//assert.equal(undefined,sample);
});
it('undefined', function(done) {
openBCISample.parseRawPacket().should.be.rejected.and.notify(done);
//var sample = openBCISample.convertPacketToSample();
//assert.equal(undefined,sample);
});
});
});
describe('#convertSampleToPacket', function() {
var generateSample = openBCISample.randomSample(k.OBCINumberOfChannelsDefault, k.OBCISampleRate250);
// get new sample
var newSample = generateSample(0);
// try to convert to packet
var packetBuffer = openBCISample.convertSampleToPacket(newSample);
it('should have correct start byte', function() {
packetBuffer[0].should.equal(k.OBCIByteStart,'confirming start byte');
});
it('should have correct stop byte', function() {
packetBuffer[k.OBCIPacketSize - 1].should.equal(k.OBCIByteStop,'confirming stop byte');
});
it('should have correct sample number', function() {
packetBuffer[1].should.equal(1,'confirming sample number is 1 more than 0');
});
it('should convert channel data to binary', function(done) {
openBCISample.parseRawPacket(packetBuffer)
.then(sample => {
for(var i = 0; i < k.OBCINumberOfChannelsDefault; i++) {
sample.channelData[i].should.be.approximately(newSample.channelData[i],0.001);
}
done();
})
.catch(err => done(err));
//var sample = openBCISample.convertPacketToSample(packetBuffer);
});
it('should convert aux data to binary', function(done) {
openBCISample.parseRawPacket(packetBuffer)
.then(sample => {
for(var i = 0; i < 3; i++) {
sample.auxData[i].should.be.approximately(newSample.auxData[i],0.001);
}
done();
})
.catch(err => done(err));
//var sample = openBCISample.convertPacketToSample(packetBuffer);
});
});
describe('#interpret24bitAsInt32', function() {
it('converts a small positive number', function() {
var buf1 = new Buffer([0x00,0x06,0x90]); // 0x000690 === 1680
var num = OpenBCISample.interpret24bitAsInt32(buf1);
var num = openBCISample.interpret24bitAsInt32(buf1);
assert.equal(num,1680);
});
it('converts a large positive number', function() {
var buf1 = new Buffer([0x02,0xC0,0x01]); // 0x02C001 === 180225
var num = OpenBCISample.interpret24bitAsInt32(buf1);
var num = openBCISample.interpret24bitAsInt32(buf1);
assert.equal(num,180225);
});
it('converts a small negative number', function() {
var buf1 = new Buffer([0xFF,0xFF,0xFF]); // 0xFFFFFF === -1
var num = OpenBCISample.interpret24bitAsInt32(buf1);
assert.equal(num,-1);
var num = openBCISample.interpret24bitAsInt32(buf1);
num.should.be.approximately(-1,1);
});
it('converts a large negative number', function() {
var buf1 = new Buffer([0x81,0xA1,0x01]); // 0x81A101 === -8281855
var num = OpenBCISample.interpret24bitAsInt32(buf1);
assert.equal(num,-8281855);
var num = openBCISample.interpret24bitAsInt32(buf1);
num.should.be.approximately(-8281855,1);
});
});
describe('#interpret16bitAsInt32', function() {
it('converts a small positive number', function() {
var buf1 = new Buffer([0x06,0x90]); // 0x0690 === 1680
var num = OpenBCISample.interpret16bitAsInt32(buf1);
var num = openBCISample.interpret16bitAsInt32(buf1);
assert.equal(num,1680);
});
it('converts a large positive number', function() {
var buf1 = new Buffer([0x02,0xC0]); // 0x02C0 === 704
var num = OpenBCISample.interpret16bitAsInt32(buf1);
var num = openBCISample.interpret16bitAsInt32(buf1);
assert.equal(num,704);
});
it('converts a small negative number', function() {
var buf1 = new Buffer([0xFF,0xFF]); // 0xFFFF === -1
var num = OpenBCISample.interpret16bitAsInt32(buf1);
var num = openBCISample.interpret16bitAsInt32(buf1);
assert.equal(num,-1);
});
it('converts a large negative number', function() {
var buf1 = new Buffer([0x81,0xA1]); // 0x81A1 === -32351
var num = OpenBCISample.interpret16bitAsInt32(buf1);
var num = openBCISample.interpret16bitAsInt32(buf1);
assert.equal(num,-32351);
});
});
describe('#floatTo3ByteBuffer', function() {
it('converts random floats to a 3-byte buffer', function() {
var generateSample = openBCISample.randomSample(k.OBCINumberOfChannelsDefault, k.OBCISampleRate250);
var newSample = generateSample(0);
for (var i = 0; i < k.OBCINumberOfChannelsDefault; i++) {
var buff = openBCISample.floatTo3ByteBuffer(newSample.channelData[i]);
var num = openBCISample.interpret24bitAsInt32(buff);
num = num * channelScaleFactor;
num.should.be.approximately(newSample.channelData[i],0.00002);
}
});
});
describe('#floatTo2ByteBuffer', function() {
it('converts random floats to a 2-byte buffer', function() {
var auxData = [0.001,1,-0.00892];
for (var i = 0; i < 3 ; i++) {
var buff = openBCISample.floatTo2ByteBuffer(auxData[i]);
var num = openBCISample.interpret16bitAsInt32(buff);
num = num * openBCISample.scaleFactorAux;
num.should.be.approximately(auxData[i],0.001);
}
});
});
describe('#randomSample', function() {
it('should generate a random sample',function() {
var generateSample = OpenBCISample.randomSample(k.OBCINumberOfChannelsDefault, k.OBCISampleRate250);
var generateSample = openBCISample.randomSample(k.OBCINumberOfChannelsDefault, k.OBCISampleRate250);
var oldSampleNumber = 0;
var newSample = generateSample(oldSampleNumber);
console.log(JSON.stringify(newSample));
assert(newSample.sampleNumber,oldSampleNumber+1);
describe('#debugPrettyPrint',function() {
it('works with a good sample',function() {
openBCISample.debugPrettyPrint(newSample);
});
it('does not with a undefined sample',function() {
openBCISample.debugPrettyPrint();
});
});
});
it('should generate a sample with accel data every 25Hz',function() {
var generateSample = openBCISample.randomSample(k.OBCINumberOfChannelsDefault, k.OBCISampleRate250);
var newSample = generateSample(0);
var passed = false;
// Should get one non-zero auxData array (on the 10th sample)
for (var i = 0; i < 10; i++) {
newSample = generateSample(newSample.sampleNumber);
if (newSample.auxData[0] !== 0 || newSample.auxData[1] !== 0 || newSample.auxData[2] !== 0) {
passed = true;
newSample.auxData[0].should.be.approximately(0,0.1);
newSample.auxData[1].should.be.approximately(0,0.1);
newSample.auxData[2].should.be.approximately(1,0.4);
}
}
assert(passed,"a sample with accel data was produced");
});
});
describe('#impedanceCalculationForChannel', function() {
it('rejects when undefined sampleObject', function(done) {
var bad;
openBCISample.impedanceCalculationForChannel(bad,1).should.be.rejected.and.notify(done);
});
it('rejects when undefined channel number', function(done) {
var bad;
openBCISample.impedanceCalculationForChannel('taco',bad).should.be.rejected.and.notify(done);
});
it('rejects when invalid channel number', function(done) {
var bad;
openBCISample.impedanceCalculationForChannel('taco',69).should.be.rejected.and.notify(done);
});
});
describe('#impedanceSummarize', function() {
var impedanceArray = [];
var numberOfChannels = 8;
beforeEach(() => {
impedanceArray = openBCISample.impedanceArray(numberOfChannels);
});
it('should find impedance good', function() {
impedanceArray[0].N.raw = 2201.84;
openBCISample.impedanceSummarize(impedanceArray[0].N);
impedanceArray[0].N.text.should.equal(k.OBCIImpedanceTextGood); // Check the text
});
it('should find impedance ok', function() {
impedanceArray[0].N.raw = 5201.84;
openBCISample.impedanceSummarize(impedanceArray[0].N);
impedanceArray[0].N.text.should.equal(k.OBCIImpedanceTextOk); // Check the text
});
it('should find impedance bad', function() {
impedanceArray[0].N.raw = 10201.84;
openBCISample.impedanceSummarize(impedanceArray[0].N);
impedanceArray[0].N.text.should.equal(k.OBCIImpedanceTextBad); // Check the text
});
it('should find impedance none', function() {
impedanceArray[0].N.data = 44194179.09; // A huge number that would be seen if there was no electrode connected
openBCISample.impedanceSummarize(impedanceArray[0].N);
impedanceArray[0].N.text.should.equal(k.OBCIImpedanceTextNone); // Check the text
});
});
});
describe('#goertzelProcessSample', function() {
var numberOfChannels = k.OBCINumberOfChannelsDefault;
var goertzelObj = openBCISample.goertzelNewObject(numberOfChannels);
var newRandomSample = openBCISample.randomSample(numberOfChannels,k.OBCISampleRate250);
it('produces an array of impedances', function(done) {
var passed = false;
for (var i = 0; i < openBCISample.GOERTZEL_BLOCK_SIZE + 1; i++) {
console.log('Iteration ' + i);
var impedanceArray = openBCISample.goertzelProcessSample(newRandomSample(i),goertzelObj);
if (impedanceArray) {
console.log('Impedance Array: ');
for(var j = 0; j < numberOfChannels; j++) {
console.log('Channel ' + (j+1) + ': ' + impedanceArray[j].toFixed(8))
}
passed = true;
}
}
setTimeout(() => {
if (passed) {
done();
} else {
done('Failed to produce impedance array within block size + 1');
}
})
});
});
-26
Ver Arquivo
@@ -1,26 +0,0 @@
//'use strict';
//
//var sinon = require('sinon');
//var chai = require('chai');
//var expect = chai.expect;
//
//var MockedOpenBCIBoard = require('../test_mocks/openbci-hardware');
//var openBCIBoard = MockedOpenBCIBoard.openBCIBoard;
//var hardware = MockedOpenBCIBoard.hardware;
//
//describe('openBCIBoard', function () {
// var sandbox;
//
// beforeEach(function (){
// sandbox = sinon.sandbox.create();
//
// // Create a board for fun and profit
// hardware.reset();
// hardware.createBoard();
// });
//
// afterEach(function () {
// sandbox.restore();
// });
//
//});
+1252 -293
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+4 -4
Ver Arquivo
@@ -12,8 +12,8 @@ var Hardware = function () {
// write: this.write.bind(this),
// softReset: this.softReset.bind(this),
// autoFindOpenBCIBoard: this.autoFindOpenBCIBoard.bind(this),
// simulatorStart: this.simulatorStart.bind(this),
// simulatorStop: this.simulatorStop.bind(this)
// simulatorEnable: this.simulatorEnable.bind(this),
// simulatorDisable: this.simulatorDisable.bind(this)
//};
};
@@ -75,7 +75,7 @@ Hardware.prototype.streamStop = function () {
});
};
Hardware.prototype.simulatorStart = function () {
Hardware.prototype.simulatorEnable = function () {
return new Promise((resolve,reject) => {
if(!this.bciBoard) reject('Board does not exist - call hardware.createBoard() first');
@@ -84,7 +84,7 @@ Hardware.prototype.simulatorStart = function () {
});
};
Hardware.prototype.simulatorStop = function () {
Hardware.prototype.simulatorDisable = function () {
return new Promise((resolve,reject) => {
if(!this.bciBoard) reject('Board does not exist - call hardware.createBoard() first');