Skip to content

Commit 0dd0ece

Browse files
author
AJ Keller
committed
Add: max channel support for simulator
1 parent 2d7547c commit 0dd0ece

11 files changed

+378
-115
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,7 @@ Stop logging to the SD card and close any open file. If you are not streaming wh
847847
848848
**_Returns_** resolves if the command was added to the write queue.
849849
850-
### <a name="method-set-info-for-board-type"></a> .setInfoForBoardType(boardType)
850+
### <a name="method-set-info-for-board-type"></a> .overrideInfoForBoardType(boardType)
851851
852852
Set the info property for board type.
853853

changelog.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
1+
# 1.5.0
2+
3+
### New Features
4+
* New simulator option `simulatorDaisyModuleCanBeAttached` - Boolean, deafults to true, allows the simulation of the a hot swapped daisy board or simulates a misinformed module.
5+
6+
### Bug Fixes
7+
* Fixes #131 - 16 chan not working by sending a channel command and parsing the return.
8+
* Fixed bug where end of transmission characters would not be ejected from buffer.
9+
10+
### Breaking changes
11+
* `.overrideInfoForBoardType()` changed to `.overrideInfoForBoardType()` to elevate it's dangerous nature.
12+
113
# 1.4.4
214

315
### New Features
416
* Set max number of channels for the board to use with `.setMaxChannels()` see readme.md
5-
* Set the core info object that drives the module with `.setInfoForBoardType()` see readme.md
6-
* Get info for the core obhect that drives the module with `.getInfo()` see readme.md
17+
* Set the core info object that drives the module with `.overrideInfoForBoardType()` see readme.md
18+
* Get info for the core object that drives the module with `.getInfo()` see readme.md
719

820
### Work In Progress
921
* Bug where daisy would sometimes not be recognized which destroyed all data.

openBCIBoard.js

