Permalink
Browse files

Add: max channel support for simulator

  • Loading branch information...
1 parent 2d7547c commit 0dd0eceb466ccfe3f1da4ae55276ea3bfb33687f @aj-ptw aj-ptw committed Jan 3, 2017
Showing with 378 additions and 115 deletions.
  1. +1 −1 README.md
  2. +14 −2 changelog.md
  3. +154 −89 openBCIBoard.js
  4. +12 −0 openBCIConstants.js
  5. +1 −1 openBCISample.js
  6. +25 −0 openBCISimulator.js
  7. +1 −1 package.json
  8. +10 −0 test/OpenBCIConstants-test.js
  9. +15 −0 test/OpenBCISample-test.js
  10. +86 −20 test/openBCIBoard-test.js
  11. +59 −1 test/openBCISimulator-test.js
View
@@ -847,7 +847,7 @@ Stop logging to the SD card and close any open file. If you are not streaming wh
**_Returns_** resolves if the command was added to the write queue.
-### <a name="method-set-info-for-board-type"></a> .setInfoForBoardType(boardType)
+### <a name="method-set-info-for-board-type"></a> .overrideInfoForBoardType(boardType)
Set the info property for board type.
View
@@ -1,9 +1,21 @@
+# 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.
+
+### 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.
+
+### Breaking changes
+* `.overrideInfoForBoardType()` changed to `.overrideInfoForBoardType()` to elevate it's dangerous nature.
+
# 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 `.setInfoForBoardType()` see readme.md
-* Get info for the core obhect that drives the module with `.getInfo()` 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.
View
@@ -23,6 +23,7 @@ function OpenBCIFactory () {
simulate: false,
simulatorBoardFailure: false,
simulatorDaisyModuleAttached: false,
+ simulatorDaisyModuleCanBeAttached: true,
simulatorFirmwareVersion: [k.OBCIFirmwareV1, k.OBCIFirmwareV2],
simulatorFragmentation: [k.OBCISimulatorFragmentationNone, k.OBCISimulatorFragmentationRandom, k.OBCISimulatorFragmentationFullBuffers, k.OBCISimulatorFragmentationOneByOne],
simulatorLatencyTime: 16,
@@ -41,80 +42,84 @@ function OpenBCIFactory () {
};
/**
- * @description The initialization method to call first, before any other method.
- * @param options (optional) - Board optional configurations.
- * - `baudRate` {Number} - Baud Rate, defaults to 115200. Manipulating this is allowed if
- * firmware on board has been previously configured.
- *
- * - `boardType` {String} - Specifies type of OpenBCI board.
- * 3 Possible Boards:
- * `default` - 8 Channel OpenBCI board (Default)
- * `daisy` - 8 Channel OpenBCI board with Daisy Module. Total of 16 channels.
- * `ganglion` - 4 Channel board
- * (NOTE: THIS IS IN-OP TIL RELEASE OF GANGLION BOARD 07/2016)
- *
- * - `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 simulator to be started with 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 the latency timer has expired
- * `oneByOne` - output each byte separately
- *
- * - `simulatorLatencyTime` {Number} - The time in milliseconds to wait before sending partially full buffers,
- 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.
- *
- * - `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 experiements on your local
- * computer, keep this `false`. (Default `false`)
- *
- * - `sntpTimeSyncHost` - {String} The ntp server to use, can be either sntp or ntp. (Defaults `pool.ntp.org`).
- *
- * - `sntpTimeSyncPort` - {Number} The port to access the ntp 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`)
- *
- * @constructor
- * @author AJ Keller (@pushtheworldllc)
- */
+ * @description The initialization method to call first, before any other method.
+ * @param options (optional) - Board optional configurations.
+ * - `baudRate` {Number} - Baud Rate, defaults to 115200. Manipulating this is allowed if
+ * firmware on board has been previously configured.
+ *
+ * - `boardType` {String} - Specifies type of OpenBCI board.
+ * 3 Possible Boards:
+ * `default` - 8 Channel OpenBCI board (Default)
+ * `daisy` - 8 Channel OpenBCI board with Daisy Module. Total of 16 channels.
+ * `ganglion` - 4 Channel board
+ * (NOTE: THIS IS IN-OP TIL RELEASE OF GANGLION BOARD 07/2016)
+ *
+ * - `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`)
+ *
+ * - `simulatorDaisyModuleCanBeAttached` {Boolean} - Allows the simulation of the a hot swapped daisy board.
+ * For example: You coule simulate if the board has only detected 8 channels and the user requested
+ * 16 channels. (Default `true`)
+ *
+ * - `simulatorFirmwareVersion` {String} - Allows simulator to be started with 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 the latency timer has expired
+ * `oneByOne` - output each byte separately
+ *
+ * - `simulatorLatencyTime` {Number} - The time in milliseconds to wait before sending partially full buffers,
+ * 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.
+ *
+ * - `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 experiements on your local
+ * computer, keep this `false`. (Default `false`)
+ *
+ * - `sntpTimeSyncHost` - {String} The ntp server to use, can be either sntp or ntp. (Defaults `pool.ntp.org`).
+ *
+ * - `sntpTimeSyncPort` - {Number} The port to access the ntp 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`)
+ *
+ * @constructor
+ * @author AJ Keller (@pushtheworldllc)
+ */
function OpenBCIBoard (options) {
options = (typeof options !== 'function') && options || {};
var opts = {};
@@ -206,6 +211,7 @@ function OpenBCIFactory () {
this.timeOfPacketArrival = 0;
this.writeOutDelay = k.OBCIWriteIntervalDelayMSShort;
// Strings
+ this.portName = null;
// NTP
if (this.options.sntpTimeSync) {
@@ -254,6 +260,7 @@ function OpenBCIFactory () {
alpha: this.options.simulatorInjectAlpha,
boardFailure: this.options.simulatorBoardFailure,
daisy: this.options.simulatorDaisyModuleAttached,
+ daisyCanBeAttached: this.options.simulatorDaisyModuleCanBeAttached,
drift: this.options.simulatorInternalClockDrift,
firmwareVersion: this.options.simulatorFirmwareVersion,
fragmentation: this.options.simulatorFragmentation,
@@ -378,14 +385,24 @@ function OpenBCIFactory () {
return this.serial.isOpen();
};
+
/**
* @description Checks if the board is currently sending samples.
* @returns {boolean} - True if streaming.
*/
+ OpenBCIBoard.prototype.isSimulating = function () {
+ return this.options.simulate;
+ };
+
+ /**
+ * @description Checks if the board is currently sending samples.
+ * @returns {boolean} - True if streaming.
+ */
OpenBCIBoard.prototype.isStreaming = function () {
return this._streaming;
};
+
/**
* @description Sends a start streaming command to the board.
* @returns {Promise} indicating if the signal was able to be sent.
@@ -1013,6 +1030,14 @@ function OpenBCIFactory () {
};
/**
+ * Get the board type.
+ * @return boardType: string
+ */
+ OpenBCIBoard.prototype.getBoardType = function() {
+ return this.info.boardType;
+ };
+
+ /**
* Get the core info object.
* @return {{boardType: string, sampleRate: number, firmware: string, numberOfChannels: number, missedPackets: number}}
*/
@@ -1025,7 +1050,7 @@ function OpenBCIFactory () {
* @param boardType {String}
* `default` or `daisy`. Defaults to `default`.
*/
- OpenBCIBoard.prototype.setInfoForBoardType = function(boardType) {
+ OpenBCIBoard.prototype.overrideInfoForBoardType = function(boardType) {
switch (boardType) {
case k.OBCIBoardDaisy:
this.info.boardType = k.OBCIBoardDaisy;
@@ -1043,20 +1068,60 @@ function OpenBCIFactory () {
/**
* Used to set the max number of channels on the Cyton board.
- * @param numberOfChannels {number}
- * Either 8 or 16
+ * @param boardType {String}
+ * Either `default` or `daisy`
* @return {Promise}
*/
- OpenBCIBoard.prototype.setMaxChannels = function (numberOfChannels) {
- if (numberOfChannels === k.OBCINumberOfChannelsDefault) {
- this.curParsingMode = k.OBCIParsingEOT;
- return this.write(k.OBCIChannelMaxNumber8);
- } else if (numberOfChannels === k.OBCINumberOfChannelsDaisy) {
- this.curParsingMode = k.OBCIParsingEOT;
- return this.write(k.OBCIChannelMaxNumber16);
- } else {
- return Promise.reject('invalid number of channels');
- }
+ OpenBCIBoard.prototype.hardSetBoardType = function (boardType) {
+ if (this.isStreaming()) return Promise.reject('Must not be streaming!');
+ return new Promise((resolve, reject) => {
+ const eotFunc = (data) => {
+ switch (data.slice(0, data.length - k.OBCIParseEOT.length).toString()) {
+ case k.OBCIChannelMaxNumber8NoDaisyToRemove:
+ this.overrideInfoForBoardType(k.OBCIBoardDefault);
+ resolve('no daisy to remove');
+ break;
+ case k.OBCIChannelMaxNumber8SuccessDaisyRemoved:
+ this.overrideInfoForBoardType(k.OBCIBoardDefault);
+ resolve('daisy removed');
+ break;
+ case k.OBCIChannelMaxNumber16DaisyAlreadyAttached:
+ this.overrideInfoForBoardType(k.OBCIBoardDaisy);
+ resolve('daisy already attached');
+ break;
+ case k.OBCIChannelMaxNumber16DaisyAttached:
+ this.overrideInfoForBoardType(k.OBCIBoardDaisy);
+ resolve('daisy attached');
+ break;
+ case k.OBCIChannelMaxNumber16NoDaisyAttached:
+ this.overrideInfoForBoardType(k.OBCIBoardDefault);
+ reject('unable to attach daisy');
+ break;
+ default:
+ reject(Error('invalid return, board may not be configured correctly.'));
+ break;
+ }
+ };
+ if (boardType === k.OBCIBoardDefault) {
+ this.curParsingMode = k.OBCIParsingEOT;
+ this.once(k.OBCIEmitterEot, eotFunc);
+ this.write(k.OBCIChannelMaxNumber8)
+ .catch((err) => {
+ this.removeListener(k.OBCIEmitterEot, eotFunc);
+ reject(err);
+ });
+ } else if (boardType === k.OBCIBoardDaisy) {
+ this.curParsingMode = k.OBCIParsingEOT;
+ this.once(k.OBCIEmitterEot, eotFunc);
+ this.write(k.OBCIChannelMaxNumber16)
+ .catch((err) => {
+ this.removeListener(k.OBCIEmitterEot, eotFunc);
+ reject(err);
+ });
+ } else {
+ reject('invalid board type');
+ }
+ });
};
/**
@@ -1854,9 +1919,9 @@ function OpenBCIFactory () {
*/
OpenBCIBoard.prototype._processParseBufferForReset = function (dataBuffer) {
if (openBCISample.countADSPresent(dataBuffer) === 2) {
- this.setInfoForBoardType(k.OBCIBoardDaisy);
+ this.overrideInfoForBoardType(k.OBCIBoardDaisy);
} else {
- this.setInfoForBoardType(k.OBCIBoardDefault);
+ this.overrideInfoForBoardType(k.OBCIBoardDefault);
}
if (openBCISample.findV2Firmware(dataBuffer)) {
View
@@ -156,6 +156,11 @@ const obciMiscSoftReset = 'v';
/** 16 Channel Commands */
const obciChannelMaxNumber8 = 'c';
const obciChannelMaxNumber16 = 'C';
+const obciChannelMaxNumber8NoDaisyToRemove = '';
+const obciChannelMaxNumber8SuccessDaisyRemoved = 'daisy removed';
+const obciChannelMaxNumber16DaisyAlreadyAttached = '16';
+const obciChannelMaxNumber16DaisyAttached = 'daisy attached16';
+const obciChannelMaxNumber16NoDaisyAttached = 'no daisy to attach!8';
/** 60Hz line filter */
const obciFilterDisable = 'g';
@@ -335,6 +340,7 @@ const obciRadioBaudRateFastStr = 'fast';
/** Emitters */
const obciEmitterClose = 'close';
const obciEmitterDroppedPacket = 'droppedPacket';
+const obciEmitterEot = 'eot';
const obciEmitterError = 'error';
const obciEmitterImpedanceArray = 'impedanceArray';
const obciEmitterQuery = 'query';
@@ -724,6 +730,11 @@ module.exports = {
/** 16 Channel Commands */
OBCIChannelMaxNumber8: obciChannelMaxNumber8,
OBCIChannelMaxNumber16: obciChannelMaxNumber16,
+ OBCIChannelMaxNumber8NoDaisyToRemove: obciChannelMaxNumber8NoDaisyToRemove,
+ OBCIChannelMaxNumber8SuccessDaisyRemoved: obciChannelMaxNumber8SuccessDaisyRemoved,
+ OBCIChannelMaxNumber16DaisyAlreadyAttached: obciChannelMaxNumber16DaisyAlreadyAttached,
+ OBCIChannelMaxNumber16DaisyAttached: obciChannelMaxNumber16DaisyAttached,
+ OBCIChannelMaxNumber16NoDaisyAttached: obciChannelMaxNumber16NoDaisyAttached,
/** Filters */
OBCIFilterDisable: obciFilterDisable,
OBCIFilterEnable: obciFilterEnable,
@@ -894,6 +905,7 @@ module.exports = {
/** Emitters */
OBCIEmitterClose: obciEmitterClose,
OBCIEmitterDroppedPacket: obciEmitterDroppedPacket,
+ OBCIEmitterEot: obciEmitterEot,
OBCIEmitterError: obciEmitterError,
OBCIEmitterImpedanceArray: obciEmitterImpedanceArray,
OBCIEmitterQuery: obciEmitterQuery,
View
@@ -1119,7 +1119,7 @@ function isSuccessInBuffer (dataBuffer) {
*/
function stripToEOTBuffer (dataBuffer) {
let indexOfEOT = dataBuffer.indexOf(k.OBCIParseEOT);
- if (indexOfEOT > 0) {
+ if (indexOfEOT >= 0) {
indexOfEOT += k.OBCIParseEOT.length;
} else {
return dataBuffer;
Oops, something went wrong.

0 comments on commit 0dd0ece

Please sign in to comment.