Skip to content

Commit e47128c

Browse files
author
AJ Keller
authored
Merge pull request #140 from OpenBCI/development
Lab Streaming Layer example (#139)
2 parents 3f42be4 + 1a60297 commit e47128c

File tree

23 files changed

+709
-278
lines changed

23 files changed

+709
-278
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ node_js:
2020
install:
2121
- npm install --all
2222
script:
23+
- npm run lint
2324
- npm run test-cov

README.md

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,46 +1172,12 @@ The name of the simulator port.
11721172
11731173
### <a name="interfacing-with-other-tools-labstreaminglayer"></a> LabStreamingLayer
11741174
1175-
[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).
1175+
[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.
11761176
1177-
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.
1177+
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.
11781178
1179-
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:
1179+
Follow the directions in the [readme](https://github.com/OpenBCI/OpenBCI_NodeJS/blob/master/examples/labstreaminglayer/readme.md) to get started.
11801180
1181-
In your NodeJS code, before initializing/connecting to the OpenBCIBoard:
1182-
```js
1183-
// Construct LSL Handoff Python Shell
1184-
var PythonShell = require('python-shell');
1185-
var lsloutlet = new PythonShell('LslHandoff.py');
1186-
1187-
lsloutlet.on('message', function(message){
1188-
console.log('LslOutlet: ' + message);
1189-
});
1190-
console.log('Python Shell Created for LSLHandoff');
1191-
```
1192-
1193-
In your NodeJS code, when reading samples:
1194-
```js
1195-
st = sample.channelData.join(' ')
1196-
//getTime returns milliseconds since midnight 1970/01/01
1197-
var s = ''+ sample.timeStamp + ': '+ st
1198-
lsloutlet.send(s)
1199-
```
1200-
1201-
in LSLHandoff.py:
1202-
```py
1203-
from pylsl import StreamInfo, StreamOutlet
1204-
info = StreamInfo('OpenBCI_EEG', 'EEG', 8, 250, 'float32', '[RANDOM NUMBER HERE]')
1205-
outlet = StreamOutlet(info)
1206-
while True:
1207-
strSample = raw_input().split(': ',1)
1208-
sample = map(float, strSample[1].split(' '))
1209-
stamp = float(strSample[0])
1210-
1211-
outlet.push_sample(sample, stamp)
1212-
print('Pushed Sample At: ' + strSample[0])
1213-
```
1214-
AUX data would be done the same way in a separate LSL stream.
12151181
12161182
## <a name="developing"></a> Developing:
12171183
### <a name="developing-running"></a> Running:

changelog.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
# 1.5.1
2+
3+
### New Features
4+
* Add new example for Lab stream layer (#139) thanks @gabrielibagon
5+
6+
### Breaking changes
7+
* Removed `impedanceCalculationForChannel()` and `impedanceCalculationForAllChannels` from `OpenBCISample.js`
8+
9+
### Bug Fixes
10+
* Fixes #131 - 16 chan not working by sending a channel command and parsing the return.
11+
112
# 1.5.0
213

314
### New Features

examples/debug/debug.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ ourBoard.autoFindOpenBCIBoard().then(portName => {
4545
*/
4646
var readyFunc = () => {
4747
// Get the sample rate after 'ready'
48-
sampleRate = ourBoard.sampleRate();
4948
if (stream) {
5049
ourBoard.streamStart()
5150
.catch(err => {
@@ -65,10 +64,10 @@ var sampleFunc = sample => {
6564
ourBoard.on('ready', readyFunc);
6665
ourBoard.on('sample', sampleFunc);
6766

68-
6967
function exitHandler (options, err) {
7068
if (options.cleanup) {
7169
if (verbose) console.log('clean');
70+
ourBoard.removeAllListeners();
7271
/** Do additional clean up here */
7372
}
7473
if (err) console.log(err.stack);
@@ -81,14 +80,14 @@ function exitHandler (options, err) {
8180
}
8281
}
8382

84-
if (process.platform === "win32") {
85-
const rl = require("readline").createInterface({
83+
if (process.platform === 'win32') {
84+
const rl = require('readline').createInterface({
8685
input: process.stdin,
8786
output: process.stdout
8887
});
8988

90-
rl.on("SIGINT", function () {
91-
process.emit("SIGINT");
89+
rl.on('SIGINT', function () {
90+
process.emit('SIGINT');
9291
});
9392
}
9493

@@ -105,4 +104,4 @@ process.on('SIGINT', exitHandler.bind(null, {
105104
// catches uncaught exceptions
106105
process.on('uncaughtException', exitHandler.bind(null, {
107106
exit: true
108-
}));
107+
}));

examples/debug/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@
1313
"author": "AJ Keller",
1414
"license": "MIT",
1515
"dependencies": {
16-
"openbci": "^1.4.1"
16+
"openbci": "^1.5.0"
1717
}
1818
}

examples/getStreaming/getStreaming.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ ourBoard.autoFindOpenBCIBoard().then(portName => {
2727
*/
2828
ourBoard.connect(portName) // Port name is a serial port name, see `.listPorts()`
2929
.then(() => {
30-
ourBoard.on('ready',() => {
30+
ourBoard.on('ready', () => {
3131
ourBoard.streamStart();
32-
ourBoard.on('sample',(sample) => {
32+
ourBoard.on('sample', (sample) => {
3333
/** Work with sample */
34-
for (var i = 0; i < ourBoard.numberOfChannels(); i++) {
35-
console.log("Channel " + (i + 1) + ": " + sample.channelData[i].toFixed(8) + " Volts.");
34+
for (let i = 0; i < ourBoard.numberOfChannels(); i++) {
35+
console.log(`Channel ${(i + 1)}: ${sample.channelData[i].toFixed(8)} Volts.`);
3636
// prints to the console
3737
// "Channel 1: 0.00001987 Volts."
3838
// "Channel 2: 0.00002255 Volts."
@@ -51,6 +51,7 @@ ourBoard.autoFindOpenBCIBoard().then(portName => {
5151
function exitHandler (options, err) {
5252
if (options.cleanup) {
5353
if (verbose) console.log('clean');
54+
ourBoard.removeAllListeners();
5455
/** Do additional clean up here */
5556
}
5657
if (err) console.log(err.stack);
@@ -60,14 +61,14 @@ function exitHandler (options, err) {
6061
}
6162
}
6263

63-
if (process.platform === "win32") {
64-
const rl = require("readline").createInterface({
64+
if (process.platform === 'win32') {
65+
const rl = require('readline').createInterface({
6566
input: process.stdin,
6667
output: process.stdout
6768
});
6869

69-
rl.on("SIGINT", function () {
70-
process.emit("SIGINT");
70+
rl.on('SIGINT', function () {
71+
process.emit('SIGINT');
7172
});
7273
}
7374

@@ -84,4 +85,4 @@ process.on('SIGINT', exitHandler.bind(null, {
8485
// catches uncaught exceptions
8586
process.on('uncaughtException', exitHandler.bind(null, {
8687
exit: true
87-
}));
88+
}));

examples/getStreaming/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@
1313
"author": "AJ Keller",
1414
"license": "MIT",
1515
"dependencies": {
16-
"openbci": "^1.4.1"
16+
"openbci": "^1.5.0"
1717
}
1818
}

examples/getStreamingDaisy/getStreamingDaisy.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ ourBoard.autoFindOpenBCIBoard().then(portName => {
3333
*/
3434
ourBoard.connect(portName) // Port name is a serial port name, see `.listPorts()`
3535
.then(() => {
36-
ourBoard.once('ready',() => {
36+
ourBoard.once('ready', () => {
3737
ourBoard.streamStart();
38-
ourBoard.on('sample',(sample) => {
38+
ourBoard.on('sample', (sample) => {
3939
/** Work with sample */
40-
for (var i = 0; i < ourBoard.numberOfChannels(); i++) {
41-
console.log("Channel " + (i + 1) + ": " + sample.channelData[i].toFixed(8) + " Volts.");
40+
for (let i = 0; i < ourBoard.numberOfChannels(); i++) {
41+
console.log(`Channel ${(i + 1)}: ${sample.channelData[i].toFixed(8)} Volts.`);
4242
// prints to the console
4343
// "Channel 1: 0.00001987 Volts."
4444
// "Channel 2: 0.00002255 Volts."
@@ -57,6 +57,7 @@ ourBoard.autoFindOpenBCIBoard().then(portName => {
5757
function exitHandler (options, err) {
5858
if (options.cleanup) {
5959
if (verbose) console.log('clean');
60+
ourBoard.removeAllListeners();
6061
/** Do additional clean up here */
6162
}
6263
if (err) console.log(err.stack);
@@ -66,14 +67,14 @@ function exitHandler (options, err) {
6667
}
6768
}
6869

69-
if (process.platform === "win32") {
70-
const rl = require("readline").createInterface({
70+
if (process.platform === 'win32') {
71+
const rl = require('readline').createInterface({
7172
input: process.stdin,
7273
output: process.stdout
7374
});
7475

75-
rl.on("SIGINT", function () {
76-
process.emit("SIGINT");
76+
rl.on('SIGINT', function () {
77+
process.emit('SIGINT');
7778
});
7879
}
7980

@@ -90,4 +91,4 @@ process.on('SIGINT', exitHandler.bind(null, {
9091
// catches uncaught exceptions
9192
process.on('uncaughtException', exitHandler.bind(null, {
9293
exit: true
93-
}));
94+
}));

examples/impedance/impedance.js

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/**
2+
* This is an example from the readme.md
3+
* On windows you should run with PowerShell not git bash.
4+
* Install
5+
* [nodejs](https://nodejs.org/en/)
6+
*
7+
* To run:
8+
* change directory to this file `cd examples/debug`
9+
* do `npm install`
10+
* then `npm start`
11+
*/
12+
var debug = false; // Pretty print any bytes in and out... it's amazing...
13+
var verbose = true; // Adds verbosity to functions
14+
15+
var OpenBCIBoard = require('openbci').OpenBCIBoard;
16+
var ourBoard = new OpenBCIBoard({
17+
debug: debug,
18+
verbose: verbose
19+
});
20+
var k = require('openbci').OpenBCIConstants;
21+
22+
let startedImpedance = false;
23+
let iBuffer = [];
24+
let count = 0;
25+
const window = 50;
26+
27+
ourBoard.autoFindOpenBCIBoard().then(portName => {
28+
if (portName) {
29+
/**
30+
* Connect to the board with portName
31+
* Only works if one board is plugged in
32+
* i.e. ourBoard.connect(portName).....
33+
*/
34+
ourBoard.connect(portName) // Port name is a serial port name, see `.listPorts()`
35+
.then(() => {
36+
ourBoard.on('ready', () => {
37+
ourBoard.streamStart();
38+
ourBoard.on('sample', (sample) => {
39+
if (startedImpedance === false) {
40+
startedImpedance = true;
41+
k.getImpedanceSetter(1, false, true)
42+
.then((commands) => {
43+
return ourBoard.write(commands);
44+
})
45+
.then(() => {
46+
console.log('wrote commands to board');
47+
})
48+
.catch((err) => {
49+
console.log('errr', err);
50+
});
51+
}
52+
/** Work with sample */
53+
const chan1ValInVolts = sample.channelData[0];
54+
55+
// const impedance = chan1ValInVolts / k.OBCILeadOffDriveInAmps;
56+
57+
// console.log(`impedance:\t${impedance} kOhms`);
58+
iBuffer.push(chan1ValInVolts);
59+
count++;
60+
if (count >= window) {
61+
let max = 0.0; // sumSquared
62+
for (let i = 0; i < window; i++) {
63+
if (iBuffer[i] > max) {
64+
max = iBuffer[i];
65+
}
66+
// sumSquared += iBuffer[i] * iBuffer[i];
67+
}
68+
let min = 0.0;
69+
for (let i = 0; i < window; i++) {
70+
if (iBuffer[i] < min) {
71+
min = iBuffer[i];
72+
}
73+
// sumSquared += iBuffer[i] * iBuffer[i];
74+
}
75+
const vP2P = max - min; // peak to peak
76+
77+
console.log(`impedance: \t${vP2P / 2 / k.OBCILeadOffDriveInAmps}`);
78+
// console.log(`impedance: ${vRms/k.OBCILeadOffDriveInAmps}`);
79+
80+
// const mean_squared = sumSquared / window;
81+
// const root_mean_squared = Math.sqrt(mean_squared);
82+
// console.log(`impedance: ${root_mean_squared/k.OBCILeadOffDriveInAmps}`);
83+
84+
count = 0;
85+
iBuffer = [];
86+
}
87+
// console.log(`uV:\t${chan1ValInVolts/(10*6)}\nimpedance:\t${impedance}`);
88+
});
89+
});
90+
});
91+
} else {
92+
/** Unable to auto find OpenBCI board */
93+
console.log('Unable to auto find OpenBCI board');
94+
}
95+
});
96+
97+
function exitHandler (options, err) {
98+
if (options.cleanup) {
99+
if (verbose) console.log('clean');
100+
/** Do additional clean up here */
101+
ourBoard.disconnect().catch(console.log);
102+
ourBoard.removeAllListeners();
103+
}
104+
if (err) console.log(err.stack);
105+
if (options.exit) {
106+
if (verbose) console.log('exit');
107+
ourBoard.streamStop()
108+
.then(() => {
109+
process.exit(0);
110+
})
111+
.catch((err) => {
112+
console.log(err);
113+
process.exit(0);
114+
});
115+
}
116+
}
117+
118+
if (process.platform === 'win32') {
119+
const rl = require('readline').createInterface({
120+
input: process.stdin,
121+
output: process.stdout
122+
});
123+
124+
rl.on('SIGINT', function () {
125+
process.emit('SIGINT');
126+
});
127+
}
128+
129+
// do something when app is closing
130+
process.on('exit', exitHandler.bind(null, {
131+
cleanup: true
132+
}));
133+
134+
// catches ctrl+c event
135+
process.on('SIGINT', exitHandler.bind(null, {
136+
exit: true
137+
}));
138+
139+
// catches uncaught exceptions
140+
process.on('uncaughtException', exitHandler.bind(null, {
141+
exit: true
142+
}));

examples/impedance/package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "get-streaming",
3+
"version": "1.0.0",
4+
"description": "Get impedance example",
5+
"main": "getStreaming.js",
6+
"scripts": {
7+
"start": "node impedance.js",
8+
"test": "echo \"Error: no test specified\" && exit 1"
9+
},
10+
"keywords": [
11+
"get"
12+
],
13+
"author": "AJ Keller",
14+
"license": "MIT",
15+
"dependencies": {
16+
"openbci": "^1.5.0"
17+
}
18+
}

0 commit comments

Comments
 (0)