Lines changed: 154 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ function OpenBCIFactory () {
2323
simulate: false,
2424
simulatorBoardFailure: false,
2525
simulatorDaisyModuleAttached: false,
26+
simulatorDaisyModuleCanBeAttached: true,
2627
simulatorFirmwareVersion: [k.OBCIFirmwareV1, k.OBCIFirmwareV2],
2728
simulatorFragmentation: [k.OBCISimulatorFragmentationNone, k.OBCISimulatorFragmentationRandom, k.OBCISimulatorFragmentationFullBuffers, k.OBCISimulatorFragmentationOneByOne],
2829
simulatorLatencyTime: 16,
@@ -41,80 +42,84 @@ function OpenBCIFactory () {
4142
};
4243

4344
/**
44-
* @description The initialization method to call first, before any other method.
45-
* @param options (optional) - Board optional configurations.
46-
* - `baudRate` {Number} - Baud Rate, defaults to 115200. Manipulating this is allowed if
47-
* firmware on board has been previously configured.
48-
*
49-
* - `boardType` {String} - Specifies type of OpenBCI board.
50-
* 3 Possible Boards:
51-
* `default` - 8 Channel OpenBCI board (Default)
52-
* `daisy` - 8 Channel OpenBCI board with Daisy Module. Total of 16 channels.
53-
* `ganglion` - 4 Channel board
54-
* (NOTE: THIS IS IN-OP TIL RELEASE OF GANGLION BOARD 07/2016)
55-
*
56-
* - `simulate` {Boolean} - Full functionality, just mock data. Must attach Daisy module by setting
57-
* `simulatorDaisyModuleAttached` to `true` in order to get 16 channels. (Default `false`)
58-
*
59-
* - `simulatorBoardFailure` {Boolean} - Simulates board communications failure. This occurs when the RFduino on
60-
* the board is not polling the RFduino on the dongle. (Default `false`)
61-
*
62-
* - `simulatorDaisyModuleAttached` {Boolean} - Simulates a daisy module being attached to the OpenBCI board.
63-
* This is useful if you want to test how your application reacts to a user requesting 16 channels
64-
* but there is no daisy module actually attached, or vice versa, where there is a daisy module
65-
* attached and the user only wants to use 8 channels. (Default `false`)
66-
*
67-
* - `simulatorFirmwareVersion` {String} - Allows simulator to be started with firmware version 2 features
68-
* 2 Possible Options:
69-
* `v1` - Firmware Version 1 (Default)
70-
* `v2` - Firmware Version 2
71-
*
72-
* - `simulatorFragmentation` {String} - Specifies how to break packets to simulate fragmentation, which
73-
* occurs commonly in real devices. It is recommended to test code with this enabled.
74-
* 4 Possible Options:
75-
* `none` - do not fragment packets; output complete chunks immediately when produced (Default)
76-
* `random` - output random small chunks of data interspersed with full buffers
77-
* `fullBuffers` - allow buffers to fill up until the latency timer has expired
78-
* `oneByOne` - output each byte separately
79-
*
80-
* - `simulatorLatencyTime` {Number} - The time in milliseconds to wait before sending partially full buffers,
81-
if `simulatorFragmentation` is specified. (Default `16`)
82-
*
83-
* - `simulatorBufferSize` {Number} - The size of a full buffer of data, if `simulatorFragmentation` is
84-
* specified. (Default `4096`)
85-
*
86-
* - `simulatorHasAccelerometer` - {Boolean} - Sets simulator to send packets with accelerometer data. (Default `true`)
87-
*
88-
* - `simulatorInjectAlpha` - {Boolean} - Inject a 10Hz alpha wave in Channels 1 and 2 (Default `true`)
89-
*
90-
* - `simulatorInjectLineNoise` {String} - Injects line noise on channels.
91-
* 3 Possible Options:
92-
* `60Hz` - 60Hz line noise (Default) [America]
93-
* `50Hz` - 50Hz line noise [Europe]
94-
* `none` - Do not inject line noise.
95-
*
96-
* - `simulatorSampleRate` {Number} - The sample rate to use for the simulator. Simulator will set to 125 if
97-
* `simulatorDaisyModuleAttached` is set `true`. However, setting this option overrides that
98-
* setting and this sample rate will be used. (Default is `250`)
99-
*
100-
* - `simulatorSerialPortFailure` {Boolean} - Simulates not being able to open a serial connection. Most likely
101-
* due to a OpenBCI dongle not being plugged in.
102-
*
103-
* - `sntpTimeSync` - {Boolean} Syncs the module up with an SNTP time server and uses that as single source
104-
* of truth instead of local computer time. If you are running experiements on your local
105-
* computer, keep this `false`. (Default `false`)
106-
*
107-
* - `sntpTimeSyncHost` - {String} The ntp server to use, can be either sntp or ntp. (Defaults `pool.ntp.org`).
108-
*
109-
* - `sntpTimeSyncPort` - {Number} The port to access the ntp server. (Defaults `123`)
110-
*
111-
* - `verbose` {Boolean} - Print out useful debugging events. (Default `false`)
112-
*
113-
* - `debug` {Boolean} - Print out a raw dump of bytes sent and received. (Default `false`)
114-
*
115-
* @constructor
116-
* @author AJ Keller (@pushtheworldllc)
117-
*/
45+
* @description The initialization method to call first, before any other method.
46+
* @param options (optional) - Board optional configurations.
47+
* - `baudRate` {Number} - Baud Rate, defaults to 115200. Manipulating this is allowed if
48+
* firmware on board has been previously configured.
49+
*
50+
* - `boardType` {String} - Specifies type of OpenBCI board.
51+
* 3 Possible Boards:
52+
* `default` - 8 Channel OpenBCI board (Default)
53+
* `daisy` - 8 Channel OpenBCI board with Daisy Module. Total of 16 channels.
54+
* `ganglion` - 4 Channel board
55+
* (NOTE: THIS IS IN-OP TIL RELEASE OF GANGLION BOARD 07/2016)
56+
*
57+
* - `simulate` {Boolean} - Full functionality, just mock data. Must attach Daisy module by setting
58+
* `simulatorDaisyModuleAttached` to `true` in order to get 16 channels. (Default `false`)
59+
*
60+
* - `simulatorBoardFailure` {Boolean} - Simulates board communications failure. This occurs when the RFduino on
61+
* the board is not polling the RFduino on the dongle. (Default `false`)
62+
*
63+
* - `simulatorDaisyModuleAttached` {Boolean} - Simulates a daisy module being attached to the OpenBCI board.
64+
* This is useful if you want to test how your application reacts to a user requesting 16 channels
65+
* but there is no daisy module actually attached, or vice versa, where there is a daisy module
66+
* attached and the user only wants to use 8 channels. (Default `false`)
67+
*
68+
* - `simulatorDaisyModuleCanBeAttached` {Boolean} - Allows the simulation of the a hot swapped daisy board.
69+
* For example: You coule simulate if the board has only detected 8 channels and the user requested
70+
* 16 channels. (Default `true`)
71+
*
72+
* - `simulatorFirmwareVersion` {String} - Allows simulator to be started with firmware version 2 features
73+
* 2 Possible Options:
74+
* `v1` - Firmware Version 1 (Default)
75+
* `v2` - Firmware Version 2
76+
*
77+
* - `simulatorFragmentation` {String} - Specifies how to break packets to simulate fragmentation, which
78+
* occurs commonly in real devices. It is recommended to test code with this enabled.
79+
* 4 Possible Options:
80+
* `none` - do not fragment packets; output complete chunks immediately when produced (Default)
81+
* `random` - output random small chunks of data interspersed with full buffers
82+
* `fullBuffers` - allow buffers to fill up until the latency timer has expired
83+
* `oneByOne` - output each byte separately
84+
*
85+
* - `simulatorLatencyTime` {Number} - The time in milliseconds to wait before sending partially full buffers,
86+
* if `simulatorFragmentation` is specified. (Default `16`)
87+
*
88+
* - `simulatorBufferSize` {Number} - The size of a full buffer of data, if `simulatorFragmentation` is
89+
* specified. (Default `4096`)
90+
*
91+
* - `simulatorHasAccelerometer` - {Boolean} - Sets simulator to send packets with accelerometer data. (Default `true`)
92+
*
93+
* - `simulatorInjectAlpha` - {Boolean} - Inject a 10Hz alpha wave in Channels 1 and 2 (Default `true`)
94+
*
95+
* - `simulatorInjectLineNoise` {String} - Injects line noise on channels.
96+
* 3 Possible Options:
97+
* `60Hz` - 60Hz line noise (Default) [America]
98+
* `50Hz` - 50Hz line noise [Europe]
99+
* `none` - Do not inject line noise.
100+
*
101+
* - `simulatorSampleRate` {Number} - The sample rate to use for the simulator. Simulator will set to 125 if
102+
* `simulatorDaisyModuleAttached` is set `true`. However, setting this option overrides that
103+
* setting and this sample rate will be used. (Default is `250`)
104+
*
105+
* - `simulatorSerialPortFailure` {Boolean} - Simulates not being able to open a serial connection. Most likely
106+
* due to a OpenBCI dongle not being plugged in.
107+
*
108+
* - `sntpTimeSync` - {Boolean} Syncs the module up with an SNTP time server and uses that as single source
109+
* of truth instead of local computer time. If you are running experiements on your local
110+
* computer, keep this `false`. (Default `false`)
111+
*
112+
* - `sntpTimeSyncHost` - {String} The ntp server to use, can be either sntp or ntp. (Defaults `pool.ntp.org`).
113+
*
114+
* - `sntpTimeSyncPort` - {Number} The port to access the ntp server. (Defaults `123`)
115+
*
116+
* - `verbose` {Boolean} - Print out useful debugging events. (Default `false`)
117+
*
118+
* - `debug` {Boolean} - Print out a raw dump of bytes sent and received. (Default `false`)
119+
*
120+
* @constructor
121+
* @author AJ Keller (@pushtheworldllc)
122+
*/
118123
function OpenBCIBoard (options) {
119124
options = (typeof options !== 'function') && options || {};
120125
var opts = {};
@@ -206,6 +211,7 @@ function OpenBCIFactory () {
206211
this.timeOfPacketArrival = 0;
207212
this.writeOutDelay = k.OBCIWriteIntervalDelayMSShort;
208213
// Strings
214+
this.portName = null;
209215

210216
// NTP
211217
if (this.options.sntpTimeSync) {
@@ -254,6 +260,7 @@ function OpenBCIFactory () {
254260
alpha: this.options.simulatorInjectAlpha,
255261
boardFailure: this.options.simulatorBoardFailure,
256262
daisy: this.options.simulatorDaisyModuleAttached,
263+
daisyCanBeAttached: this.options.simulatorDaisyModuleCanBeAttached,
257264
drift: this.options.simulatorInternalClockDrift,
258265
firmwareVersion: this.options.simulatorFirmwareVersion,
259266
fragmentation: this.options.simulatorFragmentation,
@@ -378,14 +385,24 @@ function OpenBCIFactory () {
378385
return this.serial.isOpen();
379386
};
380387

388+
381389
/**
382390
* @description Checks if the board is currently sending samples.
383391
* @returns {boolean} - True if streaming.
384392
*/
393+
OpenBCIBoard.prototype.isSimulating = function () {
394+
return this.options.simulate;
395+
};
396+
397+
/**
398+
* @description Checks if the board is currently sending samples.
399+
* @returns {boolean} - True if streaming.
400+
*/
385401
OpenBCIBoard.prototype.isStreaming = function () {
386402
return this._streaming;
387403
};
388404

405+
389406
/**
390407
* @description Sends a start streaming command to the board.
391408
* @returns {Promise} indicating if the signal was able to be sent.
@@ -1012,6 +1029,14 @@ function OpenBCIFactory () {
10121029
});
10131030
};
10141031

1032+
/**
1033+
* Get the board type.
1034+
* @return boardType: string
1035+
*/
1036+
OpenBCIBoard.prototype.getBoardType = function() {
1037+
return this.info.boardType;
1038+
};
1039+
10151040
/**
10161041
* Get the core info object.
10171042
* @return {{boardType: string, sampleRate: number, firmware: string, numberOfChannels: number, missedPackets: number}}
@@ -1025,7 +1050,7 @@ function OpenBCIFactory () {
10251050
* @param boardType {String}
10261051
* `default` or `daisy`. Defaults to `default`.
10271052
*/
1028-
OpenBCIBoard.prototype.setInfoForBoardType = function(boardType) {
1053+
OpenBCIBoard.prototype.overrideInfoForBoardType = function(boardType) {
10291054
switch (boardType) {
10301055
case k.OBCIBoardDaisy:
10311056
this.info.boardType = k.OBCIBoardDaisy;
@@ -1043,20 +1068,60 @@ function OpenBCIFactory () {
10431068

10441069
/**
10451070
* Used to set the max number of channels on the Cyton board.
1046-
* @param numberOfChannels {number}
1047-
* Either 8 or 16
1071+
* @param boardType {String}
1072+
* Either `default` or `daisy`
10481073
* @return {Promise}
10491074
*/
1050-
OpenBCIBoard.prototype.setMaxChannels = function (numberOfChannels) {
1051-
if (numberOfChannels === k.OBCINumberOfChannelsDefault) {
1052-
this.curParsingMode = k.OBCIParsingEOT;
1053-
return this.write(k.OBCIChannelMaxNumber8);
1054-
} else if (numberOfChannels === k.OBCINumberOfChannelsDaisy) {
1055-
this.curParsingMode = k.OBCIParsingEOT;
1056-
return this.write(k.OBCIChannelMaxNumber16);
1057-
} else {
1058-
return Promise.reject('invalid number of channels');
1059-
}
1075+
OpenBCIBoard.prototype.hardSetBoardType = function (boardType) {
1076+
if (this.isStreaming()) return Promise.reject('Must not be streaming!');
1077+
return new Promise((resolve, reject) => {
1078+
const eotFunc = (data) => {
1079+
switch (data.slice(0, data.length - k.OBCIParseEOT.length).toString()) {
1080+
case k.OBCIChannelMaxNumber8NoDaisyToRemove:
1081+
this.overrideInfoForBoardType(k.OBCIBoardDefault);
1082+
resolve('no daisy to remove');
1083+
break;
1084+
case k.OBCIChannelMaxNumber8SuccessDaisyRemoved:
1085+
this.overrideInfoForBoardType(k.OBCIBoardDefault);
1086+
resolve('daisy removed');
1087+
break;
1088+
case k.OBCIChannelMaxNumber16DaisyAlreadyAttached:
1089+
this.overrideInfoForBoardType(k.OBCIBoardDaisy);
1090+
resolve('daisy already attached');
1091+
break;
1092+
case k.OBCIChannelMaxNumber16DaisyAttached:
1093+
this.overrideInfoForBoardType(k.OBCIBoardDaisy);
1094+
resolve('daisy attached');
1095+
break;
1096+
case k.OBCIChannelMaxNumber16NoDaisyAttached:
1097+
this.overrideInfoForBoardType(k.OBCIBoardDefault);
1098+
reject('unable to attach daisy');
1099+
break;
1100+
default:
1101+
reject(Error('invalid return, board may not be configured correctly.'));
1102+
break;
1103+
}
1104+
};
1105+
if (boardType === k.OBCIBoardDefault) {
1106+
this.curParsingMode = k.OBCIParsingEOT;
1107+
this.once(k.OBCIEmitterEot, eotFunc);
1108+
this.write(k.OBCIChannelMaxNumber8)
1109+
.catch((err) => {
1110+
this.removeListener(k.OBCIEmitterEot, eotFunc);
1111+
reject(err);
1112+
});
1113+
} else if (boardType === k.OBCIBoardDaisy) {
1114+
this.curParsingMode = k.OBCIParsingEOT;
1115+
this.once(k.OBCIEmitterEot, eotFunc);
1116+
this.write(k.OBCIChannelMaxNumber16)
1117+
.catch((err) => {
1118+
this.removeListener(k.OBCIEmitterEot, eotFunc);
1119+
reject(err);
1120+
});
1121+
} else {
1122+
reject('invalid board type');
1123+
}
1124+
});
10601125
};
10611126

10621127
/**
@@ -1854,9 +1919,9 @@ function OpenBCIFactory () {
18541919
*/
18551920
OpenBCIBoard.prototype._processParseBufferForReset = function (dataBuffer) {
18561921
if (openBCISample.countADSPresent(dataBuffer) === 2) {
1857-
this.setInfoForBoardType(k.OBCIBoardDaisy);
1922+
this.overrideInfoForBoardType(k.OBCIBoardDaisy);
18581923
} else {
1859-
this.setInfoForBoardType(k.OBCIBoardDefault);
1924+
this.overrideInfoForBoardType(k.OBCIBoardDefault);
18601925
}
18611926

18621927
if (openBCISample.findV2Firmware(dataBuffer)) {

openBCIConstants.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ const obciMiscSoftReset = 'v';
156156
/** 16 Channel Commands */
157157
const obciChannelMaxNumber8 = 'c';
158158
const obciChannelMaxNumber16 = 'C';
159+
const obciChannelMaxNumber8NoDaisyToRemove = '';
160+
const obciChannelMaxNumber8SuccessDaisyRemoved = 'daisy removed';
161+
const obciChannelMaxNumber16DaisyAlreadyAttached = '16';
162+
const obciChannelMaxNumber16DaisyAttached = 'daisy attached16';
163+
const obciChannelMaxNumber16NoDaisyAttached = 'no daisy to attach!8';
159164

160165
/** 60Hz line filter */
161166
const obciFilterDisable = 'g';
@@ -335,6 +340,7 @@ const obciRadioBaudRateFastStr = 'fast';
335340
/** Emitters */
336341
const obciEmitterClose = 'close';
337342
const obciEmitterDroppedPacket = 'droppedPacket';
343+
const obciEmitterEot = 'eot';
338344
const obciEmitterError = 'error';
339345
const obciEmitterImpedanceArray = 'impedanceArray';
340346
const obciEmitterQuery = 'query';
@@ -724,6 +730,11 @@ module.exports = {
724730
/** 16 Channel Commands */
725731
OBCIChannelMaxNumber8: obciChannelMaxNumber8,
726732
OBCIChannelMaxNumber16: obciChannelMaxNumber16,
733+
OBCIChannelMaxNumber8NoDaisyToRemove: obciChannelMaxNumber8NoDaisyToRemove,
734+
OBCIChannelMaxNumber8SuccessDaisyRemoved: obciChannelMaxNumber8SuccessDaisyRemoved,
735+
OBCIChannelMaxNumber16DaisyAlreadyAttached: obciChannelMaxNumber16DaisyAlreadyAttached,
736+
OBCIChannelMaxNumber16DaisyAttached: obciChannelMaxNumber16DaisyAttached,
737+
OBCIChannelMaxNumber16NoDaisyAttached: obciChannelMaxNumber16NoDaisyAttached,
727738
/** Filters */
728739
OBCIFilterDisable: obciFilterDisable,
729740
OBCIFilterEnable: obciFilterEnable,
@@ -894,6 +905,7 @@ module.exports = {
894905
/** Emitters */
895906
OBCIEmitterClose: obciEmitterClose,
896907
OBCIEmitterDroppedPacket: obciEmitterDroppedPacket,
908+
OBCIEmitterEot: obciEmitterEot,
897909
OBCIEmitterError: obciEmitterError,
898910
OBCIEmitterImpedanceArray: obciEmitterImpedanceArray,
899911
OBCIEmitterQuery: obciEmitterQuery,

openBCISample.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1119,7 +1119,7 @@ function isSuccessInBuffer (dataBuffer) {
11191119
*/
11201120
function stripToEOTBuffer (dataBuffer) {
11211121
let indexOfEOT = dataBuffer.indexOf(k.OBCIParseEOT);
1122-
if (indexOfEOT > 0) {
1122+
if (indexOfEOT >= 0) {
11231123
indexOfEOT += k.OBCIParseEOT.length;
11241124
} else {
11251125
return dataBuffer;

0 commit comments

Comments
 (0)