Comparar commits
118 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 483811b092 | |||
| 54d223533a | |||
| a552248f21 | |||
| 5013112b36 | |||
| b65694575b | |||
| e47128cc4a | |||
| 1a6029770f | |||
| a404703d2e | |||
| f618dcd038 | |||
| 0f4cb8baf0 | |||
| 1ad709659b | |||
| b871bd159d | |||
| 9d778b64f8 | |||
| 95d6529e3a | |||
| 3f42be4df6 | |||
| 2523f72059 | |||
| af9f08642e | |||
| a2b4d73847 | |||
| c2958f408e | |||
| 0dd0eceb46 | |||
| 70a6165366 | |||
| 2d7547ce79 | |||
| de381a80de | |||
| 888be5d60f | |||
| 57e6d9f7c2 | |||
| fb3d722fc8 | |||
| 2728617126 | |||
| dd7639829a | |||
| b11775ecde | |||
| f96cdd94ec | |||
| 4ce630dc4c | |||
| 8557444c55 | |||
| c2c75fe9d8 | |||
| 13d4f57003 | |||
| b9d0a466f8 | |||
| 4428040a06 | |||
| f7e5c4988e | |||
| d4799dd45a | |||
| 212db205f2 | |||
| 47b2df5802 | |||
| d06b6101e3 | |||
| eb0510be6f | |||
| c696d4f5ca | |||
| 677517715a | |||
| 600b1b2b28 | |||
| 32d011d1f9 | |||
| ee6c50294c | |||
| 34be4c9fe3 | |||
| 57dc399742 | |||
| 5d989f6ea4 | |||
| 2eebde6053 | |||
| f7f8517c28 | |||
| d4f62ef382 | |||
| c4e29bbaa7 | |||
| f553cdea35 | |||
| 25c7871fec | |||
| c2097670e7 | |||
| cfb8071a9d | |||
| 22aca53dac | |||
| 9467d8a052 | |||
| b6ef09c220 | |||
| 238d4cc119 | |||
| c59cd18cf4 | |||
| 69cd84f446 | |||
| 9a17e41c0b | |||
| edff39504c | |||
| 2d1285a887 | |||
| 2100aaee5c | |||
| eba46f8e6c | |||
| de57563f51 | |||
| f59b41109d | |||
| 0193031d20 | |||
| 2904830309 | |||
| 08ce44d4e9 | |||
| 62a69e01cc | |||
| e9e6dbd624 | |||
| 1ade656876 | |||
| 83d32bc9b9 | |||
| 4b5e4ab0a6 | |||
| 2cc0d42fdf | |||
| 94e0a13d71 | |||
| 6b11910fa7 | |||
| 83ac939d4e | |||
| 38687d42bb | |||
| 1a43a06786 | |||
| 47c6918194 | |||
| c351f3afd1 | |||
| e65707cb31 | |||
| 97e44171b8 | |||
| 0f7bb33434 | |||
| 884f2f14ee | |||
| 5fde5b09bf | |||
| 23776e4b9c | |||
| 5b70268390 | |||
| 37db876f11 | |||
| 864469d3d8 | |||
| 7d78748532 | |||
| d6fbce3e4c | |||
| 7ecff0a08d | |||
| ab891d7565 | |||
| 3e99a77e88 | |||
| e3061c03ce | |||
| d6335257e1 | |||
| aea599a7dd | |||
| 031861d2ed | |||
| 6fd9ddf01b | |||
| fe2abac07b | |||
| 2528d56ac9 | |||
| 18b137b498 | |||
| 470ee81928 | |||
| 3c46100e1d | |||
| 2c0b2e37d2 | |||
| 094e328296 | |||
| 4c9bbb1f6d | |||
| b51cb54f92 | |||
| 19f05e362f | |||
| c33d08b486 | |||
| 97cff2dac9 |
@@ -0,0 +1 @@
|
||||
{"extends": ["standard"], "parser": "babel-eslint"}
|
||||
@@ -40,3 +40,6 @@ hardwareVoltageOutputAll.txt
|
||||
|
||||
# For git
|
||||
*.orig
|
||||
|
||||
# Text editor temporary files
|
||||
.*.sw* # vi/vim
|
||||
|
||||
+2
-1
@@ -17,4 +17,5 @@ node_modules
|
||||
.DS_Store
|
||||
public
|
||||
myOutput.txt
|
||||
*.tgz
|
||||
*.tgz
|
||||
openBCISerialFormat
|
||||
|
||||
@@ -3,12 +3,22 @@ node_js:
|
||||
- "4.0"
|
||||
- "4.1"
|
||||
- "4.2"
|
||||
- "4.3"
|
||||
- "4.4"
|
||||
- "4.5"
|
||||
- "4.6"
|
||||
- "5.11.0"
|
||||
- "6.0"
|
||||
- "6.1"
|
||||
- "6.2"
|
||||
- "6.3"
|
||||
- "6.4"
|
||||
- "6.5"
|
||||
- "6.6"
|
||||
- "6.7"
|
||||
- "6.8"
|
||||
install:
|
||||
- npm install --all
|
||||
script:
|
||||
- npm run lint
|
||||
- npm run test-cov
|
||||
|
||||
+251
-158
@@ -2,6 +2,9 @@
|
||||
[](https://gitter.im/OpenBCI/OpenBCI_NodeJS?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://travis-ci.org/OpenBCI/OpenBCI_NodeJS)
|
||||
[](https://codecov.io/gh/OpenBCI/OpenBCI_NodeJS)
|
||||
[](https://david-dm.org/OpenBCI/OpenBCI_NodeJS)
|
||||
[](http://npmjs.com/package/openbci)
|
||||
[](https://github.com/Flet/semistandard)
|
||||
|
||||
# OpenBCI Node.js SDK
|
||||
|
||||
@@ -11,20 +14,51 @@ We are proud to support all functionality of the OpenBCI 8 and 16 Channel boards
|
||||
|
||||
The purpose of this module is to **get connected** and **start streaming** as fast as possible.
|
||||
|
||||
## TL;DR
|
||||
Python researcher or developer? Check out how easy it is to [get access to the entire API in the Python example](examples/python)!
|
||||
|
||||
#### Install via npm:
|
||||
### Table of Contents:
|
||||
---
|
||||
|
||||
1. [Installation](#install)
|
||||
2. [TL;DR](#tldr)
|
||||
3. [About](#About)
|
||||
4. [General Overview](#general-overview)
|
||||
5. [SDK Reference Guide](#sdk-reference-guide)
|
||||
1. [Constructor](#constructor)
|
||||
2. [Methods](#method)
|
||||
3. [Events](#event)
|
||||
4. [Constants](#constants)
|
||||
6. [Interfacing With Other Tools](#interfacing-with-other-tools)
|
||||
7. [Developing](#developing)
|
||||
8. [Testing](#developing-testing)
|
||||
9. [Contribute](#contribute)
|
||||
10. [License](#license)
|
||||
11. [Roadmap](#roadmap)
|
||||
|
||||
### <a name="install"></a> Installation:
|
||||
```
|
||||
npm install openbci
|
||||
```
|
||||
|
||||
#### Get connected and start streaming
|
||||
#### serialport dependency
|
||||
If you encounter this error when trying to run:
|
||||
```
|
||||
Error: The module '/path/to/your/project/node_modules/serialport/build/Release/serialport.node'
|
||||
was compiled against a different Node.js version using
|
||||
NODE_MODULE_VERSION 48. This version of Node.js requires
|
||||
NODE_MODULE_VERSION 51. Please try re-compiling or re-installing
|
||||
the module (for instance, using `npm rebuild` or`npm install`).
|
||||
```
|
||||
...the issue can be resolved by running:
|
||||
```
|
||||
npm rebuild --build-from-source
|
||||
```
|
||||
### <a name="tldr"></a> TL;DR:
|
||||
Get connected and [start streaming right now with the example code](examples/getStreaming/getStreaming.js).
|
||||
|
||||
```js
|
||||
var OpenBCIBoard = require('openbci').OpenBCIBoard;
|
||||
var ourBoard = new OpenBCIBoard();
|
||||
ourBoard.connect(portName)
|
||||
ourBoard.connect(portName) // Port name is a serial port name, see `.listPorts()`
|
||||
.then(function() {
|
||||
ourBoard.on('ready',function() {
|
||||
ourBoard.streamStart();
|
||||
@@ -43,6 +77,7 @@ ourBoard.connect(portName)
|
||||
})
|
||||
```
|
||||
|
||||
### <a name="about"></a> About:
|
||||
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.
|
||||
@@ -51,9 +86,11 @@ Want to know if the module really works? Check out some projects and organizatio
|
||||
|
||||
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!
|
||||
How are you still doubting and not using this already? Fine, go look at some of the [800 **_automatic_** tests](https://codecov.io/gh/OpenBCI/OpenBCI_NodeJS) written for it!
|
||||
|
||||
## General Overview
|
||||
Python researcher or developer? Check out how easy it is to [get access to the entire API in the Python example](examples/python)!
|
||||
|
||||
### <a name="general-overview"></a> General Overview:
|
||||
|
||||
Initialization
|
||||
--------------
|
||||
@@ -64,6 +101,7 @@ Initializing the board:
|
||||
var OpenBCIBoard = require('openbci');
|
||||
var ourBoard = new OpenBCIBoard.OpenBCIBoard();
|
||||
```
|
||||
Go [checkout out the get streaming example](examples/getStreaming/getStreaming.js)!
|
||||
|
||||
For initializing with options, such as verbose print outs:
|
||||
|
||||
@@ -83,6 +121,16 @@ var ourBoard = new OpenBCIBoard({
|
||||
});
|
||||
```
|
||||
|
||||
Have a daisy?:
|
||||
```js
|
||||
var OpenBCIBoard = require('openbci').OpenBCIBoard;
|
||||
var ourBoard = new OpenBCIBoard({
|
||||
boardType: `daisy`,
|
||||
hardSet: true
|
||||
});
|
||||
```
|
||||
Go [checkout out the get streaming with daisy example](examples/getStreamingDaisy/getStreamingDaisy.js)!
|
||||
|
||||
Another useful way to start the simulator:
|
||||
```js
|
||||
var openBCIBoard = require('openbci');
|
||||
@@ -98,6 +146,15 @@ ourBoard.connect(k.OBCISimulatorPortName) // This will set `simulate` to true
|
||||
});
|
||||
```
|
||||
|
||||
To debug, it's amazing, do:
|
||||
|
||||
```js
|
||||
var OpenBCIBoard = require('openbci').OpenBCIBoard;
|
||||
var ourBoard = new OpenBCIBoard({
|
||||
simulate: true
|
||||
});
|
||||
```
|
||||
Go [checkout out the debug example](examples/debug/debug.js)!
|
||||
|
||||
'ready' event
|
||||
------------
|
||||
@@ -130,14 +187,14 @@ Sample properties:
|
||||
|
||||
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'`.
|
||||
You can also start the simulator by sending [`.connect(portName)`](#method-connect) with `portName` equal to [`'OpenBCISimulator'`](#constants-obcisimulatorportname).
|
||||
|
||||
To get a 'sample' event, you need to:
|
||||
To get a ['sample'](#event-sample) event, you need to:
|
||||
-------------------------------------
|
||||
1. Call `.connect(serialPortName)`
|
||||
2. Install the 'ready' event emitter on resolved promise
|
||||
3. In callback for 'ready' emitter, call `streamStart()`
|
||||
4. Install the 'sample' event emitter
|
||||
1. Call [`.connect(serialPortName)`](#method-connect)
|
||||
2. Install the ['ready'](#event-ready) event emitter on resolved promise
|
||||
3. In callback for ['ready'](#event-ready) emitter, call [`streamStart()`](#method-stream-start)
|
||||
4. Install the ['sample'](#event-sample) event emitter
|
||||
```js
|
||||
var OpenBCIBoard = require('openbci').OpenBCIBoard;
|
||||
var ourBoard = new OpenBCIBoard();
|
||||
@@ -152,7 +209,7 @@ ourBoard.connect(portName).then(function() {
|
||||
/** Handle connection errors */
|
||||
});
|
||||
```
|
||||
Close the connection with `.streamStop()` and disconnect with `.disconnect()`
|
||||
Close the connection with [`.streamStop()`](#method-stream-stop) and disconnect with [`.disconnect()`](#method-disconnect)
|
||||
```js
|
||||
var ourBoard = new require('openbci').OpenBCIBoard();
|
||||
ourBoard.streamStop().then(ourBoard.disconnect());
|
||||
@@ -160,7 +217,7 @@ ourBoard.streamStop().then(ourBoard.disconnect());
|
||||
|
||||
Time Syncing
|
||||
------------
|
||||
You must be using OpenBCI firmware version 2 in order to do time syncing. After you `.connect()` and send a `.softReset()`, you can call `.usingVersionTwoFirmware()` to get a boolean response as to if you are using `v1` or `v2`.
|
||||
You must be using OpenBCI firmware version 2 in order to do time syncing. After you [`.connect()`](#method-connect) and send a [`.softReset()`](#method-soft-reset), you can call [`.usingVersionTwoFirmware()`](#method-using-version-two-firmware) to get a boolean response as to if you are using `v1` or `v2`.
|
||||
|
||||
Now using firmware `v2`, the fun begins! We synchronize the Board's clock with the module's time. In firmware `v2` we leverage samples with time stamps and _ACKs_ from the Dongle to form a time synchronization strategy. Time syncing has been verified to +/- 4ms and a test report is on the way. We are still working on the synchronize of this module and an NTP server, this is an open call for any NTP experts out there! With a global NTP server you could use several different devices and all sync to the same time server. That way you can really do some serious cloud computing!
|
||||
|
||||
@@ -175,13 +232,13 @@ var OpenBCIBoard = require('openbci').OpenBCIBoard,
|
||||
|
||||
const resyncPeriodMin = 5; // re sync every five minutes
|
||||
const secondsInMinute = 60;
|
||||
var sampleRate = k.OBCISampleRate250; // Default to 250, ALWAYS verify with a call to `sampleRate` after `ready` event!
|
||||
var sampleRate = k.OBCISampleRate250; // Default to 250, ALWAYS verify with a call to `.sampleRate()` after 'ready' event!
|
||||
var timeSyncPossible = false;
|
||||
|
||||
// Call to connect
|
||||
ourBoard.connect(portName).then(() => {
|
||||
ourBoard.on('ready',() => {
|
||||
// Get the sample rate after 'ready' event!
|
||||
// Get the sample rate after 'ready'
|
||||
sampleRate = ourBoard.sampleRate();
|
||||
// Find out if you can even time sync, you must be using v2 and this is only accurate after a `.softReset()` call which is called internally on `.connect()`. We parse the `.softReset()` response for the presence of firmware version 2 properties.
|
||||
timeSyncPossible = ourBoard.usingVersionTwoFirmware();
|
||||
@@ -229,7 +286,7 @@ 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 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.
|
||||
If a port is not automatically found, then call [`.listPorts()`](#method-list-ports) 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 OpenBCIBoard = require('openbci').OpenBCIBoard;
|
||||
@@ -246,7 +303,7 @@ ourBoard.autoFindOpenBCIBoard().then(portName => {
|
||||
});
|
||||
```
|
||||
|
||||
Note: `.autoFindOpenBCIBoard()` will return the name of the Simulator if you instantiate with option `simulate: true`.
|
||||
Note: [`.autoFindOpenBCIBoard()`](#method-auto-find-open-bci-board) will return the name of the Simulator if you instantiate with option `simulate: true`.
|
||||
|
||||
Auto Test - (Using impedance to determine signal quality)
|
||||
---------------------------------------------------------
|
||||
@@ -260,8 +317,8 @@ 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
|
||||
3. Install the ['impedanceArray'](#event-impedance-array) event
|
||||
4. Call [`.impedanceTestChannels()`](#method-impedance-test-channels) with your configuration array
|
||||
|
||||
A configuration array looks like, for an 8 channel board, `['-','N','n','p','P','-','b','b']`
|
||||
|
||||
@@ -323,8 +380,8 @@ 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()`
|
||||
3. Install the ['impedanceArray'](#event-impedance-array)
|
||||
4. Call [`.impedanceTestAllChannels()`](#method-impedance-test-all-channels)
|
||||
|
||||
**Note: Takes up to 5 seconds to start measuring impedances. There is an unknown number of samples taken. Not always 60!**
|
||||
|
||||
@@ -344,11 +401,11 @@ ourBoard.connect(portName).then(function(boardSerial) {
|
||||
|
||||
See Reference Guide for a complete list of impedance tests.
|
||||
|
||||
Reference Guide
|
||||
# <a name="sdk-reference-guide"></a> SDK Reference Guide:
|
||||
---------------
|
||||
## Methods
|
||||
## <a name="constructor"></a> Constructor:
|
||||
|
||||
### OpenBCIBoard (options)
|
||||
### <a name="constructor-openbciboard"></a> OpenBCIBoard (options)
|
||||
|
||||
Create new instance of an OpenBCI board.
|
||||
|
||||
@@ -362,28 +419,39 @@ Board optional configurations.
|
||||
* `daisy` - 8 Channel board with Daisy Module - 16 Channels
|
||||
* `ganglion` - 4 Channel board
|
||||
(NOTE: THIS IS IN-OP TIL RELEASE OF GANGLION BOARD 08/2016)
|
||||
* `hardSet` {Boolean} - Recommended if using `daisy` board! For some reason, the `daisy` is sometimes not picked up by the module so you can set `hardSet` to true which will ensure the daisy is picked up. (Default `false`)
|
||||
* `simulate` {Boolean} - Full functionality, just mock data. Must attach Daisy module by setting `simulatorDaisyModuleAttached` to `true` in order to get 16 channels. (Default `false`)
|
||||
* `simulatorBoardFailure` {Boolean} - Simulates board communications failure. This occurs when the RFduino on the board is not polling the RFduino on the dongle. (Default `false`)
|
||||
* `simulatorDaisyModuleAttached` {Boolean} - Simulates a daisy module being attached to the OpenBCI board. This is useful if you want to test how your application reacts to a user requesting 16 channels but there is no daisy module actually attached, or vice versa, where there is a daisy module attached and the user only wants to use 8 channels. (Default `false`)
|
||||
* `simulatorFirmwareVersion` {String} - Allows the simulator to use firmware version 2 features. (2 Possible Options)
|
||||
* `v1` - Firmware Version 1 (Default)
|
||||
* `v2` - Firmware Version 2
|
||||
* `simulatorFragmentation` {String} - Specifies how to break packets to simulate fragmentation, which occurs commonly in real devices. It is recommended to test code with this enabled. (4 Possible Options)
|
||||
* `none` - do not fragment packets; output complete chunks immediately when produced (Default)
|
||||
* `random` - output random small chunks of data interspersed with full buffers
|
||||
* `fullBuffers` - allow buffers to fill up until latency timer has expired
|
||||
* `oneByOne` - output each byte separately
|
||||
* `simulatorLatencyTime` {Number} - The time in milliseconds to wait before sending partially full buffers of data, if `simulatorFragmentation` is specified. (Default `16`)
|
||||
* `simulatorBufferSize` {Number} - The size of a full buffer of data, if `simulatorFragmentation` is specified. (Default `4096`)
|
||||
* `simulatorHasAccelerometer` - {Boolean} - Sets simulator to send packets with accelerometer data. (Default `true`)
|
||||
* `simulatorInjectAlpha` - {Boolean} - Inject a 10Hz alpha wave in Channels 1 and 2 (Default `true`)
|
||||
* `simulatorInjectLineNoise` {String} - Injects line noise on channels. (3 Possible Options)
|
||||
* `60Hz` - 60Hz line noise (Default) [America]
|
||||
* `50Hz` - 50Hz line noise [Europe]
|
||||
* `None` - Do not inject line noise.
|
||||
* `none` - Do not inject line noise.
|
||||
* `simulatorSampleRate` {Number} - The sample rate to use for the simulator. Simulator will set to 125 if `simulatorDaisyModuleAttached` is set `true`. However, setting this option overrides that setting and this sample rate will be used. (Default is `250`)
|
||||
* `simulatorSerialPortFailure` {Boolean} - Simulates not being able to open a serial connection. Most likely due to a OpenBCI dongle not being plugged in.
|
||||
* `sntpTimeSync` - {Boolean} Syncs the module up with an SNTP time server and uses that as single source of truth instead of local computer time. If you are running experiments on your local computer, keep this `false`. (Default `false`)
|
||||
* `sntpTimeSyncHost` - {String} The sntp server to use, can be either sntp or ntp (Defaults `pool.ntp.org`).
|
||||
* `sntpTimeSyncPort` - {Number} The port to access the sntp server (Defaults `123`)
|
||||
* `verbose` {Boolean} - Print out useful debugging events (Default `false`)
|
||||
* `debug` {Boolean} - Print out a raw dump of bytes sent and received (Default `false`)
|
||||
|
||||
**Note, we have added support for either all lowercase OR camel case for the options, use whichever style you prefer.**
|
||||
|
||||
### .autoFindOpenBCIBoard()
|
||||
## <a name="method"></a> Methods:
|
||||
|
||||
### <a name="method-auto-find-open-bci-board"></a> .autoFindOpenBCIBoard()
|
||||
|
||||
Automatically find an OpenBCI board.
|
||||
|
||||
@@ -391,7 +459,7 @@ Automatically find an OpenBCI board.
|
||||
|
||||
**_Returns_** a promise, fulfilled with a `portName` such as `/dev/tty.*` on Mac/Linux or `OpenBCISimulator` if `this.options.simulate === true`.
|
||||
|
||||
### .channelOff(channelNumber)
|
||||
### <a name="method-channel-off"></a> .channelOff(channelNumber)
|
||||
|
||||
Turn off a specified channel
|
||||
|
||||
@@ -401,7 +469,7 @@ A number (1-16) specifying which channel you want to turn off.
|
||||
|
||||
**_Returns_** a promise, fulfilled if the command was sent to the write queue.
|
||||
|
||||
### .channelOn(channelNumber)
|
||||
### <a name="method-channel-on"></a> .channelOn(channelNumber)
|
||||
|
||||
Turn on a specified channel
|
||||
|
||||
@@ -411,7 +479,7 @@ A number (1-16) specifying which channel you want to turn on.
|
||||
|
||||
**_Returns_** a promise, fulfilled if the command was sent to the write queue.
|
||||
|
||||
### .channelSet(channelNumber,powerDown,gain,inputType,bias,srb2,srb1)
|
||||
### <a name="method-"></a> .channelSet(channelNumber,powerDown,gain,inputType,bias,srb2,srb1)
|
||||
|
||||
Send a channel setting command to the board.
|
||||
|
||||
@@ -451,7 +519,7 @@ ourBoard.channelSet(2,false,24,'normal',true,true,false);
|
||||
// sends ['x','2','0','6','0','1','1','0','X'] to the command queue
|
||||
```
|
||||
|
||||
### .connect (portName)
|
||||
### <a name="method-connect"></a> .connect(portName)
|
||||
|
||||
The essential precursor method to be called initially to establish a serial connection to the OpenBCI board.
|
||||
|
||||
@@ -461,17 +529,23 @@ The system path of the OpenBCI board serial port to open. For example, `/dev/tty
|
||||
|
||||
**_Returns_** a promise, fulfilled by a successful serial connection to the board.
|
||||
|
||||
### .debugSession()
|
||||
### <a name="method-debug-session"></a> .debugSession()
|
||||
|
||||
Calls all `.printPacketsBad()`, `.printPacketsRead()`, `.printBytesIn()`
|
||||
Calls all [`.printPacketsBad()`](#method-print-packets-bad), [`.printPacketsRead()`](#method-print-packets-read), [`.printBytesIn()`](#method-print-bytes-in)
|
||||
|
||||
### .disconnect()
|
||||
### <a name="method-disconnect"></a> .disconnect()
|
||||
|
||||
Closes the serial port opened by `.connect()`. Waits for stop streaming command to be sent if currently streaming.
|
||||
Closes the serial port opened by [`.connect()`](#method-connect). Waits for stop streaming command to be sent if currently streaming.
|
||||
|
||||
**_Returns_** a promise, fulfilled by a successful close of the serial port object, rejected otherwise.
|
||||
|
||||
### .getSettingsForChannel(channelNumber)
|
||||
### <a name="method-get-info"></a> .getInfo()
|
||||
|
||||
Get the core info object. It's the object that actually drives the parsing of data.
|
||||
|
||||
**_Returns_** Object - {{boardType: string, sampleRate: number, firmware: string, numberOfChannels: number, missedPackets: number}}
|
||||
|
||||
### <a name="method-get-settings-for-channel"></a> .getSettingsForChannel(channelNumber)
|
||||
|
||||
Gets the specified channelSettings register data from printRegisterSettings call.
|
||||
|
||||
@@ -483,17 +557,32 @@ 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.
|
||||
|
||||
### .impedanceTestAllChannels()
|
||||
### <a name="method-hard-set-board-type"></a> .hardSetBoardType(boardType)
|
||||
|
||||
Used to sync the module and board to `boardType`.
|
||||
|
||||
**Note: This has the potential to change the way data is parsed**
|
||||
|
||||
**_boardType_**
|
||||
|
||||
A String indicating the number of channels.
|
||||
|
||||
* `default` - Default board: Sample rate is `250Hz` and number of channels is `8`.
|
||||
* `daisy` - Daisy board: Sample rate is `125Hz` and number of channels is `16`.
|
||||
|
||||
**_Returns_** a promise, fulfilled if both the board and module are the requested `boardType`, rejects otherwise.
|
||||
|
||||
### <a name="method-impedance-test-all-channels"></a> .impedanceTestAllChannels()
|
||||
|
||||
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)!
|
||||
|
||||
Don't forget to install the `impedanceArray` emitter to receive the impendances!
|
||||
Don't forget to install the ['impedanceArray'](#event-impedance-array) emitter to receive the impendances!
|
||||
|
||||
**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!**
|
||||
|
||||
**_Returns_** a promise upon completion of test.
|
||||
|
||||
### .impedanceTestChannels(arrayOfCommands)
|
||||
### <a name="method-impedance-test-channels"></a> .impedanceTestChannels(arrayOfCommands)
|
||||
|
||||
**_arrayOfCommands_**
|
||||
|
||||
@@ -510,7 +599,7 @@ Don't forget to install the `impedanceArray` emitter to receive the impendances!
|
||||
|
||||
**_Returns_** a promise upon completion of test.
|
||||
|
||||
### .impedanceTestChannel(channelNumber)
|
||||
### <a name="method-impedance-test-channel"></a> .impedanceTestChannel(channelNumber)
|
||||
|
||||
Run a complete impedance test on a single channel, applying the test signal individually to P & N inputs.
|
||||
|
||||
@@ -552,7 +641,7 @@ Where an impedance for this method call would look like:
|
||||
}
|
||||
```
|
||||
|
||||
### .impedanceTestChannelInputP(channelNumber)
|
||||
### <a name="method-impedance-test-channel-input-p"></a> .impedanceTestChannelInputP(channelNumber)
|
||||
|
||||
Run impedance test on a single channel, applying the test signal only to P input.
|
||||
|
||||
@@ -594,7 +683,7 @@ Where an impedance for this method call would look like:
|
||||
}
|
||||
```
|
||||
|
||||
### .impedanceTestChannelInputN(channelNumber)
|
||||
### <a name="method-impedance-test-channel-input-n"></a> .impedanceTestChannelInputN(channelNumber)
|
||||
|
||||
Run impedance test on a single channel, applying the test signal only to N input.
|
||||
|
||||
@@ -636,25 +725,35 @@ Where an impedance for this method call would look like:
|
||||
}
|
||||
```
|
||||
|
||||
### .impedanceTestContinuousStart()
|
||||
### <a name="method-impedance-test-continuous-start"></a> .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()
|
||||
### <a name="method-impedance-test-continuous-stop"></a> .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()
|
||||
### <a name="method-is-connected"></a> .isConnected()
|
||||
|
||||
Checks if the driver is connected to a board.
|
||||
**_Returns_** a boolean, true if connected
|
||||
|
||||
### <a name="method-is-streaming"></a> .isStreaming()
|
||||
|
||||
Checks if the board is currently sending samples.
|
||||
**_Returns_** a boolean, true if streaming
|
||||
|
||||
### <a name="method-list-ports"></a> .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()
|
||||
### <a name="method-number-of-channels"></a> .numberOfChannels()
|
||||
|
||||
Get the current number of channels available to use. (i.e. 8 or 16).
|
||||
|
||||
@@ -662,25 +761,38 @@ Get the current number of channels available to use. (i.e. 8 or 16).
|
||||
|
||||
**_Returns_** a number, the total number of available channels.
|
||||
|
||||
### .printBytesIn()
|
||||
### <a name="method-override-info-for-board-type"></a> .overrideInfoForBoardType(boardType)
|
||||
|
||||
Set the info property for board type.
|
||||
|
||||
**Note: This has the potential to change the way data is parsed**
|
||||
|
||||
**_boardType_**
|
||||
|
||||
A String indicating the number of channels.
|
||||
|
||||
* `default` - Default board: Sample rate is `250Hz` and number of channels is `8`.
|
||||
* `daisy` - Daisy board: Sample rate is `125Hz` and number of channels is `16`.
|
||||
|
||||
### <a name="method-print-bytes-in"></a> .printBytesIn()
|
||||
|
||||
Prints the total number of bytes that were read in this session to the console.
|
||||
|
||||
### .printPacketsBad()
|
||||
### <a name="method-print-packets-bad"></a> .printPacketsBad()
|
||||
|
||||
Prints the total number of packets that were not able to be read in this session to the console.
|
||||
|
||||
### .printPacketsRead()
|
||||
### <a name="method-print-packets-read"></a> .printPacketsRead()
|
||||
|
||||
Prints the total number of packets that were read in this session to the console.
|
||||
|
||||
### .printRegisterSettings()
|
||||
### <a name="method-print-register-settings"></a> .printRegisterSettings()
|
||||
|
||||
Prints all register settings for the ADS1299 and the LIS3DH on the OpenBCI board.
|
||||
|
||||
**_Returns_** a promise, fulfilled if the command was sent to the write queue.
|
||||
|
||||
### .radioBaudRateSet(speed)
|
||||
### <a name="method-radio-baud-rate-set"></a> .radioBaudRateSet(speed)
|
||||
|
||||
Used to set the OpenBCI Host (Dongle) baud rate. With the RFduino configuration, the Dongle is the Host and the Board is the Device. Only the Device can initiate a communication between the two entities. There exists a detrimental error where if the Host is interrupted by the radio during a Serial write, then all hell breaks loose. So this is an effort to eliminate that problem by increasing the rate at which serial data is sent from the Host to the Serial driver. The rate can either be set to default or fast. Further the function should reject if currently streaming. Lastly and more important, if the board is not running the new firmware then this functionality does not exist and thus this method will reject. If the board is using firmware 2+ then this function should resolve the new baud rate after closing the current serial port and reopening one.
|
||||
|
||||
@@ -692,7 +804,7 @@ Used to set the OpenBCI Host (Dongle) baud rate. With the RFduino configuration,
|
||||
|
||||
**_Returns_** {Promise} - Resolves a {Number} that is the new baud rate, rejects on error.
|
||||
|
||||
### .radioChannelGet()
|
||||
### <a name="method-radio-channel-get"></a> .radioChannelGet()
|
||||
|
||||
Used to query the OpenBCI system for it's radio channel number. The function will reject if not connected to the serial port of the dongle. Further the function should reject if currently streaming. Lastly and more important, if the board is not running the new firmware then this functionality does not exist and thus this method will reject. If the board is using firmware 2+ then this function should resolve an Object. See `returns` below.
|
||||
|
||||
@@ -700,7 +812,7 @@ Used to query the OpenBCI system for it's radio channel number. The function wil
|
||||
|
||||
**_Returns_** {Promise} - Resolve an object with keys `channelNumber` which is a Number and `err` which contains an error in the condition that there system is experiencing board communications failure.
|
||||
|
||||
### .radioChannelSet(channelNumber)
|
||||
### <a name="method-radio-channel-set"></a> .radioChannelSet(channelNumber)
|
||||
|
||||
Used to set the system radio channel number. The function will reject if not connected to the serial port of the dongle. Further the function should reject if currently streaming. Lastly and more important, if the board is not running the new firmware then this functionality does not exist and thus this method will reject. If the board is using firmware 2+ then this function should resolve.
|
||||
|
||||
@@ -712,7 +824,7 @@ Used to set the system radio channel number. The function will reject if not con
|
||||
|
||||
**_Returns_** {Promise} - Resolves with the new channel number, rejects with err.
|
||||
|
||||
### .radioChannelSetHostOverride(channelNumber)
|
||||
### <a name="method-radio-channel-set-host-override"></a> .radioChannelSetHostOverride(channelNumber)
|
||||
|
||||
Used to set the ONLY the radio dongle Host channel number. This will fix your radio system if your dongle and board are not on the right channel and bring down your radio system if you take your dongle and board are not on the same channel. Use with caution! The function will reject if not connected to the serial port of the dongle. Further the function should reject if currently streaming. Lastly and more important, if the board is not running the new firmware then this functionality does not exist and thus this method will reject. If the board is using firmware 2+ then this function should resolve.
|
||||
|
||||
@@ -724,7 +836,7 @@ Used to set the ONLY the radio dongle Host channel number. This will fix your ra
|
||||
|
||||
**_Returns_** {Promise} - Resolves with the new channel number, rejects with err.
|
||||
|
||||
### .radioPollTimeGet()
|
||||
### <a name="method-radio-poll-time-get"></a> .radioPollTimeGet()
|
||||
|
||||
Used to query the OpenBCI system for it's device's poll time. The function will reject if not connected to the serial port of the dongle. Further the function should reject if currently streaming. Lastly and more important, if the board is not running the new firmware then this functionality does not exist and thus this method will reject. If the board is using firmware 2+ then this function should resolve the poll time when fulfilled. It's important to note that if the board is not on, this function will always be rejected with a failure message.
|
||||
|
||||
@@ -732,7 +844,7 @@ Used to query the OpenBCI system for it's device's poll time. The function will
|
||||
|
||||
**_Returns_** {Promise} - Resolves with the new poll time, rejects with err.
|
||||
|
||||
### .radioPollTimeSet(pollTime)
|
||||
### <a name="method-radio-poll-time-set"></a> .radioPollTimeSet(pollTime)
|
||||
|
||||
Used to set the OpenBCI poll time. With the RFduino configuration, the Dongle is the Host and the Board is the Device. Only the Device can initiate a communication between the two entities. Therefore this sets the interval at which the Device polls the Host for new information. Further the function should reject if currently streaming. Lastly and more important, if the board is not running the new firmware then this functionality does not exist and thus this method will reject. If the board is using firmware 2+ then this function should resolve.
|
||||
|
||||
@@ -744,7 +856,7 @@ Used to set the OpenBCI poll time. With the RFduino configuration, the Dongle is
|
||||
|
||||
**_Returns_** {Promise} - Resolves with the new channel number, rejects with err.
|
||||
|
||||
### .radioSystemStatusGet()
|
||||
### <a name="method-radio-system-status-get"></a> .radioSystemStatusGet()
|
||||
|
||||
Used to ask the Host if it's radio system is up. This is useful to quickly determine if you are in fact ready to start trying to connect and such. The function will reject if not connected to the serial port of the dongle. Further the function should reject if currently streaming. Lastly and more important, if the board is not running the new firmware then this functionality does not exist and thus this method will reject. If the board is using firmware +v2.0.0 and the radios are both on the same channel and powered, then this will resolve true.
|
||||
|
||||
@@ -752,7 +864,7 @@ Used to ask the Host if it's radio system is up. This is useful to quickly deter
|
||||
|
||||
**_Returns_** {Promise} - Resolves true if both radios are powered and on the same channel; false otherwise.
|
||||
|
||||
### .sampleRate()
|
||||
### <a name="method-sample-rate"></a> .sampleRate()
|
||||
|
||||
Get the current sample rate.
|
||||
|
||||
@@ -760,7 +872,7 @@ Get the current sample rate.
|
||||
|
||||
**_Returns_** a number, the current sample rate.
|
||||
|
||||
### .sdStart(recordingDuration)
|
||||
### <a name="method-sd-start"></a> .sdStart(recordingDuration)
|
||||
|
||||
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 `$$$`.
|
||||
|
||||
@@ -782,21 +894,21 @@ The duration you want to log SD information for. Opens a new SD file to write in
|
||||
|
||||
**_Returns_** resolves if the command was added to the write queue.
|
||||
|
||||
### .sdStop()
|
||||
### <a name="method-sd-stop"></a> .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()
|
||||
### <a name="method-simulator-enable"></a> .simulatorEnable()
|
||||
|
||||
To enter simulate mode. Must call `.connect()` after.
|
||||
To enter simulate mode. Must call [`.connect()`](#method-connect) after.
|
||||
|
||||
**Note, must be called after the constructor.**
|
||||
|
||||
**_Returns_** a promise, fulfilled if able to enter simulate mode, reject if not.
|
||||
|
||||
### .simulatorDisable()
|
||||
### <a name="method-simulator-disable"></a> .simulatorDisable()
|
||||
|
||||
To leave simulate mode.
|
||||
|
||||
@@ -804,27 +916,27 @@ To leave simulate mode.
|
||||
|
||||
**_Returns_** a promise, fulfilled if able to stop simulate mode, reject if not.
|
||||
|
||||
### .sntp
|
||||
### <a name="method-sntp"></a> .sntp
|
||||
|
||||
Extends the popular STNP package on [npmjs](https://www.npmjs.com/package/sntp)
|
||||
Extends the popular SNTP package on [npmjs](https://www.npmjs.com/package/sntp)
|
||||
|
||||
### .sntpGetOffset()
|
||||
### <a name="method-sntp-get-offset"></a> .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
|
||||
|
||||
### .sntpStart()
|
||||
### <a name="method-sntp-start"></a> .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()
|
||||
### <a name="method-sntp-stop"></a> .sntpStop()
|
||||
|
||||
Stops the SNTP from updating
|
||||
|
||||
### .softReset()
|
||||
### <a name="method-soft-reset"></a> .softReset()
|
||||
|
||||
Sends a soft reset command to the board.
|
||||
|
||||
@@ -832,23 +944,23 @@ Sends a soft reset command to the board.
|
||||
|
||||
**_Returns_** a promise, fulfilled if the command was sent to the write queue.
|
||||
|
||||
### .streamStart()
|
||||
### <a name="method-stream-start"></a> .streamStart()
|
||||
|
||||
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.**
|
||||
**Note, You must have called and fulfilled [`.connect()`](#method-connect) AND observed a `'ready'` emitter before calling this method.**
|
||||
|
||||
**_Returns_** a promise, fulfilled if the command was sent to the write queue, rejected if unable.
|
||||
|
||||
### .streamStop()
|
||||
### <a name="method-stream-stop"></a> .streamStop()
|
||||
|
||||
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.**
|
||||
**Note, You must have called and fulfilled [`.connect()`](#method-connect) AND observed a `'ready'` emitter before calling this method.**
|
||||
|
||||
**_Returns_** a promise, fulfilled if the command was sent to the write queue, rejected if unable.
|
||||
|
||||
### .syncClocks()
|
||||
### <a name="method-sync-clocks"></a> .syncClocks()
|
||||
|
||||
Send the command to tell the board to start the syncing protocol. Must be connected, streaming and using version +2 firmware.
|
||||
|
||||
@@ -856,7 +968,7 @@ Send the command to tell the board to start the syncing protocol. Must be connec
|
||||
|
||||
**_Returns_** {Promise} resolves if the command was sent to the write queue, rejects if unable.
|
||||
|
||||
### .syncClocksFull()
|
||||
### <a name="method-sync-clocks-full"></a> .syncClocksFull()
|
||||
|
||||
Send the command to tell the board to start the syncing protocol. Must be connected, streaming and using v2 firmware. Uses the `synced` event to ensure multiple syncs don't overlap.
|
||||
|
||||
@@ -873,6 +985,7 @@ Send the command to tell the board to start the syncing protocol. Must be connec
|
||||
timeRoundTrip: 0, // Simply timeSyncSetPacket - timeSyncSent.
|
||||
timeTransmission: 0, // Estimated time it took for time sync set packet to be sent from Board to Driver.
|
||||
timeOffset: 0, // The map (or translation) from boardTime to module time.
|
||||
timeOffsetMaster: 0, // The map (or translation) from boardTime to module time averaged over time syncs.
|
||||
valid: false // If there was an error in the process, valid will be false and no time sync was done. It's important to resolve this so we can perform multiple promise syncs as show in the example below.
|
||||
}
|
||||
```
|
||||
@@ -948,7 +1061,7 @@ ourBoard.connect(portName)
|
||||
|
||||
```
|
||||
|
||||
### .testSignal(signal)
|
||||
### <a name="method-test-signal"></a> .testSignal(signal)
|
||||
|
||||
Apply the internal test signal to all channels.
|
||||
|
||||
@@ -966,21 +1079,21 @@ A String indicating which test signal to apply
|
||||
|
||||
**_Returns_** a promise, if the commands were sent to write buffer.
|
||||
|
||||
### .time()
|
||||
### <a name="method-time"></a> .time()
|
||||
|
||||
Uses `._sntpNow()` time when sntpTimeSync specified in options, or else use Date.now() for time.
|
||||
Uses `._sntpNow()` time when sntpTimeSync specified `true` in options, or else Date.now() for time.
|
||||
|
||||
**_Returns_** time since UNIX epoch in ms.
|
||||
|
||||
### .usingVersionTwoFirmware()
|
||||
### <a name="method-using-version-two-firmware"></a> .usingVersionTwoFirmware()
|
||||
|
||||
Convenience method to determine if you can use firmware v2.x.x capabilities.
|
||||
|
||||
**Note, should be called after a `.softReset()` because we can parse the output of that to determine if we are using firmware version 2.**
|
||||
**Note, should be called after a [`.softReset()`](#method-soft-reset) because we can parse the output of that to determine if we are using firmware version 2.**
|
||||
|
||||
**_Returns_** a boolean, true if using firmware version 2 or greater.
|
||||
|
||||
### .write(dataToWrite)
|
||||
### <a name="method-write"></a> .write(dataToWrite)
|
||||
|
||||
Send commands to the board. Due to the OpenBCI board firmware 1.0, 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. If you are using firmware version +2.0 then you no spacing will be used.
|
||||
|
||||
@@ -994,13 +1107,13 @@ Either a single character or an Array of characters
|
||||
|
||||
Sends a single character command to the board.
|
||||
```js
|
||||
// ourBoard has fulfilled the promise on .connected() and 'ready' has been observed previously
|
||||
// ourBoard has fulfilled the promise on .connect() and 'ready' has been observed previously
|
||||
ourBoard.write('a');
|
||||
```
|
||||
|
||||
Sends an array of bytes
|
||||
```js
|
||||
// ourBoard has fulfilled the promise on .connected() and 'ready' has been observed previously
|
||||
// ourBoard has fulfilled the promise on .connect() and 'ready' has been observed previously
|
||||
ourBoard.write(['x','0','1','0','0','0','0','0','0','X']);
|
||||
```
|
||||
|
||||
@@ -1012,47 +1125,49 @@ ourBoard.write('c');
|
||||
ourBoard.write('o');
|
||||
```
|
||||
|
||||
## Events
|
||||
## <a name="event"></a> Events:
|
||||
|
||||
### .on('close', callback)
|
||||
### <a name="event-close"></a> .on('close', callback)
|
||||
|
||||
Emitted when the serial connection to the board is closed.
|
||||
|
||||
### .on('error', callback)
|
||||
### <a name="event-close"></a> .on('droppedPacket', callback)
|
||||
|
||||
Emitted when a packet (or packets) are dropped. Returns an array.
|
||||
|
||||
### <a name="event-error"></a> .on('error', callback)
|
||||
|
||||
Emitted when there is an on the serial port.
|
||||
|
||||
### .on('impedanceArray', callback)
|
||||
### <a name="event-hard-set"></a> .on('hardSet', callback)
|
||||
|
||||
Emitted when there is a new impedanceArray available.
|
||||
Emitted when the module detects the board is not configured as the options for the module intended and tries to save itself. i.e. when the `daisy` option is `true` and a soft reset message is parsed and the module determines that a daisy was not detected, the module will emit `hardSet` then send an attach daisy command to recover. Either `error` will be emitted if unable to attach or `ready` will be emitted if success.
|
||||
|
||||
### .on('query', callback)
|
||||
### <a name="event-impedance-array"></a> .on('impedanceArray', callback)
|
||||
|
||||
Emitted resulting in a call to `.getChannelSettings()` with the channelSettingsObject
|
||||
Emitted when there is a new impedanceArray available. Returns an array.
|
||||
|
||||
### .on('rawDataPacket', callback)
|
||||
### <a name="event-query"></a> .on('query', callback)
|
||||
|
||||
Emitted resulting in a call to [`.getChannelSettings()`](#method-get-settings-for-channel) with the channelSettingsObject
|
||||
|
||||
### <a name="event-raw-data-packet"></a> .on('rawDataPacket', callback)
|
||||
|
||||
Emitted when there is a new raw data packet available.
|
||||
|
||||
### .on('ready', callback)
|
||||
### <a name="event-ready"></a> .on('ready', callback)
|
||||
|
||||
Emitted when the board is in a ready to start streaming state.
|
||||
|
||||
### .on('sample', callback)
|
||||
### <a name="event-sample"></a> .on('sample', callback)
|
||||
|
||||
Emitted when there is a new sample available.
|
||||
|
||||
## Properties
|
||||
### <a name="event-sample"></a> .on('synced', callback)
|
||||
|
||||
### connected
|
||||
Emitted when there is a new sample available.
|
||||
|
||||
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
|
||||
## <a name="constants"></a> Constants:
|
||||
|
||||
To use the constants file simply:
|
||||
```js
|
||||
@@ -1062,76 +1177,54 @@ var k = openBCIBoard.OpenBCIConstants;
|
||||
console.log(k.OBCISimulatorPortName); // prints OpenBCISimulator to the console.
|
||||
```
|
||||
|
||||
### .OBCISimulatorPortName
|
||||
### <a name="constants-obcisimulatorportname"></a> .OBCISimulatorPortName
|
||||
|
||||
The name of the simulator port.
|
||||
|
||||
## Interfacing With Other Tools
|
||||
## <a name="interfacing-with-other-tools"></a> Interfacing With Other Tools:
|
||||
|
||||
### LabStreamingLayer
|
||||
### <a name="interfacing-with-other-tools-labstreaminglayer"></a> LabStreamingLayer
|
||||
|
||||
[LabStreamingLayer](https://github.com/sccn/labstreaminglayer) by SCCN is a stream management tool designed to time-synchronize multiple data streams, potentially from different sources, over a LAN network with millisecond accuracy (given configuration).
|
||||
[LabStreamingLayer](https://github.com/sccn/labstreaminglayer) is a tool for streaming or recording time-series data. It can be used to interface with [Matlab](https://github.com/sccn/labstreaminglayer/tree/master/LSL/liblsl-Matlab), [Python](https://github.com/sccn/labstreaminglayer/tree/master/LSL/liblsl-Python), [Unity](https://github.com/xfleckx/LSL4Unity), and many other programs.
|
||||
|
||||
For example, a VR display device running a Unity simulation may, using the [LSL4Unity](https://github.com/xfleckx/LSL4Unity) library, emit string markers into LSL corresponding to events of interest (For the P300 ERP, this event would be the onset of an attended, unusual noise in a pattern of commonplace ones). The computer doing data collection via the OpenBCI_NodeJS library (potentially with 4ms accuracy) would then output into an LSL stream the EEG and AUX data. LSL can then synchronize the two clocks relative to each other before inputting into a different program or toolkit, like [BCILAB](https://github.com/sccn/BCILAB) for analysis to trigger responses in the Unity display.
|
||||
To use LSL with the NodeJS SDK, go to our [labstreaminglayer example](https://github.com/OpenBCI/OpenBCI_NodeJS/tree/master/examples/labstreaminglayer), which contains code that is ready to start an LSL stream of OpenBCI data.
|
||||
|
||||
This requires OpenBCI_NodeJS exporting data into LSL. Currently, there does not exist a pre-built NodeJS module for LSL, though LSL comes with tools that could possibly allow creation of one. In the meantime, the simpler route is to use a concurrent python script (driven by NodeJS module [python-shell](https://www.npmjs.com/package/python-shell)) to handoff the data to LSL for you, like so:
|
||||
Follow the directions in the [readme](https://github.com/OpenBCI/OpenBCI_NodeJS/blob/master/examples/labstreaminglayer/readme.md) to get started.
|
||||
|
||||
In your NodeJS code, before initializing/connecting to the OpenBCIBoard:
|
||||
```js
|
||||
// Construct LSL Handoff Python Shell
|
||||
var PythonShell = require('python-shell');
|
||||
var lsloutlet = new PythonShell('LslHandoff.py');
|
||||
|
||||
lsloutlet.on('message', function(message){
|
||||
console.log('LslOutlet: ' + message);
|
||||
});
|
||||
console.log('Python Shell Created for LSLHandoff');
|
||||
## <a name="developing"></a> Developing:
|
||||
### <a name="developing-running"></a> Running:
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
In your NodeJS code, when reading samples:
|
||||
```js
|
||||
st = sample.channelData.join(' ')
|
||||
//getTime returns milliseconds since midnight 1970/01/01
|
||||
var s = ''+ sample.timeStamp + ': '+ st
|
||||
lsloutlet.send(s)
|
||||
```
|
||||
### <a name="developing-testing"></a> Testing:
|
||||
|
||||
in LSLHandoff.py:
|
||||
```py
|
||||
from pylsl import StreamInfo, StreamOutlet
|
||||
info = StreamInfo('OpenBCI_EEG', 'EEG', 8, 250, 'float32', '[RANDOM NUMBER HERE]')
|
||||
outlet = StreamOutlet(info)
|
||||
while True:
|
||||
strSample = raw_input().split(': ',1)
|
||||
sample = map(float, strSample[1].split(' '))
|
||||
stamp = float(strSample[0])
|
||||
|
||||
outlet.push_sample(sample, stamp)
|
||||
print('Pushed Sample At: ' + strSample[0])
|
||||
```
|
||||
AUX data would be done the same way in a separate LSL stream.
|
||||
|
||||
## Dev Notes
|
||||
Running
|
||||
-------
|
||||
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
|
||||
## <a name="contribute"></a> Contribute:
|
||||
|
||||
1. Fork it!
|
||||
2. Branch off of `development`: `git checkout development`
|
||||
2. Create your feature branch: `git checkout -b my-new-feature`
|
||||
3. Make changes
|
||||
4. If adding a feature, please add test coverage.
|
||||
5. Ensure tests all pass. (`npm test`)
|
||||
6. Commit your changes: `git commit -m 'Add some feature'`
|
||||
7. Push to the branch: `git push origin my-new-feature`
|
||||
8. Submit a pull request. Make sure it is based off of the `development` branch when submitting! :D
|
||||
|
||||
## <a name="license"></a> License:
|
||||
|
||||
## License
|
||||
MIT
|
||||
|
||||
## <a name="roadmap"></a> Roadmap:
|
||||
|
||||
1. Ganglion integration (2.x)
|
||||
2. Compatible with node streams (3.x)
|
||||
3. Remove factory paradigm from main file (3.x)
|
||||
5. ES6/ES7 total adoption (3.x)
|
||||
4. Browser support (with browser serialport) (x.x)
|
||||
|
||||
+155
@@ -1,3 +1,158 @@
|
||||
# 1.5.2
|
||||
|
||||
### Dependency Package Updates
|
||||
* `performance-now`: from `^0.2.0` to `2.1.0`
|
||||
* `serialport` - from `4.0.1` to `4.0.7`
|
||||
|
||||
### Development Dependency Package Updates
|
||||
* `bluebird`: from `3.4.6` to `3.5.0`
|
||||
* `chai-as-promised`: from `^5.2.0` to `^6.0.0`
|
||||
* `codecov`: from `^1.0.1` to `^2.1.0`
|
||||
* `semistandard`: from `^9.0.0` to `^10.0.0`
|
||||
* `sinon`: from `^1.17.2` to `^2.1.0`
|
||||
* `snazzy`: from `^5.0.0` to `^6.0.0`
|
||||
|
||||
# 1.5.1
|
||||
|
||||
### New Features
|
||||
* Add new example for Lab stream layer (#139) thanks @gabrielibagon
|
||||
|
||||
### Breaking changes
|
||||
* Removed `impedanceCalculationForChannel()` and `impedanceCalculationForAllChannels` from `OpenBCISample.js`
|
||||
|
||||
### Bug Fixes
|
||||
* Fixes #28- Impedance not working properly.
|
||||
|
||||
# 1.5.0
|
||||
|
||||
### New Features
|
||||
* New simulator option `simulatorDaisyModuleCanBeAttached` - Boolean, deafults to true, allows the simulation of the a hot swapped daisy board or simulates a misinformed module.
|
||||
* New `EventEmitter` - `hardSet` - for when the module detects the board is not configured as the options for the module intended and tries to save itself. i.e. when the `daisy` option is `true` and a soft reset message is parsed and the module determines that a daisy was not detected, the module will emit `hardSet` then send an attach daisy command to recover. Either `error` will be emitted if unable to attach or `ready` will be emitted if success.
|
||||
* Add example for streaming with `daisy` and `hardSet`.
|
||||
|
||||
### Breaking changes
|
||||
* `.setInfoForBoardType()` changed to `.overrideInfoForBoardType()` to elevate it's dangerous nature.
|
||||
* `.setMaxChannels()` changed to `.hardSetBoardType()` and input changed from numerical to string: 8 and 16 to `default` and `daisy` respectively.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixes #131 - 16 chan not working by sending a channel command and parsing the return.
|
||||
* Fixed bug where end of transmission characters would not be ejected from buffer.
|
||||
|
||||
### Enhancements
|
||||
* Separated radio tests from main board test file.
|
||||
|
||||
# 1.4.4
|
||||
|
||||
### New Features
|
||||
* Set max number of channels for the board to use with `.setMaxChannels()` see readme.md
|
||||
* Set the core info object that drives the module with `.overrideInfoForBoardType()` see readme.md
|
||||
* Get info for the core object that drives the module with `.getInfo()` see readme.md
|
||||
|
||||
### Work In Progress
|
||||
* Bug where daisy would sometimes not be recognized which destroyed all data.
|
||||
|
||||
# 1.4.3
|
||||
|
||||
### New examples
|
||||
* Add example of node to python
|
||||
|
||||
# 1.4.2
|
||||
|
||||
### New examples
|
||||
* Add example of debug
|
||||
* Add example of get streaming
|
||||
|
||||
# 1.4.1
|
||||
|
||||
### Bug Fixes
|
||||
* Fixes bug where extra data after EOT (`$$$`) was dumped by preserving the poriton after the EOT for further decomposition.
|
||||
* Fixes bug where any calls to channel set would actually break the openBCISample code as the channelSettingsArray contained an undefined.
|
||||
* Writes promises resolve when they are actually sent over the serial port.
|
||||
|
||||
# 1.4.0
|
||||
|
||||
### New Features
|
||||
|
||||
* Three new initialization options: `simulatorFragmentation`, `simulatorBufferSize`, and `simulatorLatencyTimer`. Together, these enable a more _realistic_ serial port simulation, mimicking different potential user computer systems.
|
||||
* New option `debug` gives a live dump of serial traffic on the console if enabled
|
||||
* New API function `.isConnected()` to check if communications are active.
|
||||
* New API function `.isStreaming()` to check if samples are coming from the board.
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Implement and adapt semi-standard code style. Closes #83
|
||||
* autoFindOpenBCIBoard now notices and uses the stock dongle on Linux
|
||||
* 'synced' object now has `error` property, null on good syncs, error description on bad syncs.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The setting for simulatorInjectLineNoise has changed from `None` to `none`
|
||||
* connect() will now fail if already connected
|
||||
* The constructor will throw an error now if an invalid option is passed
|
||||
* The `.connected` property has been removed, replaced by `.isConnected()`. Removed from docs.
|
||||
* The `.streaming` property has been removed, replaced by `.isStreaming()`. Removed from docs.
|
||||
* An error event will be emitted if sntp fails to initialize on construction
|
||||
* The simulator will no longer communicate when disconnected
|
||||
* Promises returned by writes will now only resolve after the write has been sent
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed bug where early packet fragments were dropped after board reset
|
||||
* Fixed bug where time sync replies that began a buffered chunk were ignored
|
||||
* Fixed bug where simulator would output wrong version in its reset message
|
||||
* Fixed bug where resources were not cleaned up if connect was called twice
|
||||
* Fixed bug where serial data was written after disconnection
|
||||
* Fixed bug where unexpected disconnection was not detected
|
||||
* Fixed bug where promises could lead to out of order packet processing.
|
||||
|
||||
# 1.3.3
|
||||
|
||||
### New Features
|
||||
|
||||
* Add `timeOffsetMaster` to object emitted when bad time sync.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed log statement on impedance setting function
|
||||
* Remove event emitter with time sync on reject of sync clock full
|
||||
|
||||
# 1.3.2
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Added master time offset `timeOffsetMaster` to `syncObj` which is a running average across sync attempts.
|
||||
|
||||
# 1.3.1
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed bug where `connected` and `streaming` were not set in constructor
|
||||
|
||||
# 1.3.0
|
||||
|
||||
### New Features
|
||||
|
||||
* Add dropped packet detection, new event `droppedPacket` can be added to get an array of dropped packet numbers in the case of the dropped packet event.
|
||||
|
||||
# 1.2.3
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Add table of contents to read me
|
||||
* Reduce size of repo by removing impedance test report
|
||||
|
||||
# 1.2.2
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Upgrade serialport to 4.x
|
||||
|
||||
# 1.2.1
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed bug where set channel function allowed for channel 0 to be set. Cannot set system to channel 0; lower limit is 1.
|
||||
|
||||
# 1.2.0
|
||||
|
||||
### New Features
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* This is an example of debugging the board. Thanks to Karl @baffo32
|
||||
* On windows you should run with PowerShell not git bash.
|
||||
* Install
|
||||
* [nodejs](https://nodejs.org/en/)
|
||||
*
|
||||
* To run:
|
||||
* change directory to this file `cd examples/debug`
|
||||
* do `npm install`
|
||||
* then `npm start`
|
||||
*/
|
||||
|
||||
var stream = true;
|
||||
var debug = true; // Pretty print any bytes in and out... it's amazing...
|
||||
var verbose = true; // Adds verbosity to functions
|
||||
var OpenBCIBoard = require('openbci').OpenBCIBoard;
|
||||
|
||||
var ourBoard = new OpenBCIBoard({
|
||||
debug: debug,
|
||||
verbose: verbose
|
||||
});
|
||||
|
||||
ourBoard.autoFindOpenBCIBoard().then(portName => {
|
||||
if (portName) {
|
||||
/**
|
||||
* Connect to the board with portName
|
||||
* Only works if one board is plugged in
|
||||
* i.e. ourBoard.connect(portName).....
|
||||
*/
|
||||
// Call to connect
|
||||
ourBoard.connect(portName).then(() => {
|
||||
console.log(`connected`);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(`connect: ${err}`);
|
||||
});
|
||||
} else {
|
||||
/** Unable to auto find OpenBCI board */
|
||||
console.log('Unable to auto find OpenBCI board');
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The board is ready to start streaming after the ready function is fired.
|
||||
*/
|
||||
var readyFunc = () => {
|
||||
// Get the sample rate after 'ready'
|
||||
if (stream) {
|
||||
ourBoard.streamStart()
|
||||
.catch(err => {
|
||||
console.log(`stream start: ${err}`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var sampleFunc = sample => {
|
||||
/**
|
||||
* Checkout the README.md for all other API functions.
|
||||
* We support every feature.
|
||||
* */
|
||||
};
|
||||
|
||||
// Subscribe to your functions
|
||||
ourBoard.on('ready', readyFunc);
|
||||
ourBoard.on('sample', sampleFunc);
|
||||
|
||||
function exitHandler (options, err) {
|
||||
if (options.cleanup) {
|
||||
if (verbose) console.log('clean');
|
||||
ourBoard.removeAllListeners();
|
||||
/** Do additional clean up here */
|
||||
}
|
||||
if (err) console.log(err.stack);
|
||||
if (options.exit) {
|
||||
if (verbose) console.log('exit');
|
||||
if (stream) {
|
||||
ourBoard.streamStop().catch(console.log);
|
||||
}
|
||||
ourBoard.disconnect().catch(console.log);
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const rl = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
rl.on('SIGINT', function () {
|
||||
process.emit('SIGINT');
|
||||
});
|
||||
}
|
||||
|
||||
// do something when app is closing
|
||||
process.on('exit', exitHandler.bind(null, {
|
||||
cleanup: true
|
||||
}));
|
||||
|
||||
// catches ctrl+c event
|
||||
process.on('SIGINT', exitHandler.bind(null, {
|
||||
exit: true
|
||||
}));
|
||||
|
||||
// catches uncaught exceptions
|
||||
process.on('uncaughtException', exitHandler.bind(null, {
|
||||
exit: true
|
||||
}));
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "debug",
|
||||
"version": "1.0.0",
|
||||
"description": "Debug example",
|
||||
"main": "debug.js",
|
||||
"scripts": {
|
||||
"start": "node debug.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"debug"
|
||||
],
|
||||
"author": "AJ Keller",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"openbci": "^1.5.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* This is an example from the readme.md
|
||||
* On windows you should run with PowerShell not git bash.
|
||||
* Install
|
||||
* [nodejs](https://nodejs.org/en/)
|
||||
*
|
||||
* To run:
|
||||
* change directory to this file `cd examples/debug`
|
||||
* do `npm install`
|
||||
* then `npm start`
|
||||
*/
|
||||
var debug = false; // Pretty print any bytes in and out... it's amazing...
|
||||
var verbose = true; // Adds verbosity to functions
|
||||
|
||||
var OpenBCIBoard = require('openbci').OpenBCIBoard;
|
||||
var ourBoard = new OpenBCIBoard({
|
||||
debug: debug,
|
||||
verbose: verbose
|
||||
});
|
||||
|
||||
ourBoard.autoFindOpenBCIBoard().then(portName => {
|
||||
if (portName) {
|
||||
/**
|
||||
* Connect to the board with portName
|
||||
* Only works if one board is plugged in
|
||||
* i.e. ourBoard.connect(portName).....
|
||||
*/
|
||||
ourBoard.connect(portName) // Port name is a serial port name, see `.listPorts()`
|
||||
.then(() => {
|
||||
ourBoard.on('ready', () => {
|
||||
ourBoard.streamStart();
|
||||
ourBoard.on('sample', (sample) => {
|
||||
/** Work with sample */
|
||||
for (let 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."
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
/** Unable to auto find OpenBCI board */
|
||||
console.log('Unable to auto find OpenBCI board');
|
||||
}
|
||||
});
|
||||
|
||||
function exitHandler (options, err) {
|
||||
if (options.cleanup) {
|
||||
if (verbose) console.log('clean');
|
||||
ourBoard.removeAllListeners();
|
||||
/** Do additional clean up here */
|
||||
}
|
||||
if (err) console.log(err.stack);
|
||||
if (options.exit) {
|
||||
if (verbose) console.log('exit');
|
||||
ourBoard.disconnect().catch(console.log);
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const rl = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
rl.on('SIGINT', function () {
|
||||
process.emit('SIGINT');
|
||||
});
|
||||
}
|
||||
|
||||
// do something when app is closing
|
||||
process.on('exit', exitHandler.bind(null, {
|
||||
cleanup: true
|
||||
}));
|
||||
|
||||
// catches ctrl+c event
|
||||
process.on('SIGINT', exitHandler.bind(null, {
|
||||
exit: true
|
||||
}));
|
||||
|
||||
// catches uncaught exceptions
|
||||
process.on('uncaughtException', exitHandler.bind(null, {
|
||||
exit: true
|
||||
}));
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "get-streaming",
|
||||
"version": "1.0.0",
|
||||
"description": "Get streaming example",
|
||||
"main": "getStreaming.js",
|
||||
"scripts": {
|
||||
"start": "node getStreaming.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"get"
|
||||
],
|
||||
"author": "AJ Keller",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"openbci": "^1.5.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* This is an example from the readme.md
|
||||
* On windows you should run with PowerShell not git bash.
|
||||
* Install
|
||||
* [nodejs](https://nodejs.org/en/)
|
||||
*
|
||||
* To run:
|
||||
* change directory to this file `cd examples/debug`
|
||||
* do `npm install`
|
||||
* then `npm start`
|
||||
*/
|
||||
var debug = false; // Pretty print any bytes in and out... it's amazing...
|
||||
var verbose = true; // Adds verbosity to functions
|
||||
|
||||
var OpenBCIBoard = require('openbci').OpenBCIBoard;
|
||||
var ourBoard = new OpenBCIBoard({
|
||||
boardType: 'daisy',
|
||||
debug: debug,
|
||||
hardSet: true,
|
||||
verbose: verbose
|
||||
});
|
||||
|
||||
ourBoard.on('error', (err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
ourBoard.autoFindOpenBCIBoard().then(portName => {
|
||||
if (portName) {
|
||||
/**
|
||||
* Connect to the board with portName
|
||||
* Only works if one board is plugged in
|
||||
* i.e. ourBoard.connect(portName).....
|
||||
*/
|
||||
ourBoard.connect(portName) // Port name is a serial port name, see `.listPorts()`
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.streamStart();
|
||||
ourBoard.on('sample', (sample) => {
|
||||
/** Work with sample */
|
||||
for (let 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 16: -0.00001875 Volts."
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
/** Unable to auto find OpenBCI board */
|
||||
console.log('Unable to auto find OpenBCI board');
|
||||
}
|
||||
});
|
||||
|
||||
function exitHandler (options, err) {
|
||||
if (options.cleanup) {
|
||||
if (verbose) console.log('clean');
|
||||
ourBoard.removeAllListeners();
|
||||
/** Do additional clean up here */
|
||||
}
|
||||
if (err) console.log(err.stack);
|
||||
if (options.exit) {
|
||||
if (verbose) console.log('exit');
|
||||
ourBoard.disconnect().catch(console.log);
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const rl = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
rl.on('SIGINT', function () {
|
||||
process.emit('SIGINT');
|
||||
});
|
||||
}
|
||||
|
||||
// do something when app is closing
|
||||
process.on('exit', exitHandler.bind(null, {
|
||||
cleanup: true
|
||||
}));
|
||||
|
||||
// catches ctrl+c event
|
||||
process.on('SIGINT', exitHandler.bind(null, {
|
||||
exit: true
|
||||
}));
|
||||
|
||||
// catches uncaught exceptions
|
||||
process.on('uncaughtException', exitHandler.bind(null, {
|
||||
exit: true
|
||||
}));
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "get-streaming-daisy",
|
||||
"version": "1.0.0",
|
||||
"description": "Get streaming with hard set daisy example",
|
||||
"main": "getStreaming.js",
|
||||
"scripts": {
|
||||
"start": "node getStreamingDaisy.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"get"
|
||||
],
|
||||
"author": "AJ Keller",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"openbci": "^1.5.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* This is an example from the readme.md
|
||||
* On windows you should run with PowerShell not git bash.
|
||||
* Install
|
||||
* [nodejs](https://nodejs.org/en/)
|
||||
*
|
||||
* To run:
|
||||
* change directory to this file `cd examples/debug`
|
||||
* do `npm install`
|
||||
* then `npm start`
|
||||
*/
|
||||
var debug = false; // Pretty print any bytes in and out... it's amazing...
|
||||
var verbose = true; // Adds verbosity to functions
|
||||
|
||||
var OpenBCIBoard = require('openbci').OpenBCIBoard;
|
||||
var ourBoard = new OpenBCIBoard({
|
||||
debug: debug,
|
||||
verbose: verbose
|
||||
});
|
||||
var k = require('openbci').OpenBCIConstants;
|
||||
|
||||
let startedImpedance = false;
|
||||
let iBuffer = [];
|
||||
let count = 0;
|
||||
const window = 50;
|
||||
|
||||
ourBoard.autoFindOpenBCIBoard().then(portName => {
|
||||
if (portName) {
|
||||
/**
|
||||
* Connect to the board with portName
|
||||
* Only works if one board is plugged in
|
||||
* i.e. ourBoard.connect(portName).....
|
||||
*/
|
||||
ourBoard.connect(portName) // Port name is a serial port name, see `.listPorts()`
|
||||
.then(() => {
|
||||
ourBoard.on('ready', () => {
|
||||
ourBoard.streamStart();
|
||||
ourBoard.on('sample', (sample) => {
|
||||
if (startedImpedance === false) {
|
||||
startedImpedance = true;
|
||||
k.getImpedanceSetter(1, false, true)
|
||||
.then((commands) => {
|
||||
return ourBoard.write(commands);
|
||||
})
|
||||
.then(() => {
|
||||
console.log('wrote commands to board');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('errr', err);
|
||||
});
|
||||
}
|
||||
/** Work with sample */
|
||||
const chan1ValInVolts = sample.channelData[0];
|
||||
|
||||
// const impedance = chan1ValInVolts / k.OBCILeadOffDriveInAmps;
|
||||
|
||||
// console.log(`impedance:\t${impedance} kOhms`);
|
||||
iBuffer.push(chan1ValInVolts);
|
||||
count++;
|
||||
if (count >= window) {
|
||||
let max = 0.0; // sumSquared
|
||||
for (let i = 0; i < window; i++) {
|
||||
if (iBuffer[i] > max) {
|
||||
max = iBuffer[i];
|
||||
}
|
||||
// sumSquared += iBuffer[i] * iBuffer[i];
|
||||
}
|
||||
let min = 0.0;
|
||||
for (let i = 0; i < window; i++) {
|
||||
if (iBuffer[i] < min) {
|
||||
min = iBuffer[i];
|
||||
}
|
||||
// sumSquared += iBuffer[i] * iBuffer[i];
|
||||
}
|
||||
const vP2P = max - min; // peak to peak
|
||||
|
||||
console.log(`impedance: \t${vP2P / 2 / k.OBCILeadOffDriveInAmps}`);
|
||||
// console.log(`impedance: ${vRms/k.OBCILeadOffDriveInAmps}`);
|
||||
|
||||
// const mean_squared = sumSquared / window;
|
||||
// const root_mean_squared = Math.sqrt(mean_squared);
|
||||
// console.log(`impedance: ${root_mean_squared/k.OBCILeadOffDriveInAmps}`);
|
||||
|
||||
count = 0;
|
||||
iBuffer = [];
|
||||
}
|
||||
// console.log(`uV:\t${chan1ValInVolts/(10*6)}\nimpedance:\t${impedance}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
/** Unable to auto find OpenBCI board */
|
||||
console.log('Unable to auto find OpenBCI board');
|
||||
}
|
||||
});
|
||||
|
||||
function exitHandler (options, err) {
|
||||
if (options.cleanup) {
|
||||
if (verbose) console.log('clean');
|
||||
/** Do additional clean up here */
|
||||
ourBoard.disconnect().catch(console.log);
|
||||
ourBoard.removeAllListeners();
|
||||
}
|
||||
if (err) console.log(err.stack);
|
||||
if (options.exit) {
|
||||
if (verbose) console.log('exit');
|
||||
ourBoard.streamStop()
|
||||
.then(() => {
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const rl = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
rl.on('SIGINT', function () {
|
||||
process.emit('SIGINT');
|
||||
});
|
||||
}
|
||||
|
||||
// do something when app is closing
|
||||
process.on('exit', exitHandler.bind(null, {
|
||||
cleanup: true
|
||||
}));
|
||||
|
||||
// catches ctrl+c event
|
||||
process.on('SIGINT', exitHandler.bind(null, {
|
||||
exit: true
|
||||
}));
|
||||
|
||||
// catches uncaught exceptions
|
||||
process.on('uncaughtException', exitHandler.bind(null, {
|
||||
exit: true
|
||||
}));
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "get-streaming",
|
||||
"version": "1.0.0",
|
||||
"description": "Get impedance example",
|
||||
"main": "getStreaming.js",
|
||||
"scripts": {
|
||||
"start": "node impedance.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"get"
|
||||
],
|
||||
"author": "AJ Keller",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"openbci": "^1.5.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import json
|
||||
import sys
|
||||
import numpy as np
|
||||
import time
|
||||
import zmq
|
||||
from pylsl import StreamInfo, StreamOutlet
|
||||
|
||||
class Interface:
|
||||
def __init__(self, verbose=False):
|
||||
context = zmq.Context()
|
||||
self._socket = context.socket(zmq.PAIR)
|
||||
self._socket.connect("tcp://localhost:3004")
|
||||
|
||||
self.verbose = verbose
|
||||
|
||||
if self.verbose:
|
||||
print("Client Ready!")
|
||||
|
||||
# Send a quick message to tell node process we are up and running
|
||||
self.send(json.dumps({
|
||||
'action': 'started',
|
||||
'command': 'status',
|
||||
'message': time.time()*1000.0
|
||||
}))
|
||||
|
||||
def send(self, msg):
|
||||
"""
|
||||
Sends a message to TCP server
|
||||
:param msg: str
|
||||
A string to send to node TCP server, could be a JSON dumps...
|
||||
:return: None
|
||||
"""
|
||||
if self.verbose:
|
||||
print('<- out ' + msg)
|
||||
self._socket.send_string(msg)
|
||||
return
|
||||
|
||||
def recv(self):
|
||||
"""
|
||||
Checks the ZeroMQ for data
|
||||
:return: str
|
||||
String of data
|
||||
"""
|
||||
return self._socket.recv()
|
||||
|
||||
|
||||
def initializeOutlet(interface):
|
||||
"""
|
||||
Initializes and returns an LSL outlet
|
||||
:param interface: Interface
|
||||
the Python interface to communicate with node
|
||||
:return: StreamOutlet
|
||||
returns a labstreaminglayer StreamOutlet
|
||||
"""
|
||||
numChans = None
|
||||
while not numChans:
|
||||
msg = interface.recv()
|
||||
try:
|
||||
dicty = json.loads(msg)
|
||||
numChans = dicty.get('numChans')
|
||||
sampleRate = dicty.get('sampleRate')
|
||||
except ValueError as e:
|
||||
print(e)
|
||||
info = StreamInfo('OpenBCI_EEG', 'EEG', numChans, sampleRate, 'float32', 'myuid34234')
|
||||
outlet = StreamOutlet(info)
|
||||
print('init')
|
||||
return outlet
|
||||
|
||||
def main(argv):
|
||||
verbose = False
|
||||
# Create a new python interface.
|
||||
interface = Interface(verbose)
|
||||
# Create a new labstreaminglayer outlet
|
||||
outlet = initializeOutlet(interface);
|
||||
|
||||
# Stream incoming data to LSL
|
||||
while True:
|
||||
msg = interface.recv()
|
||||
try:
|
||||
dicty = json.loads(msg)
|
||||
message = dicty.get('message')
|
||||
data=message.get('channelData')
|
||||
timeStamp = message.get('timeStamp')
|
||||
outlet.push_sample(data,timeStamp)
|
||||
except BaseException as e:
|
||||
print(e)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* This is an example from the readme.md
|
||||
* On windows you should run with PowerShell not git bash.
|
||||
* Install
|
||||
* [nodejs](https://nodejs.org/en/)
|
||||
*
|
||||
* To run:
|
||||
* change directory to this file `cd examples/debug`
|
||||
* do `npm install`
|
||||
* then `npm start`
|
||||
*/
|
||||
var OpenBCIBoard = require('openbci').OpenBCIBoard;
|
||||
var portPub = 'tcp://127.0.0.1:3004';
|
||||
var zmq = require('zmq-prebuilt');
|
||||
var socket = zmq.socket('pair');
|
||||
var debug = false; // Pretty print any bytes in and out... it's amazing...
|
||||
var verbose = true; // Adds verbosity to functions
|
||||
|
||||
var ourBoard = new OpenBCIBoard({
|
||||
simulatorFirmwareVersion: 'v2',
|
||||
debug: debug,
|
||||
verbose: verbose
|
||||
});
|
||||
|
||||
var timeSyncPossible = false;
|
||||
var resyncPeriodMin = 1;
|
||||
var secondsInMinute = 60;
|
||||
var numChans = 8;
|
||||
var resyncPeriod = ourBoard.sampleRate() * resyncPeriodMin * secondsInMinute;
|
||||
|
||||
ourBoard.autoFindOpenBCIBoard().then(portName => {
|
||||
if (portName) {
|
||||
/**
|
||||
* Connect to the board with portName
|
||||
* i.e. ourBoard.connect(portName).....
|
||||
*/
|
||||
// Call to connect
|
||||
ourBoard.connect(portName)
|
||||
.then(() => {
|
||||
ourBoard.on('ready', () => {
|
||||
// Get the sample rate after 'ready'
|
||||
numChans = ourBoard.numberOfChannels();
|
||||
if (numChans === 16) {
|
||||
ourBoard.overrideInfoForBoardType('daisy');
|
||||
}
|
||||
|
||||
// Find out if you can even time sync, you must be using v2 and this is only accurate after a `.softReset()` call which is called internally on `.connect()`. We parse the `.softReset()` response for the presence of firmware version 2 properties.
|
||||
timeSyncPossible = ourBoard.usingVersionTwoFirmware();
|
||||
|
||||
sendToPython({'numChans': numChans, 'sampleRate': ourBoard.sampleRate()});
|
||||
if (timeSyncPossible) {
|
||||
ourBoard.streamStart()
|
||||
.catch(err => {
|
||||
console.log(`stream start: ${err}`);
|
||||
});
|
||||
} else {
|
||||
console.log('not able to time sync');
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(`connect: ${err}`);
|
||||
});
|
||||
} else {
|
||||
/** Unable to auto find OpenBCI board */
|
||||
console.log('Unable to auto find OpenBCI board');
|
||||
}
|
||||
});
|
||||
|
||||
var sampleFunc = sample => {
|
||||
if (sample._count % resyncPeriod === 0) {
|
||||
ourBoard.syncClocksFull()
|
||||
.then(syncObj => {
|
||||
// Sync was successful
|
||||
if (syncObj.valid) {
|
||||
// Log the object to check it out!
|
||||
console.log(`timeOffset`, syncObj.timeOffsetMaster);
|
||||
} else {
|
||||
// Retry it
|
||||
console.log(`Was not able to sync... retry!`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (sample.timeStamp) { // true after the first successful sync
|
||||
if (sample.timeStamp < 10 * 60 * 60 * 1000) { // Less than 10 hours
|
||||
console.log(`Bad time sync ${sample.timeStamp}`);
|
||||
} else {
|
||||
sendToPython({
|
||||
action: 'process',
|
||||
command: 'sample',
|
||||
message: sample
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Subscribe to your functions
|
||||
ourBoard.on('sample', sampleFunc);
|
||||
|
||||
// ZMQ
|
||||
socket.bind(portPub, function (err) {
|
||||
if (err) throw err;
|
||||
console.log(`bound to ${portPub}`);
|
||||
});
|
||||
|
||||
/**
|
||||
* Used to send a message to the Python process.
|
||||
* @param {Object} interProcessObject The standard inter-process object.
|
||||
* @return {None}
|
||||
*/
|
||||
var sendToPython = (interProcessObject, verbose) => {
|
||||
if (verbose) {
|
||||
console.log(`<- out ${JSON.stringify(interProcessObject)}`);
|
||||
}
|
||||
if (socket) {
|
||||
socket.send(JSON.stringify(interProcessObject));
|
||||
}
|
||||
};
|
||||
|
||||
function exitHandler (options, err) {
|
||||
if (options.cleanup) {
|
||||
if (verbose) console.log('clean');
|
||||
/** Do additional clean up here */
|
||||
}
|
||||
if (err) console.log(err.stack);
|
||||
if (options.exit) {
|
||||
if (verbose) console.log('exit');
|
||||
ourBoard.disconnect().catch(console.log);
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const rl = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
rl.on('SIGINT', function () {
|
||||
process.emit('SIGINT');
|
||||
});
|
||||
}
|
||||
|
||||
// do something when app is closing
|
||||
process.on('exit', exitHandler.bind(null, {
|
||||
cleanup: true
|
||||
}));
|
||||
|
||||
// catches ctrl+c event
|
||||
process.on('SIGINT', exitHandler.bind(null, {
|
||||
exit: true
|
||||
}));
|
||||
|
||||
// catches uncaught exceptions
|
||||
process.on('uncaughtException', exitHandler.bind(null, {
|
||||
exit: true
|
||||
}));
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "labstreaminglayer",
|
||||
"version": "1.0.0",
|
||||
"description": "labstreaminglayer example",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "concurrently --kill-others \"python handoff.py\" \"node index.js\"",
|
||||
"start-node": "node index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"python",
|
||||
"openbci",
|
||||
"node",
|
||||
"labstreaminglayer",
|
||||
"LSL"
|
||||
],
|
||||
"author": "AJ Keller",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"openbci": "^1.4.2",
|
||||
"zmq-prebuilt": "^2.1.0"
|
||||
},
|
||||
"devEngines": {
|
||||
"node": "<=6.x",
|
||||
"npm": ">=3.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^3.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
# OpenBCI Node SDK to Lab Streaming Layer
|
||||
|
||||
## About
|
||||
|
||||
This code provides an example of how to stream OpenBCI data through the [lab streaming layer](https://github.com/sccn/labstreaminglayer) using the NodeJS SDK.
|
||||
|
||||
Follow the steps in this README to start streaming. The code is ready to run as-is, but can be modified and extended to customize how you are sending your data. This is designed to be used with the **OpenBCI Cyton** (for **Ganglion support**, see the [Ganglion Node SDK](https://github.com/OpenBCI/OpenBCI_NodeJS_Ganglion/tree/master/examples/labstreaminglayer)).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* [Python](https://www.python.org/downloads/)
|
||||
* [ZeroMQ](http://zeromq.org/bindings:python)
|
||||
|
||||
```py
|
||||
pip install pyzmq
|
||||
```
|
||||
* [Node.js LTS](https://nodejs.org/en/)
|
||||
* [Lab Streaming Layer](https://github.com/sccn/labstreaminglayer)
|
||||
|
||||
```py
|
||||
pip install pylsl
|
||||
```
|
||||
|
||||
|
||||
## Installation
|
||||
First, install Python dependencies:
|
||||
```bash
|
||||
python setup.py install
|
||||
```
|
||||
Next, install NodeJS dependencies:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
Note: depending on your computer settings, you may need to run as administrator or with `sudo`.
|
||||
|
||||
## Running
|
||||
```
|
||||
npm start
|
||||
```
|
||||
For running just the node, for example if you were running the python in a separate ide and debugging, it's useful.
|
||||
```python
|
||||
npm run start-node
|
||||
```
|
||||
Note: depending on your computer settings, you may need to run as administrator or with `sudo`.
|
||||
|
||||
## Writing Lab Streaming Layer Code
|
||||
If you would like to use lab streaming layer in a custom OpenBCI NodeJS application, you must include an instance of the OpenBCI NodeJS library and an instance of a Python interface. A basic example is shown below:
|
||||
|
||||
index.js
|
||||
```js
|
||||
var OpenBCIBoard = require('openbci').OpenBCIBoard;
|
||||
var portPub = 'tcp://127.0.0.1:3004';
|
||||
var zmq = require('zmq-prebuilt');
|
||||
var socket = zmq.socket('pair');
|
||||
var ourBoard = new OpenBCIBoard();
|
||||
|
||||
socket.bind(portPub);
|
||||
|
||||
ourBoard.autoFindOpenBCIBoard().then(portName => {
|
||||
if (portName) {
|
||||
ourBoard.connect(portName) // Port name is a serial port name, see `.listPorts()`
|
||||
.then(() => {
|
||||
ourBoard.on('ready',() => {
|
||||
ourBoard.streamStart();
|
||||
ourBoard.on('sample',(sample) => {
|
||||
if (socket) {
|
||||
socket.send(JSON.stringify({message: sample}));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.log('Unable to auto find OpenBCI board');
|
||||
}
|
||||
});
|
||||
|
||||
/* Insert additional exit handlers and cleanup below*/
|
||||
```
|
||||
|
||||
handoff.py
|
||||
```python
|
||||
import json
|
||||
import zmq
|
||||
from pylsl import StreamInfo, StreamOutlet
|
||||
|
||||
# Create ZMQ socket
|
||||
context = zmq.Context()
|
||||
_socket = context.socket(zmq.PAIR)
|
||||
_socket.connect("tcp://localhost:3004")
|
||||
|
||||
# Create a new labstreaminglayer outlet
|
||||
numChans = 8;
|
||||
sampleRate = 250;
|
||||
info = StreamInfo('OpenBCI_EEG', 'EEG', numChans, sampleRate, 'float32', 'openbci_12345')
|
||||
outlet = StreamOutlet(info)
|
||||
# Stream incoming data to LSL
|
||||
while True:
|
||||
msg = _socket.recv()
|
||||
try:
|
||||
dicty = json.loads(msg)
|
||||
message = dicty.get('message')
|
||||
data=message.get('channelData')
|
||||
timeStamp = message.get('timeStamp')
|
||||
outlet.push_sample(data,timeStamp)
|
||||
except BaseException as e:
|
||||
print(e)
|
||||
```
|
||||
|
||||
## Contributing
|
||||
Please PR if you have code to contribute!
|
||||
@@ -0,0 +1,12 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(name='openbci-node-labstreaminglayer',
|
||||
version='0.0.1',
|
||||
description='Labstreaminglayer with NodeJS',
|
||||
url='',
|
||||
author='AJ Keller',
|
||||
author_email='pushtheworldllc@gmail.com',
|
||||
license='MIT',
|
||||
packages=find_packages(),
|
||||
install_requires=['numpy', 'pyzmq','pylsl'],
|
||||
zip_safe=False)
|
||||
@@ -0,0 +1,117 @@
|
||||
import json
|
||||
import sys
|
||||
import numpy as np
|
||||
import time
|
||||
import zmq
|
||||
|
||||
|
||||
class Interface:
|
||||
def __init__(self, verbose=False):
|
||||
context = zmq.Context()
|
||||
self._socket = context.socket(zmq.PAIR)
|
||||
self._socket.connect("tcp://localhost:3004")
|
||||
|
||||
self.verbose = verbose
|
||||
|
||||
if self.verbose:
|
||||
print "Client Ready!"
|
||||
|
||||
# Send a quick message to tell node process we are up and running
|
||||
self.send(json.dumps({
|
||||
'action': 'started',
|
||||
'command': 'status',
|
||||
'message': time.time()*1000.0
|
||||
}))
|
||||
|
||||
def send(self, msg):
|
||||
"""
|
||||
Sends a message to TCP server
|
||||
:param msg: str
|
||||
A string to send to node TCP server, could be a JSON dumps...
|
||||
:return: None
|
||||
"""
|
||||
if self.verbose:
|
||||
print '<- out ' + msg
|
||||
self._socket.send(msg)
|
||||
return
|
||||
|
||||
def recv(self):
|
||||
"""
|
||||
Checks the ZeroMQ for data
|
||||
:return: str
|
||||
String of data
|
||||
"""
|
||||
return self._socket.recv()
|
||||
|
||||
|
||||
class RingBuffer(np.ndarray):
|
||||
"""A multidimensional ring buffer."""
|
||||
|
||||
def __new__(cls, input_array):
|
||||
obj = np.asarray(input_array).view(cls)
|
||||
return obj
|
||||
|
||||
def __array_finalize__(self, obj):
|
||||
if obj is None:
|
||||
return
|
||||
|
||||
def __array_wrap__(self, out_arr, context=None):
|
||||
return np.ndarray.__array_wrap__(self, out_arr, context)
|
||||
|
||||
def append(self, x):
|
||||
"""Adds element x to the ring buffer."""
|
||||
x = np.asarray(x)
|
||||
self[:, :-1] = self[:, 1:]
|
||||
self[:, -1] = x
|
||||
|
||||
|
||||
def main(argv):
|
||||
nb_chan = 8
|
||||
verbose = True
|
||||
|
||||
# Create a new python interface.
|
||||
interface = Interface(verbose=verbose)
|
||||
# Signal buffer
|
||||
signal = RingBuffer(np.zeros((nb_chan + 1, 2500)))
|
||||
|
||||
while True:
|
||||
msg = interface.recv()
|
||||
try:
|
||||
dicty = json.loads(msg)
|
||||
action = dicty.get('action')
|
||||
command = dicty.get('command')
|
||||
message = dicty.get('message')
|
||||
|
||||
if command == 'sample':
|
||||
if action == 'process':
|
||||
# Do sample processing here
|
||||
try:
|
||||
if type(message) is not dict:
|
||||
print "sample is not a dict", message
|
||||
raise ValueError
|
||||
# Get keys of sample
|
||||
data = np.zeros(9)
|
||||
|
||||
data[:-1] = message.get('channelData')
|
||||
data[-1] = message.get('timeStamp')
|
||||
|
||||
# Add data to end of ring buffer
|
||||
signal.append(data)
|
||||
|
||||
print message.get('sampleNumber')
|
||||
except ValueError as e:
|
||||
print e
|
||||
elif command == 'status':
|
||||
if action == 'active':
|
||||
interface.send(json.dumps({
|
||||
'action': 'alive',
|
||||
'command': 'status',
|
||||
'message': time.time() * 1000.0
|
||||
}))
|
||||
|
||||
except BaseException as e:
|
||||
print e
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* This is an example from the readme.md
|
||||
* On windows you should run with PowerShell not git bash.
|
||||
* Install
|
||||
* [nodejs](https://nodejs.org/en/)
|
||||
*
|
||||
* To run:
|
||||
* change directory to this file `cd examples/debug`
|
||||
* do `npm install`
|
||||
* then `npm start`
|
||||
*/
|
||||
var OpenBCIBoard = require('openbci').OpenBCIBoard;
|
||||
var portPub = 'tcp://127.0.0.1:3004';
|
||||
var zmq = require('zmq-prebuilt');
|
||||
var socket = zmq.socket('pair');
|
||||
var simulate = false; // Sends synthetic data
|
||||
var debug = false; // Pretty print any bytes in and out... it's amazing...
|
||||
var verbose = true; // Adds verbosity to functions
|
||||
|
||||
var ourBoard = new OpenBCIBoard({
|
||||
simulate: simulate, // Uncomment to see how it works with simulator!
|
||||
simulatorFirmwareVersion: 'v2',
|
||||
debug: debug,
|
||||
verbose: verbose
|
||||
});
|
||||
|
||||
var timeSyncPossible = false;
|
||||
var resyncPeriodMin = 1;
|
||||
var secondsInMinute = 60;
|
||||
var resyncPeriod = ourBoard.sampleRate() * resyncPeriodMin * secondsInMinute;
|
||||
|
||||
ourBoard.autoFindOpenBCIBoard().then(portName => {
|
||||
if (portName) {
|
||||
/**
|
||||
* Connect to the board with portName
|
||||
* i.e. ourBoard.connect(portName).....
|
||||
*/
|
||||
// Call to connect
|
||||
ourBoard.connect(portName)
|
||||
.then(() => {
|
||||
ourBoard.on('ready', () => {
|
||||
// Find out if you can even time sync, you must be using v2 and this is only accurate after a `.softReset()` call which is called internally on `.connect()`. We parse the `.softReset()` response for the presence of firmware version 2 properties.
|
||||
timeSyncPossible = ourBoard.usingVersionTwoFirmware();
|
||||
|
||||
if (timeSyncPossible) {
|
||||
ourBoard.streamStart()
|
||||
.catch(err => {
|
||||
console.log(`stream start: ${err}`);
|
||||
});
|
||||
} else {
|
||||
console.log('not able to time sync');
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(`connect: ${err}`);
|
||||
});
|
||||
} else {
|
||||
/** Unable to auto find OpenBCI board */
|
||||
console.log('Unable to auto find OpenBCI board');
|
||||
}
|
||||
});
|
||||
|
||||
var sampleFunc = sample => {
|
||||
if (sample._count % resyncPeriod === 0) {
|
||||
ourBoard.syncClocksFull()
|
||||
.then(syncObj => {
|
||||
// Sync was successful
|
||||
if (syncObj.valid) {
|
||||
// Log the object to check it out!
|
||||
console.log(`timeOffset`, syncObj.timeOffsetMaster);
|
||||
} else {
|
||||
// Retry it
|
||||
console.log(`Was not able to sync... retry!`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (sample.timeStamp) { // true after the first successful sync
|
||||
if (sample.timeStamp < 10 * 60 * 60 * 1000) { // Less than 10 hours
|
||||
console.log(`Bad time sync ${sample.timeStamp}`);
|
||||
} else {
|
||||
sendToPython({
|
||||
action: 'process',
|
||||
command: 'sample',
|
||||
message: sample
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Subscribe to your functions
|
||||
ourBoard.on('sample', sampleFunc);
|
||||
|
||||
// ZMQ fun
|
||||
|
||||
socket.bind(portPub, function (err) {
|
||||
if (err) throw err;
|
||||
console.log(`bound to ${portPub}`);
|
||||
});
|
||||
|
||||
/**
|
||||
* Used to send a message to the Python process.
|
||||
* @param {Object} interProcessObject The standard inter-process object.
|
||||
* @return {None}
|
||||
*/
|
||||
var sendToPython = (interProcessObject, verbose) => {
|
||||
if (verbose) {
|
||||
console.log(`<- out ${JSON.stringify(interProcessObject)}`);
|
||||
}
|
||||
if (socket) {
|
||||
socket.send(JSON.stringify(interProcessObject));
|
||||
}
|
||||
};
|
||||
|
||||
var receiveFromPython = (rawData) => {
|
||||
try {
|
||||
let body = JSON.parse(rawData); // five because `resp `
|
||||
processInterfaceObject(body);
|
||||
} catch (err) {
|
||||
console.log('in -> ' + 'bad json');
|
||||
}
|
||||
};
|
||||
|
||||
socket.on('message', receiveFromPython);
|
||||
|
||||
var sendStatus = () => {
|
||||
sendToPython({'action': 'active', 'message': 'ready', 'command': 'status'}, true);
|
||||
};
|
||||
|
||||
sendStatus();
|
||||
|
||||
/**
|
||||
* Process an incoming message
|
||||
* @param {String} body A stringify JSON object that shall be parsed.
|
||||
* @return {None}
|
||||
*/
|
||||
var processInterfaceObject = (body) => {
|
||||
switch (body.command) {
|
||||
case 'status':
|
||||
processStatus(body);
|
||||
break;
|
||||
default:
|
||||
unrecognizedCommand(body);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to process a status related command from TCP IPC.
|
||||
* @param {Object} body
|
||||
* @return {None}
|
||||
*/
|
||||
var processStatus = (body) => {
|
||||
switch (body.action) {
|
||||
case 'started':
|
||||
console.log(`python started @ ${body.message}ms`);
|
||||
break;
|
||||
case 'alive':
|
||||
console.log(`python duplex communication test completed @ ${body.message}ms`);
|
||||
break;
|
||||
default:
|
||||
unrecognizedCommand(body);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
function unrecognizedCommand (body) {
|
||||
console.log(`unrecognizedCommand ${body}`);
|
||||
}
|
||||
|
||||
function exitHandler (options, err) {
|
||||
if (options.cleanup) {
|
||||
if (verbose) console.log('clean');
|
||||
/** Do additional clean up here */
|
||||
}
|
||||
if (err) console.log(err.stack);
|
||||
if (options.exit) {
|
||||
if (verbose) console.log('exit');
|
||||
ourBoard.disconnect().catch(console.log);
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const rl = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
rl.on('SIGINT', function () {
|
||||
process.emit('SIGINT');
|
||||
});
|
||||
}
|
||||
|
||||
// do something when app is closing
|
||||
process.on('exit', exitHandler.bind(null, {
|
||||
cleanup: true
|
||||
}));
|
||||
|
||||
// catches ctrl+c event
|
||||
process.on('SIGINT', exitHandler.bind(null, {
|
||||
exit: true
|
||||
}));
|
||||
|
||||
// catches uncaught exceptions
|
||||
process.on('uncaughtException', exitHandler.bind(null, {
|
||||
exit: true
|
||||
}));
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "python",
|
||||
"version": "1.0.0",
|
||||
"description": "node to python example",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "concurrently --kill-others \"python handoff.py\" \"node index.js\"",
|
||||
"start-node": "node index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"python",
|
||||
"openbci",
|
||||
"node"
|
||||
],
|
||||
"author": "AJ Keller",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"openbci": "^1.4.2",
|
||||
"zmq-prebuilt": "^2.1.0"
|
||||
},
|
||||
"devEngines": {
|
||||
"node": "<=6.x",
|
||||
"npm": ">=3.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^3.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# OpenBCI Node SDK to Python
|
||||
|
||||
## About
|
||||
|
||||
Written to end the struggles of python researchers and developers. ~ written with love by [Push The World!](http://www.pushtheworldllc.com)
|
||||
|
||||
This module has every feature available on the OpenBCI Board.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* [Python 2.7](https://www.python.org/downloads/)
|
||||
* [ZeroMQ](http://zeromq.org/bindings:python)
|
||||
|
||||
```py
|
||||
pip install pyzmq
|
||||
```
|
||||
* [Node.js LTS](https://nodejs.org/en/)
|
||||
|
||||
|
||||
## Installation
|
||||
For Python 2.7 do:
|
||||
```bash
|
||||
python setup.py install
|
||||
```
|
||||
For Node:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
## Running
|
||||
```
|
||||
npm start
|
||||
```
|
||||
For running just the node, for example if you were running the python in a separate ide and debugging, it's useful.
|
||||
```python
|
||||
npm run start-node
|
||||
```
|
||||
|
||||
## Contributing
|
||||
Please PR if you have code to contribute!
|
||||
@@ -0,0 +1,12 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(name='openbci-node-python',
|
||||
version='0.0.1',
|
||||
description='Node to Python the right way',
|
||||
url='',
|
||||
author='AJ Keller',
|
||||
author_email='pushtheworldllc@gmail.com',
|
||||
license='MIT',
|
||||
packages=find_packages(),
|
||||
install_requires=['numpy', 'pyzmq'],
|
||||
zip_safe=False)
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "timesync",
|
||||
"version": "1.0.0",
|
||||
"description": "Time sync example",
|
||||
"main": "timeSync.js",
|
||||
"scripts": {
|
||||
"start": "node timeSync.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"time",
|
||||
"sync"
|
||||
],
|
||||
"author": "AJ Keller",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"openbci": "^1.3.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* This is an example of time syncing every second for one minute. Used with a
|
||||
* real board.
|
||||
* To run:
|
||||
* change directory to this file `cd examples/timeSync`
|
||||
* do `npm install`
|
||||
* then `npm start`
|
||||
*/
|
||||
|
||||
var OpenBCIBoard = require('openbci').OpenBCIBoard;
|
||||
|
||||
var ourBoard = new OpenBCIBoard({});
|
||||
var verbose = true; // Adds verbosity to functions
|
||||
|
||||
var sampleRate = 250; // Default to 250, ALWAYS verify with a call to `.sampleRate()` after 'ready' event!
|
||||
var timeSyncPossible = false;
|
||||
|
||||
ourBoard.autoFindOpenBCIBoard().then(portName => {
|
||||
if (portName) {
|
||||
/**
|
||||
* Connect to the board with portName
|
||||
* i.e. ourBoard.connect(portName).....
|
||||
*/
|
||||
// Call to connect
|
||||
ourBoard.connect(portName).then(() => {
|
||||
console.log(`connected`);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(`connect: ${err}`);
|
||||
});
|
||||
} else {
|
||||
/** Unable to auto find OpenBCI board */
|
||||
console.log('Unable to auto find OpenBCI board');
|
||||
}
|
||||
});
|
||||
|
||||
var readyFunc = () => {
|
||||
// Get the sample rate after 'ready'
|
||||
sampleRate = ourBoard.sampleRate();
|
||||
// Find out if you can even time sync, you must be using v2 and this is only accurate after a `.softReset()` call which is called internally on `.connect()`. We parse the `.softReset()` response for the presence of firmware version 2 properties.
|
||||
timeSyncPossible = ourBoard.usingVersionTwoFirmware();
|
||||
|
||||
if (timeSyncPossible) {
|
||||
ourBoard.streamStart()
|
||||
.catch(err => {
|
||||
console.log(`stream start: ${err}`);
|
||||
});
|
||||
} else {
|
||||
killFunc();
|
||||
}
|
||||
};
|
||||
|
||||
var killFunc = () => {
|
||||
ourBoard.disconnect()
|
||||
.then(() => {
|
||||
process.kill();
|
||||
});
|
||||
};
|
||||
|
||||
var sampleFunc = sample => {
|
||||
// Resynchronize every every second
|
||||
if (sample._count % (sampleRate * 1) === 0) {
|
||||
ourBoard.syncClocksFull()
|
||||
.then(syncObj => {
|
||||
// Sync was successful
|
||||
if (syncObj.valid) {
|
||||
// Log the object to check it out!
|
||||
console.log(`timeOffset`, syncObj.timeOffsetMaster);
|
||||
} else {
|
||||
// Retry it
|
||||
console.log(`Was not able to sync... retry!`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (sample.timeStamp) { // true after the first successful sync
|
||||
if (sample.timeStamp < 10 * 60 * 60 * 1000) { // Less than 10 hours
|
||||
console.log(`Bad time sync ${sample.timeStamp}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Stop after one minute
|
||||
if (sample._count > sampleRate * 60) {
|
||||
killFunc();
|
||||
}
|
||||
};
|
||||
|
||||
// Subscribe to your functions
|
||||
ourBoard.on('ready', readyFunc);
|
||||
ourBoard.on('sample', sampleFunc);
|
||||
|
||||
function exitHandler (options, err) {
|
||||
if (options.cleanup) {
|
||||
if (verbose) console.log('clean');
|
||||
/** Do additional clean up here */
|
||||
}
|
||||
if (err) console.log(err.stack);
|
||||
if (options.exit) {
|
||||
if (verbose) console.log('exit');
|
||||
ourBoard.disconnect().catch(console.log);
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const rl = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
rl.on('SIGINT', function () {
|
||||
process.emit('SIGINT');
|
||||
});
|
||||
}
|
||||
|
||||
// do something when app is closing
|
||||
process.on('exit', exitHandler.bind(null, {
|
||||
cleanup: true
|
||||
}));
|
||||
|
||||
// catches ctrl+c event
|
||||
process.on('SIGINT', exitHandler.bind(null, {
|
||||
exit: true
|
||||
}));
|
||||
|
||||
// catches uncaught exceptions
|
||||
process.on('uncaughtException', exitHandler.bind(null, {
|
||||
exit: true
|
||||
}));
|
||||
Arquivo binário não exibido.
+2449
-2191
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+1098
-1035
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+1089
-1149
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+447
-323
@@ -1,5 +1,4 @@
|
||||
'use strict';
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var util = require('util');
|
||||
var stream = require('stream');
|
||||
@@ -8,355 +7,480 @@ var openBCISample = require('./openBCISample');
|
||||
var k = openBCISample.k;
|
||||
var now = require('performance-now');
|
||||
|
||||
function OpenBCISimulatorFactory () {
|
||||
var factory = this;
|
||||
|
||||
function OpenBCISimulatorFactory() {
|
||||
var factory = this;
|
||||
var _options = {
|
||||
accel: true,
|
||||
alpha: true,
|
||||
boardFailure: false,
|
||||
daisy: false,
|
||||
daisyCanBeAttached: true,
|
||||
drift: 0,
|
||||
firmwareVersion: [k.OBCIFirmwareV1, k.OBCIFirmwareV2],
|
||||
fragmentation: [k.OBCISimulatorFragmentationNone, k.OBCISimulatorFragmentationRandom, k.OBCISimulatorFragmentationFullBuffers, k.OBCISimulatorFragmentationOneByOne],
|
||||
latencyTime: 16,
|
||||
bufferSize: 4096,
|
||||
lineNoise: [k.OBCISimulatorLineNoiseHz60, k.OBCISimulatorLineNoiseHz50, k.OBCISimulatorLineNoiseNone],
|
||||
sampleRate: 250,
|
||||
serialPortFailure: false,
|
||||
verbose: false
|
||||
};
|
||||
|
||||
var _options = {
|
||||
accel: true,
|
||||
alpha: true,
|
||||
boardFailure:false,
|
||||
daisy: false,
|
||||
drift: 0,
|
||||
firmwareVersion: k.OBCIFirmwareV1,
|
||||
lineNoise: '60Hz',
|
||||
sampleRate: 250,
|
||||
serialPortFailure:false,
|
||||
verbose: false
|
||||
};
|
||||
function OpenBCISimulator (portName, options) {
|
||||
options = (typeof options !== 'function') && options || {};
|
||||
var opts = {};
|
||||
|
||||
function OpenBCISimulator(portName, options) {
|
||||
options = (typeof options !== 'function') && options || {};
|
||||
var opts = {};
|
||||
stream.Stream.call(this);
|
||||
|
||||
stream.Stream.call(this);
|
||||
/** Configuring Options */
|
||||
var o;
|
||||
for (o in _options) {
|
||||
var userValue = options[o];
|
||||
delete options[o];
|
||||
|
||||
/** Configuring Options */
|
||||
if (options.accel === false) {
|
||||
opts.accel = false;
|
||||
if (typeof _options[o] === 'object') {
|
||||
// an array specifying a list of choices
|
||||
// if the choice is not in the list, the first one is defaulted to
|
||||
|
||||
if (_options[o].indexOf(userValue) !== -1) {
|
||||
opts[o] = userValue;
|
||||
} else {
|
||||
opts.accel = _options.accel;
|
||||
opts[o] = _options[o][0];
|
||||
}
|
||||
if (options.alpha === false) {
|
||||
opts.alpha = false;
|
||||
} else {
|
||||
// anything else takes the user value if provided, otherwise is a default
|
||||
|
||||
if (userValue !== undefined) {
|
||||
opts[o] = userValue;
|
||||
} else {
|
||||
opts.alpha = _options.alpha;
|
||||
opts[o] = _options[o];
|
||||
}
|
||||
opts.boardFailure = options.boardFailure || _options.boardFailure;
|
||||
opts.daisy = options.daisy || _options.daisy;
|
||||
opts.drift = options.drift || _options.drift;
|
||||
opts.firmwareVersion = options.firmwareVersion || _options.firmwareVersion;
|
||||
opts.lineNoise = options.lineNoise || _options.lineNoise;
|
||||
if (options.sampleRate) {
|
||||
opts.sampleRate = options.sampleRate;
|
||||
} else {
|
||||
opts.sampleRate = k.OBCISampleRate250;
|
||||
}
|
||||
opts.serialPortFailure = options.serialPortFailure || _options.serialPortFailure;
|
||||
opts.verbose = options.verbose || _options.verbose;
|
||||
|
||||
this.options = opts;
|
||||
|
||||
// Bools
|
||||
this.connected = false;
|
||||
this.sd = {
|
||||
active:false,
|
||||
startTime: 0
|
||||
};
|
||||
this.streaming = false;
|
||||
this.synced = false;
|
||||
this.sendSyncSetPacket = false;
|
||||
// Buffers
|
||||
this.buffer = new Buffer(500);
|
||||
// Numbers
|
||||
this.channelNumber = 1;
|
||||
this.hostChannelNumber = this.channelNumber;
|
||||
this.pollTime = 80;
|
||||
this.sampleNumber = -1; // So the first sample is 0
|
||||
// Objects
|
||||
this.sampleGenerator = openBCISample.randomSample(k.OBCINumberOfChannelsDefault, this.options.sampleRate, this.options.alpha, this.options.lineNoise);
|
||||
this.time = {
|
||||
current: 0,
|
||||
start: now(),
|
||||
loop: null
|
||||
};
|
||||
// Strings
|
||||
this.portName = portName || k.OBCISimulatorPortName;
|
||||
|
||||
// Call 'open'
|
||||
if (this.options.verbose) console.log(`Port name: ${portName}`);
|
||||
setTimeout(() => {
|
||||
this.emit('open');
|
||||
this.connected = true;
|
||||
}, 200);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// This allows us to use the emitter class freely outside of the module
|
||||
util.inherits(OpenBCISimulator, stream.Stream);
|
||||
for (o in options) throw new Error('"' + o + '" is not a valid option');
|
||||
|
||||
OpenBCISimulator.prototype.flush = function() {
|
||||
this.buffer.fill(0);
|
||||
//if (this.options.verbose) console.log('flushed');
|
||||
this.options = opts;
|
||||
|
||||
// Bools
|
||||
this.connected = false;
|
||||
this.sd = {
|
||||
active: false,
|
||||
startTime: 0
|
||||
};
|
||||
this.streaming = false;
|
||||
this.synced = false;
|
||||
this.sendSyncSetPacket = false;
|
||||
// Buffers
|
||||
this.outputBuffer = new Buffer(this.options.bufferSize);
|
||||
this.outputBuffered = 0;
|
||||
// Numbers
|
||||
this.channelNumber = 1;
|
||||
this.hostChannelNumber = this.channelNumber;
|
||||
this.pollTime = 80;
|
||||
this.sampleNumber = -1; // So the first sample is 0
|
||||
// Objects
|
||||
this.sampleGenerator = openBCISample.randomSample(k.OBCINumberOfChannelsDefault, this.options.sampleRate, this.options.alpha, this.options.lineNoise);
|
||||
this.time = {
|
||||
current: 0,
|
||||
start: now(),
|
||||
loop: null
|
||||
};
|
||||
// Strings
|
||||
this.portName = portName || k.OBCISimulatorPortName;
|
||||
|
||||
OpenBCISimulator.prototype.write = function(data,callback) {
|
||||
switch (data[0]) {
|
||||
case k.OBCIRadioKey:
|
||||
this._processPrivateRadioMessage(data);
|
||||
break;
|
||||
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 V3 Simulator\nOn Board ADS1299 Device ID: 0x12345\n${this.options.daisy ? "On Daisy ADS1299 Device ID: 0xFFFFF\n" : ""}LIS3DH Device ID: 0x38422\n${this.options.firmware === k.OBCIFirmwareV2 ? "Firmware: v2.0.0\n" : ""}$$$`));
|
||||
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`));
|
||||
this._printEOT();
|
||||
} else {
|
||||
this.emit('data', new Buffer('No open file to close\n'));
|
||||
this._printEOT();
|
||||
}
|
||||
}
|
||||
this.SDLogActive = false;
|
||||
break;
|
||||
case k.OBCISyncTimeSet:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
this.synced = true;
|
||||
setTimeout(() => {
|
||||
this.emit('data', new Buffer(k.OBCISyncTimeSent));
|
||||
this._syncUp();
|
||||
}, 10);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
// Call 'open'
|
||||
if (this.options.verbose) console.log(`Port name: ${portName}`);
|
||||
setTimeout(() => {
|
||||
this.connected = true;
|
||||
this.emit('open');
|
||||
}, 200);
|
||||
}
|
||||
|
||||
// This allows us to use the emitter class freely outside of the module
|
||||
// TODO: upgrade from old-style streams to stream.Duplex or stream.Transform
|
||||
util.inherits(OpenBCISimulator, stream.Stream);
|
||||
|
||||
OpenBCISimulator.prototype.flush = function (callback) {
|
||||
this.outputBuffered = 0;
|
||||
|
||||
clearTimeout(this.outputLoopHandle);
|
||||
this.outputLoopHandle = null;
|
||||
|
||||
if (callback) callback();
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype.isOpen = function () {
|
||||
return this.connected;
|
||||
};
|
||||
|
||||
// output only size bytes of the output buffer
|
||||
OpenBCISimulator.prototype._partialDrain = function (size) {
|
||||
if (!this.connected) throw new Error('not connected');
|
||||
|
||||
if (size > this.outputBuffered) size = this.outputBuffered;
|
||||
|
||||
// buffer is copied because presently openBCIBoard.js reuses it
|
||||
var outBuffer = new Buffer(this.outputBuffer.slice(0, size));
|
||||
|
||||
this.outputBuffer.copy(this.outputBuffer, 0, size, this.outputBuffered);
|
||||
this.outputBuffered -= size;
|
||||
|
||||
this.emit('data', outBuffer);
|
||||
};
|
||||
|
||||
// queue some data for output and send it out depending on options.fragmentation
|
||||
OpenBCISimulator.prototype._output = function (dataBuffer) {
|
||||
// drain full buffers until there is no overflow
|
||||
while (this.outputBuffered + dataBuffer.length > this.outputBuffer.length) {
|
||||
var len = dataBuffer.copy(this.outputBuffer, this.outputBuffered);
|
||||
dataBuffer = dataBuffer.slice(len);
|
||||
this.outputBuffered += len;
|
||||
|
||||
this._partialDrain(this.outputBuffered);
|
||||
this.flush();
|
||||
}
|
||||
|
||||
dataBuffer.copy(this.outputBuffer, this.outputBuffered);
|
||||
this.outputBuffered += dataBuffer.length;
|
||||
|
||||
if (!this.outputLoopHandle) {
|
||||
var latencyTime = this.options.latencyTime;
|
||||
if (this.options.fragmentation === k.OBCISimulatorFragmentationOneByOne ||
|
||||
this.options.fragmentation === k.OBCISimulatorFragmentationNone) {
|
||||
// no need to wait for latencyTime
|
||||
// note that this is the only difference between 'none' and 'fullBuffers'
|
||||
latencyTime = 0;
|
||||
}
|
||||
var outputLoop = () => {
|
||||
var size;
|
||||
switch (this.options.fragmentation) {
|
||||
case k.OBCISimulatorFragmentationRandom:
|
||||
if (Math.random() < 0.5) {
|
||||
// randomly picked to send out a fragment
|
||||
size = Math.ceil(Math.random() * Math.max(this.outputBuffered, 62));
|
||||
break;
|
||||
} // else, randomly picked to send a complete buffer in next block
|
||||
/* falls through */
|
||||
case k.OBCISimulatorFragmentationFullBuffers:
|
||||
case k.OBCISimulatorFragmentationNone:
|
||||
case false:
|
||||
size = this.outputBuffered;
|
||||
break;
|
||||
case k.OBCISimulatorFragmentationOneByOne:
|
||||
size = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/** Handle Callback */
|
||||
if (this.connected) {
|
||||
callback(null,'Success!');
|
||||
this._partialDrain(size);
|
||||
if (this.outputBuffered) {
|
||||
this.outputLoopHandle = setTimeout(outputLoop, latencyTime);
|
||||
} else {
|
||||
this.outputLoopHandle = null;
|
||||
}
|
||||
};
|
||||
};
|
||||
if (latencyTime === 0) {
|
||||
outputLoop();
|
||||
} else {
|
||||
this.outputLoopHandle = setTimeout(outputLoop, latencyTime);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype.drain = function(callback) {
|
||||
callback();
|
||||
};
|
||||
OpenBCISimulator.prototype.write = function (data, callback) {
|
||||
if (!this.connected) {
|
||||
if (callback) callback('Not connected');
|
||||
else throw new Error('Not connected!');
|
||||
return;
|
||||
}
|
||||
|
||||
OpenBCISimulator.prototype.close = function(callback) {
|
||||
if (this.connected) {
|
||||
this.emit('close');
|
||||
// TODO: this function assumes a type of Buffer for radio, and a type of String otherwise
|
||||
// FIX THIS it makes it unusable outside the api code
|
||||
switch (data[0]) {
|
||||
case k.OBCIRadioKey:
|
||||
this._processPrivateRadioMessage(data);
|
||||
break;
|
||||
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._output(new Buffer(`OpenBCI V3 Simulator On Board ADS1299 Device ID: 0x12345 ${this.options.daisy ? `On Daisy ADS1299 Device ID: 0xFFFFF\n` : ``} LIS3DH Device ID: 0x38422 ${this.options.firmwareVersion === k.OBCIFirmwareV2 ? `Firmware: v2.0.0\n` : ``}$$$`));
|
||||
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._output(new Buffer('Wiring is correct and a card is present.\nCorresponding SD file OBCI_69.TXT\n$$$'));
|
||||
}
|
||||
this.connected = false;
|
||||
callback();
|
||||
this.sd.active = true;
|
||||
this.sd.startTime = now();
|
||||
break;
|
||||
case k.OBCISDLogStop:
|
||||
if (!this.streaming) {
|
||||
if (this.SDLogActive) {
|
||||
this._output(new Buffer(`Total Elapsed Time: ${now() - this.sd.startTime} ms`));
|
||||
this._output(new Buffer(`Max write time: ${Math.random() * 500} us`));
|
||||
this._output(new Buffer(`Min write time: ${Math.random() * 200} us`));
|
||||
this._output(new Buffer(`Overruns: 0`));
|
||||
this._printEOT();
|
||||
} else {
|
||||
this._output(new Buffer('No open file to close\n'));
|
||||
this._printEOT();
|
||||
}
|
||||
}
|
||||
this.SDLogActive = false;
|
||||
break;
|
||||
case k.OBCISyncTimeSet:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
this.synced = true;
|
||||
setTimeout(() => {
|
||||
this._output(new Buffer(k.OBCISyncTimeSent));
|
||||
this._syncUp();
|
||||
}, 10);
|
||||
}
|
||||
break;
|
||||
case k.OBCIChannelMaxNumber8:
|
||||
if (this.options.daisy) {
|
||||
this.options.daisy = false;
|
||||
this._output(new Buffer(k.OBCIChannelMaxNumber8SuccessDaisyRemoved));
|
||||
this._printEOT();
|
||||
} else {
|
||||
this._printEOT();
|
||||
}
|
||||
break;
|
||||
case k.OBCIChannelMaxNumber16:
|
||||
if (this.options.daisy) {
|
||||
this._output(new Buffer(k.OBCIChannelMaxNumber16DaisyAlreadyAttached));
|
||||
this._printEOT();
|
||||
} else {
|
||||
if (this.options.daisyCanBeAttached) {
|
||||
this.options.daisy = true;
|
||||
this._output(new Buffer(k.OBCIChannelMaxNumber16DaisyAttached));
|
||||
this._printEOT();
|
||||
} else {
|
||||
this._output(new Buffer(k.OBCIChannelMaxNumber16NoDaisyAttached));
|
||||
this._printEOT();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/** Handle Callback */
|
||||
if (callback) {
|
||||
callback(null, 'Success!');
|
||||
}
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype.drain = function (callback) {
|
||||
if (callback) callback();
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype.close = function (callback) {
|
||||
if (this.connected) {
|
||||
this.flush();
|
||||
|
||||
if (this.stream) clearInterval(this.stream);
|
||||
|
||||
this.connected = false;
|
||||
this.emit('close');
|
||||
if (callback) callback();
|
||||
} else {
|
||||
if (callback) callback('not connected');
|
||||
}
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype._startStream = function () {
|
||||
var intervalInMS = 1000 / this.options.sampleRate;
|
||||
|
||||
if (intervalInMS < 2) intervalInMS = 2;
|
||||
|
||||
var getNewPacket = sampNumber => {
|
||||
if (this.options.accel) {
|
||||
if (this.synced) {
|
||||
if (this.sendSyncSetPacket) {
|
||||
this.sendSyncSetPacket = false;
|
||||
return openBCISample.convertSampleToPacketAccelTimeSyncSet(this.sampleGenerator(sampNumber), now().toFixed(0));
|
||||
} else {
|
||||
return openBCISample.convertSampleToPacketAccelTimeSynced(this.sampleGenerator(sampNumber), now().toFixed(0));
|
||||
}
|
||||
} else {
|
||||
return openBCISample.convertSampleToPacketStandard(this.sampleGenerator(sampNumber));
|
||||
}
|
||||
} else {
|
||||
if (this.synced) {
|
||||
if (this.sendSyncSetPacket) {
|
||||
this.sendSyncSetPacket = false;
|
||||
return openBCISample.convertSampleToPacketRawAuxTimeSyncSet(this.sampleGenerator(sampNumber), now().toFixed(0), new Buffer([0, 0, 0, 0, 0, 0]));
|
||||
} else {
|
||||
return openBCISample.convertSampleToPacketRawAuxTimeSynced(this.sampleGenerator(sampNumber), now().toFixed(0), new Buffer([0, 0, 0, 0, 0, 0]));
|
||||
}
|
||||
} else {
|
||||
return openBCISample.convertSampleToPacketRawAux(this.sampleGenerator(sampNumber), new Buffer([0, 0, 0, 0, 0, 0]));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype._startStream = function() {
|
||||
var intervalInMS = 1000 / this.options.sampleRate;
|
||||
this.stream = setInterval(() => {
|
||||
this._output(getNewPacket(this.sampleNumber));
|
||||
this.sampleNumber++;
|
||||
}, intervalInMS);
|
||||
};
|
||||
|
||||
if (intervalInMS < 2) intervalInMS = 2;
|
||||
OpenBCISimulator.prototype._syncUp = function () {
|
||||
setTimeout(() => {
|
||||
this.sendSyncSetPacket = true;
|
||||
}, 12); // 3 packets later
|
||||
};
|
||||
|
||||
var getNewPacket = sampNumber => {
|
||||
if (this.options.accel) {
|
||||
if (this.synced) {
|
||||
if (this.sendSyncSetPacket) {
|
||||
this.sendSyncSetPacket = false;
|
||||
return openBCISample.convertSampleToPacketAccelTimeSyncSet(this.sampleGenerator(sampNumber),now().toFixed(0));
|
||||
} else {
|
||||
return openBCISample.convertSampleToPacketAccelTimeSynced(this.sampleGenerator(sampNumber),now().toFixed(0));
|
||||
}
|
||||
} else {
|
||||
return openBCISample.convertSampleToPacketStandard(this.sampleGenerator(sampNumber));
|
||||
}
|
||||
OpenBCISimulator.prototype._printEOT = function () {
|
||||
this._output(new Buffer('$$$'));
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype._printFailure = function () {
|
||||
this._output(new Buffer('Failure: '));
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype._printSuccess = function () {
|
||||
this._output(new Buffer('Success: '));
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype._printValidatedCommsTimeout = function () {
|
||||
this._printFailure();
|
||||
this._output(new Buffer('Communications timeout - Device failed to poll Host'));
|
||||
this._printEOT();
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype._processPrivateRadioMessage = function (dataBuffer) {
|
||||
switch (dataBuffer[1]) {
|
||||
case k.OBCIRadioCmdChannelGet:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
if (!this.options.boardFailure) {
|
||||
this._printSuccess();
|
||||
this._output(new Buffer(`Host and Device on Channel Number ${this.channelNumber}`));
|
||||
this._output(new Buffer([this.channelNumber]));
|
||||
this._printEOT();
|
||||
} else if (!this.serialPortFailure) {
|
||||
this._printFailure();
|
||||
this._output(new Buffer(`Host on Channel Number ${this.channelNumber}`));
|
||||
this._output(new Buffer([this.channelNumber]));
|
||||
this._printEOT();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case k.OBCIRadioCmdChannelSet:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
if (!this.options.boardFailure) {
|
||||
if (dataBuffer[2] <= k.OBCIRadioChannelMax) {
|
||||
this.channelNumber = dataBuffer[2];
|
||||
this.hostChannelNumber = this.channelNumber;
|
||||
this._printSuccess();
|
||||
this._output(new Buffer(`Channel Number ${this.channelNumber}`));
|
||||
this._output(new Buffer([this.channelNumber]));
|
||||
this._printEOT();
|
||||
} else {
|
||||
if (this.synced) {
|
||||
if (this.sendSyncSetPacket) {
|
||||
this.sendSyncSetPacket = false;
|
||||
return openBCISample.convertSampleToPacketRawAuxTimeSyncSet(this.sampleGenerator(sampNumber),now().toFixed(0),new Buffer([0,0,0,0,0,0]));
|
||||
} else {
|
||||
return openBCISample.convertSampleToPacketRawAuxTimeSynced(this.sampleGenerator(sampNumber),now().toFixed(0),new Buffer([0,0,0,0,0,0]));
|
||||
}
|
||||
} else {
|
||||
return openBCISample.convertSampleToPacketRawAux(this.sampleGenerator(sampNumber),new Buffer([0,0,0,0,0,0]));
|
||||
}
|
||||
|
||||
this._printFailure();
|
||||
this._output(new Buffer('Verify channel number is less than 25'));
|
||||
this._printEOT();
|
||||
}
|
||||
};
|
||||
|
||||
this.stream = setInterval(() => {
|
||||
this.emit('data', getNewPacket(this.sampleNumber));
|
||||
this.sampleNumber++;
|
||||
}, intervalInMS);
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype._syncUp = function() {
|
||||
setTimeout(() => {
|
||||
this.sendSyncSetPacket = true;
|
||||
}, 12); // 3 packets later
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype._printEOT = function () {
|
||||
this.emit('data', new Buffer("$$$"));
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype._printFailure = function () {
|
||||
this.emit('data', new Buffer("Failure: "));
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype._printSuccess = function () {
|
||||
this.emit('data', new Buffer("Success: "));
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype._printValidatedCommsTimeout = function () {
|
||||
this._printFailure();
|
||||
this.emit('data', new Buffer("Communications timeout - Device failed to poll Host"));
|
||||
this._printEOT();
|
||||
};
|
||||
|
||||
OpenBCISimulator.prototype._processPrivateRadioMessage = function(dataBuffer) {
|
||||
switch (dataBuffer[1]) {
|
||||
case k.OBCIRadioCmdChannelGet:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
if (!this.options.boardFailure) {
|
||||
this._printSuccess();
|
||||
this.emit('data', new Buffer(`Host and Device on Channel Number ${this.channelNumber}`));
|
||||
this.emit('data', new Buffer([this.channelNumber]));
|
||||
this._printEOT();
|
||||
} else if (!this.serialPortFailure) {
|
||||
this._printFailure();
|
||||
this.emit('data', new Buffer(`Host on Channel Number ${this.channelNumber}`));
|
||||
this.emit('data', new Buffer([this.channelNumber]));
|
||||
this._printEOT();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case k.OBCIRadioCmdChannelSet:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
if (!this.options.boardFailure) {
|
||||
if (dataBuffer[2] < k.OBCIRadioChannelMax) {
|
||||
this.channelNumber = dataBuffer[2];
|
||||
this.hostChannelNumber = this.channelNumber;
|
||||
this._printSuccess();
|
||||
this.emit('data', new Buffer(`Channel Number ${this.channelNumber}`));
|
||||
this.emit('data', new Buffer([this.channelNumber]));
|
||||
this._printEOT();
|
||||
} else {
|
||||
this._printFailure();
|
||||
this.emit('data', new Buffer("Verify channel number is less than 25"));
|
||||
this._printEOT();
|
||||
}
|
||||
} else if (!this.serialPortFailure) {
|
||||
this._printValidatedCommsTimeout();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case k.OBCIRadioCmdChannelSetOverride:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
if (dataBuffer[2] < k.OBCIRadioChannelMax) {
|
||||
if (dataBuffer[2] === this.channelNumber) {
|
||||
this.options.boardFailure = false;
|
||||
} else {
|
||||
this.options.boardFailure = true;
|
||||
}
|
||||
this.hostChannelNumber = dataBuffer[2];
|
||||
this._printSuccess();
|
||||
this.emit('data', new Buffer(`Host override - Channel Number ${this.hostChannelNumber}`));
|
||||
this.emit('data', new Buffer([this.hostChannelNumber]));
|
||||
this._printEOT();
|
||||
} else {
|
||||
this._printFailure();
|
||||
this.emit('data', new Buffer("Verify channel number is less than 25"));
|
||||
this._printEOT();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case k.OBCIRadioCmdPollTimeGet:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
if (!this.options.boardFailure) {
|
||||
this._printSuccess();
|
||||
this.emit('data', new Buffer(`Poll Time ${this.pollTime}`));
|
||||
this.emit('data', new Buffer([this.pollTime]));
|
||||
this._printEOT();
|
||||
} else {
|
||||
this._printValidatedCommsTimeout();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case k.OBCIRadioCmdPollTimeSet:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
if (!this.options.boardFailure) {
|
||||
this.pollTime = dataBuffer[2];
|
||||
this._printSuccess();
|
||||
this.emit('data', new Buffer(`Poll Time ${this.pollTime}`));
|
||||
this.emit('data', new Buffer([this.pollTime]));
|
||||
this._printEOT();
|
||||
} else {
|
||||
this._printValidatedCommsTimeout();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case k.OBCIRadioCmdBaudRateSetDefault:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
this._printSuccess();
|
||||
this.emit('data', new Buffer("Switch your baud rate to 115200"));
|
||||
this.emit('data', new Buffer([0x24,0x24,0x24,0xFF])); // The board really does this
|
||||
}
|
||||
break;
|
||||
case k.OBCIRadioCmdBaudRateSetFast:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
this._printSuccess();
|
||||
this.emit('data', new Buffer("Switch your baud rate to 230400"));
|
||||
this.emit('data', new Buffer([0x24,0x24,0x24,0xFF])); // The board really does this
|
||||
}
|
||||
break;
|
||||
case k.OBCIRadioCmdSystemStatus:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
if (!this.options.boardFailure) {
|
||||
this._printSuccess();
|
||||
this.emit('data', new Buffer("System is Up"));
|
||||
this._printEOT();
|
||||
} else {
|
||||
this._printFailure();
|
||||
this.emit('data', new Buffer("System is Down"));
|
||||
this._printEOT();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} else if (!this.serialPortFailure) {
|
||||
this._printValidatedCommsTimeout();
|
||||
}
|
||||
}
|
||||
};
|
||||
break;
|
||||
case k.OBCIRadioCmdChannelSetOverride:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
if (dataBuffer[2] <= k.OBCIRadioChannelMax) {
|
||||
if (dataBuffer[2] === this.channelNumber) {
|
||||
this.options.boardFailure = false;
|
||||
} else {
|
||||
this.options.boardFailure = true;
|
||||
}
|
||||
this.hostChannelNumber = dataBuffer[2];
|
||||
this._printSuccess();
|
||||
this._output(new Buffer(`Host override - Channel Number ${this.hostChannelNumber}`));
|
||||
this._output(new Buffer([this.hostChannelNumber]));
|
||||
this._printEOT();
|
||||
} else {
|
||||
this._printFailure();
|
||||
this._output(new Buffer('Verify channel number is less than 25'));
|
||||
this._printEOT();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case k.OBCIRadioCmdPollTimeGet:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
if (!this.options.boardFailure) {
|
||||
this._printSuccess();
|
||||
this._output(new Buffer(`Poll Time ${this.pollTime}`));
|
||||
this._output(new Buffer([this.pollTime]));
|
||||
this._printEOT();
|
||||
} else {
|
||||
this._printValidatedCommsTimeout();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case k.OBCIRadioCmdPollTimeSet:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
if (!this.options.boardFailure) {
|
||||
this.pollTime = dataBuffer[2];
|
||||
this._printSuccess();
|
||||
this._output(new Buffer(`Poll Time ${this.pollTime}`));
|
||||
this._output(new Buffer([this.pollTime]));
|
||||
this._printEOT();
|
||||
} else {
|
||||
this._printValidatedCommsTimeout();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case k.OBCIRadioCmdBaudRateSetDefault:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
this._printSuccess();
|
||||
this._output(new Buffer('Switch your baud rate to 115200'));
|
||||
this._output(new Buffer([0x24, 0x24, 0x24, 0xFF])); // The board really does this
|
||||
}
|
||||
break;
|
||||
case k.OBCIRadioCmdBaudRateSetFast:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
this._printSuccess();
|
||||
this._output(new Buffer('Switch your baud rate to 230400'));
|
||||
this._output(new Buffer([0x24, 0x24, 0x24, 0xFF])); // The board really does this
|
||||
}
|
||||
break;
|
||||
case k.OBCIRadioCmdSystemStatus:
|
||||
if (this.options.firmwareVersion === k.OBCIFirmwareV2) {
|
||||
if (!this.options.boardFailure) {
|
||||
this._printSuccess();
|
||||
this._output(new Buffer('System is Up'));
|
||||
this._printEOT();
|
||||
} else {
|
||||
this._printFailure();
|
||||
this._output(new Buffer('System is Down'));
|
||||
this._printEOT();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
factory.OpenBCISimulator = OpenBCISimulator;
|
||||
factory.OpenBCISimulator = OpenBCISimulator;
|
||||
}
|
||||
|
||||
util.inherits(OpenBCISimulatorFactory, EventEmitter);
|
||||
|
||||
+27
-9
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "openbci",
|
||||
"version": "1.2.0",
|
||||
"version": "1.5.2",
|
||||
"description": "The official Node.js SDK for the OpenBCI Biosensor Board.",
|
||||
"main": "openBCIBoard",
|
||||
"scripts": {
|
||||
"lint": "semistandard | snazzy",
|
||||
"start": "node index",
|
||||
"test": "mocha test",
|
||||
"test": "semistandard | snazzy && mocha test",
|
||||
"test-cov": "istanbul cover ./node_modules/mocha/bin/_mocha -- -R spec && codecov"
|
||||
},
|
||||
"keywords": [
|
||||
@@ -18,8 +19,8 @@
|
||||
"buffer-equal": "^1.0.0",
|
||||
"gaussian": "^1.0.0",
|
||||
"mathjs": "^3.3.0",
|
||||
"performance-now": "^0.2.0",
|
||||
"serialport": "3.1.2",
|
||||
"performance-now": "^2.1.0",
|
||||
"serialport": "4.0.7",
|
||||
"sntp": "^2.0.0",
|
||||
"streamsearch": "^0.1.2"
|
||||
},
|
||||
@@ -27,14 +28,18 @@
|
||||
"test": "test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bluebird": "3.5.0",
|
||||
"chai": "^3.4.1",
|
||||
"chai-as-promised": "^5.2.0",
|
||||
"codecov": "^1.0.1",
|
||||
"chai-as-promised": "^6.0.0",
|
||||
"codecov": "^2.1.0",
|
||||
"istanbul": "^0.4.4",
|
||||
"mocha": "^2.3.4",
|
||||
"mocha": "^3.0.2",
|
||||
"sandboxed-module": "^2.0.3",
|
||||
"sinon": "^1.17.2",
|
||||
"sinon-chai": "^2.8.0"
|
||||
"semistandard": "^10.0.0",
|
||||
"sinon": "^2.1.0",
|
||||
"sinon-as-promised": "^4.0.2",
|
||||
"sinon-chai": "^2.8.0",
|
||||
"snazzy": "^6.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -46,5 +51,18 @@
|
||||
"homepage": "https://github.com/openbci/openbci_nodejs#readme",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
},
|
||||
"semistandard": {
|
||||
"globals": [
|
||||
"describe",
|
||||
"context",
|
||||
"before",
|
||||
"beforeEach",
|
||||
"after",
|
||||
"afterEach",
|
||||
"it",
|
||||
"expect",
|
||||
"should"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+1440
-1156
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+1234
-1343
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,142 @@
|
||||
var timingEventsAsPromises = require('./timingEventsAsPromises');
|
||||
exports.BluebirdPromise = require('bluebird');
|
||||
exports.PromiseIgnored = global.Promise;
|
||||
|
||||
// Enable bluebird for all promise usage during tests only
|
||||
// Fails tests for issues bluebird finds
|
||||
// Exports a function to list all promises (getPendingPromises)
|
||||
// Exports a function to verify no promises pending within a timeout (noPendingPromises)
|
||||
|
||||
exports.BluebirdPromise.config({
|
||||
// TODO: wForgottenReturn is disabled because timingEventsAsPromises triggers it; find a workaround
|
||||
warnings: { wForgottenReturn: false },
|
||||
longStackTraces: true,
|
||||
monitoring: true,
|
||||
cancellation: true
|
||||
});
|
||||
|
||||
// nextTick conveniently not instrumented by timingEventsAsPromises
|
||||
exports.BluebirdPromise.setScheduler(process.nextTick);
|
||||
|
||||
// unhandled rejections become test failures
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
if (!(reason instanceof Error)) {
|
||||
reason = new Error('unhandled promise rejection: ' + reason);
|
||||
} else {
|
||||
reason.message = 'unhandled promise rejection: ' + reason.message;
|
||||
}
|
||||
process.nextTick(() => { throw reason; });
|
||||
});
|
||||
|
||||
// // warnings become test failures
|
||||
// process.on('warning', (warning) => {
|
||||
// var error = new Error(warning);
|
||||
// process.nextTick(() => { throw error; });
|
||||
// });
|
||||
|
||||
// provide access to all currently pending promises
|
||||
var pendingPromises = {};
|
||||
var promiseId = 0;
|
||||
var nested = 0;
|
||||
|
||||
function promiseCreationHandler (promise) {
|
||||
// promise created already resolved; ignore
|
||||
if (!promise.isPending()) return;
|
||||
|
||||
// need to create another promise to get access to the extended stack trace
|
||||
// nested detects if we are inside our own dummy promise
|
||||
++nested;
|
||||
if (nested === 1) {
|
||||
// not the dummy promise
|
||||
promise.___id = ++promiseId;
|
||||
// store promise details
|
||||
var error = new Error('Promise ' + promise.___id + ' is still pending');
|
||||
var entry = {
|
||||
promise: promise,
|
||||
id: promise.___id,
|
||||
error: error
|
||||
};
|
||||
pendingPromises[promise.___id] = entry;
|
||||
// extract stack trace by rejecting an error; bluebird fills in expanded stack
|
||||
exports.BluebirdPromise.reject(error).catch(error => {
|
||||
entry.error = error;
|
||||
entry.stack = error.stack;
|
||||
});
|
||||
} else {
|
||||
promise.___nested = nested;
|
||||
}
|
||||
--nested;
|
||||
}
|
||||
process.on('promiseCreated', promiseCreationHandler);
|
||||
|
||||
function promiseDoneHandler (promise) {
|
||||
if (promise.___nested) return;
|
||||
delete pendingPromises[promise.___id];
|
||||
}
|
||||
process.on('promiseFulfilled', promiseDoneHandler);
|
||||
process.on('promiseRejected', promiseDoneHandler);
|
||||
process.on('promiseResolved', promiseDoneHandler);
|
||||
process.on('promiseCancelled', promiseDoneHandler);
|
||||
|
||||
exports.getPendingPromises = function () {
|
||||
var ret = [];
|
||||
for (var promise in pendingPromises) {
|
||||
ret.push(pendingPromises[promise]);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
exports.noPendingPromises = function (milliseconds) {
|
||||
if (!milliseconds) milliseconds = 0;
|
||||
|
||||
return new exports.PromiseIgnored((resolve, reject) => {
|
||||
function waited100 () {
|
||||
var promises = exports.getPendingPromises();
|
||||
|
||||
if (promises.length === 0) {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
if (milliseconds > 0) {
|
||||
milliseconds -= 100;
|
||||
return timingEventsAsPromises.setTimeoutIgnored(waited100, 100);
|
||||
}
|
||||
|
||||
// timed out, but promises remaining: cancel all
|
||||
|
||||
console.log(promises.length + ' promises still pending');
|
||||
|
||||
promises.forEach(promise => {
|
||||
promise.promise.cancel();
|
||||
});
|
||||
|
||||
// report one
|
||||
reject(promises[0].error);
|
||||
}
|
||||
|
||||
timingEventsAsPromises.setTimeoutIgnored(waited100, 0);
|
||||
});
|
||||
};
|
||||
|
||||
// now instrument the Promise object itself to always use a simplified version of bluebird
|
||||
// bluebird is composed inside a bare-bones Promise object providing only the official calls
|
||||
|
||||
global.Promise = function (handler) {
|
||||
this._promise = new exports.BluebirdPromise(handler);
|
||||
};
|
||||
|
||||
// compose class methods
|
||||
['all', 'race', 'reject', 'resolve'].forEach(classMethod => {
|
||||
global.Promise[classMethod] = function () {
|
||||
return exports.BluebirdPromise[classMethod].apply(exports.BluebirdPromise, [].slice.call(arguments));
|
||||
};
|
||||
Object.defineProperty(global.Promise[classMethod], 'name', { value: 'Promise.' + classMethod });
|
||||
});
|
||||
|
||||
// compose object methods
|
||||
['then', 'catch'].forEach(objectMethod => {
|
||||
global.Promise.prototype[objectMethod] = function () {
|
||||
return this._promise[objectMethod].apply(this._promise, [].slice.call(arguments));
|
||||
};
|
||||
Object.defineProperty(global.Promise.prototype[objectMethod], 'name', { value: 'Promise.' + objectMethod });
|
||||
});
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,949 @@
|
||||
'use strict';
|
||||
var bluebirdChecks = require('./bluebirdChecks');
|
||||
var sinon = require('sinon'); // eslint-disable-line no-unused-vars
|
||||
var chai = require('chai');
|
||||
var expect = chai.expect;
|
||||
var should = chai.should(); // eslint-disable-line no-unused-vars
|
||||
var openBCIBoard = require('../openBCIBoard');
|
||||
var openBCISample = openBCIBoard.OpenBCISample;
|
||||
var k = openBCISample.k;
|
||||
var chaiAsPromised = require('chai-as-promised');
|
||||
var sinonChai = require('sinon-chai');
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
chai.use(sinonChai);
|
||||
|
||||
describe('openbci-radios', function () {
|
||||
this.timeout(2000);
|
||||
var ourBoard, masterPortName;
|
||||
|
||||
before(function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard();
|
||||
ourBoard.autoFindOpenBCIBoard()
|
||||
.then(portName => {
|
||||
ourBoard = null;
|
||||
masterPortName = portName;
|
||||
done();
|
||||
})
|
||||
.catch(() => {
|
||||
ourBoard = null;
|
||||
masterPortName = k.OBCISimulatorPortName;
|
||||
done();
|
||||
});
|
||||
});
|
||||
after(done => {
|
||||
if (ourBoard) {
|
||||
if (ourBoard['connected']) {
|
||||
ourBoard.disconnect()
|
||||
.then(() => {
|
||||
done();
|
||||
})
|
||||
.catch(err => {
|
||||
done(err);
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
describe('#radioChannelSet', function () {
|
||||
afterEach(function (done) {
|
||||
if (ourBoard.isConnected()) {
|
||||
ourBoard.disconnect().then(() => {
|
||||
done();
|
||||
}).catch(() => done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
afterEach(() => bluebirdChecks.noPendingPromises());
|
||||
|
||||
it('should not change the channel number if not connected', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.radioChannelGet().should.be.rejected.and.notify(done);
|
||||
});
|
||||
|
||||
it('should reject if streaming', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.streamStart()
|
||||
.then(() => {
|
||||
ourBoard.radioChannelSet(1).then(() => {
|
||||
done('should have rejected');
|
||||
}).catch(() => {
|
||||
done(); // Test pass
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should reject if not firmware version 2', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelSet(1).should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should reject if a number is not sent as input', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelSet('1').should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
|
||||
it('should reject if no channel number is presented as arg', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelSet().should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
|
||||
it('should reject if the requested new channel number is lower than 0', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelSet(-1).should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
|
||||
it('should reject if the requested new channel number is higher than 25', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelSet(26).should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
|
||||
it('should not change the channel if the board is not communicating with the host', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorBoardFailure: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelSet(1).should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
|
||||
it('should change the channel if connected, not steaming, and using firmware version 2+', function (done) {
|
||||
var newChannelNumber = 2;
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelSet(newChannelNumber).then(channelNumber => {
|
||||
expect(channelNumber).to.be.equal(newChannelNumber);
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
});
|
||||
|
||||
describe('#radioChannelSetHostOverride', function () {
|
||||
afterEach(function (done) {
|
||||
if (ourBoard.isConnected()) {
|
||||
ourBoard.disconnect().then(() => {
|
||||
done();
|
||||
}).catch(() => done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
afterEach(() => bluebirdChecks.noPendingPromises());
|
||||
it('should not change the channel number if not connected', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.radioChannelSetHostOverride().should.be.rejected.and.notify(done);
|
||||
});
|
||||
it('should reject if streaming', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.streamStart()
|
||||
.then(() => {
|
||||
ourBoard.radioChannelSetHostOverride(1).then(() => {
|
||||
done('should have rejected');
|
||||
}).catch(() => {
|
||||
done(); // Test pass
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should reject if a number is not sent as input', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelSetHostOverride('1').should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should reject if no channel number is presented as arg', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelSetHostOverride().should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should reject if the requested new channel number is lower than 0', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelSetHostOverride(-1).should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should reject if the requested new channel number is higher than 25', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelSetHostOverride(26).should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should change the channel if connected, not steaming, and using firmware version 2+', function (done) {
|
||||
var newChannelNumber = 2;
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelSetHostOverride(newChannelNumber).then(channelNumber => {
|
||||
expect(channelNumber).to.be.equal(newChannelNumber);
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
});
|
||||
|
||||
describe('#radioChannelGet', function () {
|
||||
afterEach(function (done) {
|
||||
if (ourBoard.isConnected()) {
|
||||
ourBoard.disconnect().then(() => {
|
||||
done();
|
||||
}).catch(() => done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
afterEach(() => bluebirdChecks.noPendingPromises());
|
||||
|
||||
it('should not query if not connected', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.radioChannelGet().should.be.rejected.and.notify(done);
|
||||
});
|
||||
it('should not query if streaming', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.streamStart()
|
||||
.then(() => {
|
||||
ourBoard.radioChannelGet().then(() => {
|
||||
done('should have rejected');
|
||||
}).catch(() => {
|
||||
done(); // Test pass
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should not query if not firmware version 2', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelGet().should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should query if firmware version 2', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelGet().then(res => {
|
||||
expect(res.channelNumber).to.be.within(k.OBCIRadioChannelMin, k.OBCIRadioChannelMax);
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should get message even if the board is not communicating with dongle', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorBoardFailure: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioChannelGet().should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
});
|
||||
|
||||
describe('#radioPollTimeSet', function () {
|
||||
afterEach(function (done) {
|
||||
if (ourBoard.isConnected()) {
|
||||
ourBoard.disconnect().then(() => {
|
||||
done();
|
||||
}).catch(() => done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
afterEach(() => bluebirdChecks.noPendingPromises());
|
||||
it('should not change the channel number if not connected', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.radioPollTimeSet().should.be.rejected.and.notify(done);
|
||||
});
|
||||
|
||||
it('should reject if streaming', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.streamStart()
|
||||
.then(() => {
|
||||
ourBoard.radioPollTimeSet(1).then(() => {
|
||||
done('should have rejected');
|
||||
}).catch(() => {
|
||||
done(); // Test pass
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
|
||||
it('should reject if not firmware version 2', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioPollTimeSet(1).should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
|
||||
it('should reject if a number is not sent as input', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioPollTimeSet('1').should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
|
||||
it('should reject if no poll time is presented as arg', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioPollTimeSet().should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
|
||||
it('should reject if the requested new poll time is lower than 0', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioPollTimeSet(-1).should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
|
||||
it('should reject if the requested new poll time is higher than 255', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioPollTimeSet(256).should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
|
||||
it('should not change the poll time if the board is not communicating with the host', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorBoardFailure: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioPollTimeSet(1).should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
|
||||
it('should change the poll time if connected, not steaming, and using firmware version 2+', function (done) {
|
||||
var newPollTime = 69;
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioPollTimeSet(newPollTime).then(() => {
|
||||
done();
|
||||
}).catch(err => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
});
|
||||
|
||||
describe('#radioPollTimeGet', function () {
|
||||
afterEach(function (done) {
|
||||
if (ourBoard.isConnected()) {
|
||||
ourBoard.disconnect().then(() => {
|
||||
done();
|
||||
}).catch(() => done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
afterEach(() => bluebirdChecks.noPendingPromises());
|
||||
|
||||
it('should not query if not connected', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.radioPollTimeGet().should.be.rejected.and.notify(done);
|
||||
});
|
||||
it('should not query if streaming', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.streamStart()
|
||||
.then(() => {
|
||||
ourBoard.radioPollTimeGet().then(() => {
|
||||
done('should have rejected');
|
||||
}).catch(() => {
|
||||
done(); // Test pass
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should not query if not firmware version 2', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioPollTimeGet().should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should query if firmware version 2', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioPollTimeGet().then(pollTime => {
|
||||
expect(pollTime).to.be.greaterThan(0);
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should get failure message if the board is not communicating with dongle', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorBoardFailure: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioPollTimeGet().should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
});
|
||||
|
||||
describe('#radioBaudRateSet', function () {
|
||||
afterEach(function (done) {
|
||||
if (ourBoard.isConnected()) {
|
||||
ourBoard.disconnect().then(() => {
|
||||
done();
|
||||
}).catch(() => done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
afterEach(() => bluebirdChecks.noPendingPromises());
|
||||
|
||||
it('should not try to set baud rate if not connected', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.radioBaudRateSet('default').should.be.rejected.and.notify(done);
|
||||
});
|
||||
it('should reject if no input', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.radioBaudRateSet().should.be.rejected.and.notify(done);
|
||||
});
|
||||
it('should be rejected if input type incorrect', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.radioBaudRateSet(1).should.be.rejected.and.notify(done);
|
||||
});
|
||||
it('should not try to change the baud rate if streaming', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.streamStart()
|
||||
.then(() => {
|
||||
ourBoard.radioBaudRateSet('default').then(() => {
|
||||
done('should have rejected');
|
||||
}).catch(() => {
|
||||
done(); // Test pass
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should not try to change the baud rate if not firmware version 2', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioBaudRateSet('default').should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should set the baud rate to default if firmware version 2', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioBaudRateSet('default').then(baudrate => {
|
||||
expect(baudrate).to.be.equal(115200);
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should set the baud rate to fast if firmware version 2', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioBaudRateSet('fast').then(baudrate => {
|
||||
expect(baudrate).to.be.equal(230400);
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
});
|
||||
|
||||
describe('#radioSystemStatusGet', function () {
|
||||
afterEach(function (done) {
|
||||
if (ourBoard.isConnected()) {
|
||||
ourBoard.disconnect().then(() => {
|
||||
done();
|
||||
}).catch(() => done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
afterEach(() => bluebirdChecks.noPendingPromises());
|
||||
|
||||
it('should not get system status if not connected', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.radioSystemStatusGet().should.be.rejected.and.notify(done);
|
||||
});
|
||||
it('should not get system status if streaming', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.streamStart()
|
||||
.then(() => {
|
||||
ourBoard.radioSystemStatusGet().then(() => {
|
||||
done('should have rejected');
|
||||
}).catch(() => {
|
||||
done(); // Test pass
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should not get system status if not firmware version 2', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioSystemStatusGet().should.be.rejected.and.notify(done);
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should get up system status if firmware version 2', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2'
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioSystemStatusGet().then(isUp => {
|
||||
expect(isUp).to.be.true;
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should get down system status if firmware version 2', function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulate: true,
|
||||
simulatorFirmwareVersion: 'v2',
|
||||
simulatorBoardFailure: true
|
||||
});
|
||||
ourBoard.connect(k.OBCISimulatorPortName)
|
||||
.then(() => {
|
||||
ourBoard.once('ready', () => {
|
||||
ourBoard.radioSystemStatusGet().then(isUp => {
|
||||
expect(isUp).to.be.false;
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
});
|
||||
|
||||
describe('#radioTests', function () {
|
||||
this.timeout(0);
|
||||
before(function (done) {
|
||||
ourBoard = new openBCIBoard.OpenBCIBoard({
|
||||
verbose: true,
|
||||
simulatorFirmwareVersion: 'v2',
|
||||
simulatorFragmentation: k.OBCISimulatorFragmentationRandom
|
||||
});
|
||||
ourBoard.connect(masterPortName).catch(err => done(err));
|
||||
|
||||
ourBoard.once('ready', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
after(function (done) {
|
||||
if (ourBoard.isConnected()) {
|
||||
ourBoard.disconnect().then(() => {
|
||||
done();
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
after(() => bluebirdChecks.noPendingPromises());
|
||||
it('should be able to get the channel number', function (done) {
|
||||
// Don't test if not using v2
|
||||
if (!ourBoard.usingVersionTwoFirmware()) return done();
|
||||
// The channel number should be between 0 and 25. Those are hard limits.
|
||||
ourBoard.radioChannelGet().then(res => {
|
||||
expect(res.channelNumber).to.be.within(0, 25);
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should be able to set the channel to 1', function (done) {
|
||||
// Don't test if not using v2
|
||||
if (!ourBoard.usingVersionTwoFirmware()) return done();
|
||||
ourBoard.radioChannelSet(1).then(channelNumber => {
|
||||
expect(channelNumber).to.equal(1);
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should be able to set the channel to 2', function (done) {
|
||||
// Don't test if not using v2
|
||||
if (!ourBoard.usingVersionTwoFirmware()) return done();
|
||||
ourBoard.radioChannelSet(2).then(channelNumber => {
|
||||
expect(channelNumber).to.equal(2);
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should be able to set the channel to 25', function (done) {
|
||||
// Don't test if not using v2
|
||||
if (!ourBoard.usingVersionTwoFirmware()) return done();
|
||||
ourBoard.radioChannelSet(25).then(channelNumber => {
|
||||
expect(channelNumber).to.equal(25);
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should be able to set the channel to 5', function (done) {
|
||||
// Don't test if not using v2
|
||||
if (!ourBoard.usingVersionTwoFirmware()) return done();
|
||||
ourBoard.radioChannelSet(5).then(channelNumber => {
|
||||
expect(channelNumber).to.equal(5);
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should be able to override host channel number, verify system is down, set the host back and verify system is up', function (done) {
|
||||
// Don't test if not using v2
|
||||
if (!ourBoard.usingVersionTwoFirmware()) return done();
|
||||
var systemChanNumber = 0;
|
||||
var newChanNum = 0;
|
||||
// var timey = Date.now()
|
||||
// Get the current system channel
|
||||
ourBoard.radioChannelGet()
|
||||
.then(res => {
|
||||
// Store it
|
||||
systemChanNumber = res.channelNumber;
|
||||
// console.log(`system channel number: ${res.channelNumber}`)
|
||||
if (systemChanNumber === 25) {
|
||||
newChanNum = 24;
|
||||
} else {
|
||||
newChanNum = systemChanNumber + 1;
|
||||
}
|
||||
// Call to change just the host
|
||||
return ourBoard.radioChannelSetHostOverride(newChanNum);
|
||||
})
|
||||
.then(newChanNumActual => {
|
||||
expect(newChanNumActual).to.equal(newChanNum);
|
||||
// timey = Date.now()
|
||||
// console.log(`new chan ${newChanNumActual} got`, timey, '0ms')
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(function () {
|
||||
// console.log(`get status`, Date.now(), `${Date.now() - timey}ms`)
|
||||
ourBoard.radioSystemStatusGet().then(isUp => {
|
||||
// console.log('resolving', Date.now(), `${Date.now() - timey}ms`)
|
||||
resolve(isUp);
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
}, 270); // Should be accurate after 270 seconds
|
||||
});
|
||||
})
|
||||
.then(isUp => {
|
||||
// console.log(`isUp test`, Date.now(), `${Date.now() - timey}ms`)
|
||||
expect(isUp).to.be.false;
|
||||
return ourBoard.radioChannelSetHostOverride(systemChanNumber); // Set back to good
|
||||
})
|
||||
.then(newChanNumActual => {
|
||||
// Verify we set it back to normal
|
||||
expect(newChanNumActual).to.equal(systemChanNumber);
|
||||
return ourBoard.radioSystemStatusGet();
|
||||
})
|
||||
.then(isUp => {
|
||||
expect(isUp).to.be.true;
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
it('should be able to get the poll time', function (done) {
|
||||
// Don't test if not using v2
|
||||
if (!ourBoard.usingVersionTwoFirmware()) return done();
|
||||
ourBoard.radioPollTimeGet().should.eventually.be.greaterThan(0).and.notify(done);
|
||||
});
|
||||
it('should be able to set the poll time', function (done) {
|
||||
// Don't test if not using v2
|
||||
if (!ourBoard.usingVersionTwoFirmware()) return done();
|
||||
ourBoard.radioPollTimeSet(80).should.become(80).and.notify(done);
|
||||
});
|
||||
it('should be able to change to default baud rate', function (done) {
|
||||
// Don't test if not using v2
|
||||
if (!ourBoard.usingVersionTwoFirmware()) return done();
|
||||
ourBoard.radioBaudRateSet('default').should.become(115200).and.notify(done);
|
||||
});
|
||||
it('should be able to change to fast baud rate', function (done) {
|
||||
// Don't test if not using v2
|
||||
if (!ourBoard.usingVersionTwoFirmware()) return done();
|
||||
ourBoard.radioBaudRateSet('fast').then(newBaudRate => {
|
||||
expect(newBaudRate).to.equal(230400);
|
||||
return ourBoard.radioBaudRateSet('default');
|
||||
}).then(() => {
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
it('should be able to set the system status', function (done) {
|
||||
// Don't test if not using v2
|
||||
if (!ourBoard.usingVersionTwoFirmware()) return done();
|
||||
ourBoard.radioSystemStatusGet().then(isUp => {
|
||||
expect(isUp).to.be.true;
|
||||
done();
|
||||
}).catch(err => done(err));
|
||||
});
|
||||
});
|
||||
});
|
||||
+2719
-2820
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+871
-687
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,108 @@
|
||||
// Converts timing events to use promises, to gain bluebird's checks
|
||||
|
||||
function instrumentTimingEvents (module, type) {
|
||||
// replaces module[type] with a function that does the same thing but uses a promise
|
||||
// assumes the first argument will be to a callback function
|
||||
|
||||
// if this a setSomething function with a clearSomething partner, the partner will also be instrumented
|
||||
var setName = type;
|
||||
var clearName = null;
|
||||
if (type.substring(0, 3) === 'set') {
|
||||
clearName = 'clear' + type.substring(3);
|
||||
}
|
||||
|
||||
if (!module[setName]) return;
|
||||
|
||||
// store original functions
|
||||
var originalSet = module[setName];
|
||||
var originalClear = module[clearName];
|
||||
exports[setName + 'Ignored'] = originalSet;
|
||||
|
||||
// dictionary to store promise details for each call
|
||||
var events = {};
|
||||
|
||||
// setAsPromise() is the brunt of the function. It replaces the previous global function,
|
||||
// and sets up a promise to resolve when the callback is called
|
||||
var eventCount = 0;
|
||||
var setAsPromise = function (callback) {
|
||||
var args = [].slice.call(arguments);
|
||||
|
||||
var eventNum = ++eventCount;
|
||||
var eventHandle;
|
||||
|
||||
if (setAsPromise._ignoreCount > 0) {
|
||||
--setAsPromise._ignoreCount;
|
||||
|
||||
return originalSet.apply(this, args);
|
||||
}
|
||||
|
||||
// actual callback is replaced by promise resolve
|
||||
args[0] = function () {
|
||||
if (!events[eventNum]) throw new Error(setName + ' ' + eventNum + ' disappeared');
|
||||
events[eventNum].resolve([].slice.call(arguments));
|
||||
};
|
||||
|
||||
eventHandle = originalSet.apply(this, args) || eventNum;
|
||||
|
||||
// this portion is a function so that setInterval may be handled via recursion
|
||||
function dispatch () {
|
||||
var handlerDetails = { handle: eventHandle };
|
||||
|
||||
var promise = new Promise((resolve, reject) => {
|
||||
handlerDetails.resolve = resolve;
|
||||
handlerDetails.reject = reject;
|
||||
});
|
||||
|
||||
handlerDetails.promise = promise;
|
||||
events[eventNum] = handlerDetails;
|
||||
|
||||
promise.then(function (argumentArray) {
|
||||
if (type !== 'setInterval') {
|
||||
delete events[eventNum];
|
||||
} else {
|
||||
dispatch();
|
||||
}
|
||||
|
||||
// call original handler
|
||||
callback.apply(this, argumentArray);
|
||||
}, () => {
|
||||
originalClear(eventHandle);
|
||||
delete events[eventNum];
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
dispatch();
|
||||
|
||||
return eventNum;
|
||||
};
|
||||
|
||||
// actually replace functions with instrumented ones
|
||||
setAsPromise._ignoreCount = 0;
|
||||
module[setName] = setAsPromise;
|
||||
Object.defineProperty(setAsPromise, 'name', { value: setName + 'AsPromise' });
|
||||
|
||||
if (clearName) {
|
||||
module[clearName] = (eventNum) => {
|
||||
if (!events[eventNum]) {
|
||||
originalClear(eventNum);
|
||||
} else {
|
||||
events[eventNum].reject(new Error('cleared'));
|
||||
}
|
||||
};
|
||||
Object.defineProperty(module[clearName], 'name', { value: clearName + 'AsPromise' });
|
||||
}
|
||||
}
|
||||
|
||||
instrumentTimingEvents(global, 'setTimeout');
|
||||
instrumentTimingEvents(global, 'setInterval');
|
||||
instrumentTimingEvents(global, 'setImmediate');
|
||||
// // Possible TODO: nextTick needs some exceptions included to prevent infinite recursion
|
||||
// instrumentTimingEvents(process, 'nextTick');
|
||||
|
||||
// the next call to the passed function should not be promisified
|
||||
// may be queued multiple times
|
||||
exports.ignoreOnce = (ignored) => {
|
||||
ignored._ignoreCount ++;
|
||||
};
|
||||
@@ -1,135 +0,0 @@
|
||||
// This takes a openbci-sdk factory and mocks the shit out of it in complete isolation per require of this file
|
||||
|
||||
"use strict";
|
||||
|
||||
var Hardware = function () {
|
||||
this.bciBoard = {};
|
||||
//this.mockBinding = {
|
||||
// connect: this.connect.bind(this),
|
||||
// disconnect: this.disconnect.bind(this),
|
||||
// streamStart: this.streamStart.bind(this),
|
||||
// streamStop: this.streamStop.bind(this),
|
||||
// write: this.write.bind(this),
|
||||
// softReset: this.softReset.bind(this),
|
||||
// autoFindOpenBCIBoard: this.autoFindOpenBCIBoard.bind(this),
|
||||
// simulatorEnable: this.simulatorEnable.bind(this),
|
||||
// simulatorDisable: this.simulatorDisable.bind(this)
|
||||
//};
|
||||
};
|
||||
|
||||
Hardware.prototype.reset = function () {
|
||||
this.bciBoard = {};
|
||||
};
|
||||
|
||||
Hardware.prototype.createBoard = function () {
|
||||
this.bciBoard = {
|
||||
serial: null,
|
||||
connected: false,
|
||||
streaming: false,
|
||||
isSimulating: false,
|
||||
badPackets: 0,
|
||||
bytesIn: 0,
|
||||
commandsToWrite: 0,
|
||||
sampleCount: 0
|
||||
}
|
||||
};
|
||||
|
||||
Hardware.prototype.connect = function (portName) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if(!this.bciBoard) reject('Board does not exist - call hardware.createBoard() first');
|
||||
|
||||
this.bciBoard.connected = true;
|
||||
this.bciBoard.serial = {
|
||||
portName:portName,
|
||||
baudRate:115200
|
||||
};
|
||||
resolve(this.bciBoard.serial);
|
||||
});
|
||||
};
|
||||
|
||||
Hardware.prototype.disconnect = function () {
|
||||
return new Promise((resolve,reject) => {
|
||||
if(!this.bciBoard) reject('Board does not exist - call hardware.createBoard() first');
|
||||
|
||||
this.bciBoard.connected = false;
|
||||
this.bciBoard.serial = null;
|
||||
});
|
||||
};
|
||||
|
||||
Hardware.prototype.streamStart = function () {
|
||||
return new Promise((resolve,reject) => {
|
||||
if(!this.bciBoard) reject('Board does not exist - call hardware.createBoard() first');
|
||||
|
||||
this.bciBoard.streaming = true;
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
Hardware.prototype.streamStop = function () {
|
||||
return new Promise((resolve,reject) => {
|
||||
if(!this.bciBoard) reject('Board does not exist - call hardware.createBoard() first');
|
||||
|
||||
this.bciBoard.streaming = false;
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
Hardware.prototype.simulatorEnable = function () {
|
||||
return new Promise((resolve,reject) => {
|
||||
if(!this.bciBoard) reject('Board does not exist - call hardware.createBoard() first');
|
||||
|
||||
this.bciBoard.isSimulating = true;
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
Hardware.prototype.simulatorDisable = function () {
|
||||
return new Promise((resolve,reject) => {
|
||||
if(!this.bciBoard) reject('Board does not exist - call hardware.createBoard() first');
|
||||
|
||||
this.bciBoard.isSimulating = false;
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
Hardware.prototype.autoFindOpenBCIBoard = function () {
|
||||
return new Promise((resolve,reject) => {
|
||||
resolve('/dev/tty.usbserial-D069XXX');
|
||||
});
|
||||
};
|
||||
|
||||
Hardware.prototype.emitSample = function (data) {
|
||||
return new Promise((resolve,reject) => {
|
||||
if(!this.bciBoard) reject('Board does not exist - call hardware.createBoard() first');
|
||||
|
||||
this.bciBoard.isSimulating = false;
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
Hardware.prototype.fakeData = function (data) {
|
||||
//do something, not quite sure what yet...
|
||||
};
|
||||
|
||||
var hardware = new Hardware();
|
||||
|
||||
var SandboxedModule = require('sandboxed-module');
|
||||
|
||||
var openBCIBoard = SandboxedModule.require('../openBCIBoard', {
|
||||
requires: {
|
||||
fs: {
|
||||
read: hardware.fakeData.bind(hardware)
|
||||
}
|
||||
},
|
||||
globals: {
|
||||
process: {
|
||||
platform: 'darwin',
|
||||
nextTick: process.nextTick
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
openBCIBoard.hardware = hardware;
|
||||
|
||||
module.exports = openBCIBoard;
|
||||
Referência em uma Nova Issue
Bloquear um usuário