@neurosity/sdk
Version:
Neurosity SDK
72 lines (71 loc) • 4.16 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.epoch = exports.bufferToEpoch = exports.addInfo = void 0;
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const defaultDataProp = "data";
const defaultSamplingRate = 256;
const isObject = (object) => object instanceof Object && object === Object(object);
const isFunction = (object) => typeof object === "function";
const patch = (sample) => (info) => {
var _a;
return (Object.assign(Object.assign({}, sample), { info: Object.assign(Object.assign({}, ((_a = sample === null || sample === void 0 ? void 0 : sample.info) !== null && _a !== void 0 ? _a : {})), (info || {})) }));
};
/**
* Annotates stream with user-defined metadata
* @method addInfo
* @example eeg$.pipe(addinfo({ samplingRate: 256, channelNames: ["Af7", "Fp1", "Fp2", "Af8"] })
* @param {Object} info Info to be added to the EEG stream. Relevant info may include: `samplingRate` and `channelNames`
* @returns {Observable<Sample|Epoch|PSD>}
*/
const addInfo = (infoValue) => (0, rxjs_1.pipe)((0, operators_1.map)((sample) => {
if (!isObject(sample) ||
(!isObject(infoValue) && !isFunction(infoValue))) {
return sample;
}
const info = isFunction(infoValue) ? infoValue(sample) : infoValue;
return patch(sample)(info);
}));
exports.addInfo = addInfo;
/**
* Get a 2D data array organized by channel from an array of Samples. Credit to Ken from Seattle's elegant transposition
* http://www.codesuck.com/2012/02/transpose-javascript-array-in-one-line.html
* @method groupByChannel
* @param {Array<Sample>} samplesBuffer Array of Samples to be grouped
* @param {string} [dataProp] Name of the key associated with EEG data
* @returns {Array<Array<number>>}
*/
const groupByChannel = (samplesBuffer, dataProp = defaultDataProp) => samplesBuffer[0][dataProp].map((_, channelIndex) => samplesBuffer.map((sample) => sample[dataProp][channelIndex]));
/**
* Takes an array or RxJS buffer of EEG Samples and returns an Epoch.
* @method bufferToEpoch
* @example eeg$.pipe(bufferTime(1000), bufferToEpoch({ samplingRate: 256 }))
*
* @param {Object} options - Data structure options
* @param {number} [options.samplingRate] Sampling rate
* @param {string} [options.dataProp='data'] Name of the key associated with eeg data
*
* @returns {Observable<Epoch>}
*/
const bufferToEpoch = ({ samplingRate = defaultSamplingRate, dataProp = defaultDataProp } = {}) => (0, rxjs_1.pipe)((0, operators_1.map)((samplesArray) => ({
[dataProp]: groupByChannel(samplesArray, dataProp),
info: Object.assign(Object.assign({}, (samplesArray[0] && samplesArray[0].info
? samplesArray[0].info
: {})), { startTime: samplesArray[0].timestamp, samplingRate: samplesArray[0].info && samplesArray[0].info.samplingRate
? samplesArray[0].info.samplingRate
: samplingRate })
})));
exports.bufferToEpoch = bufferToEpoch;
/**
* Converts a stream of individual Samples of EEG data into a stream of Epochs of a given duration emitted at specified interval. This operator functions similarly to a circular buffer internally and allows overlapping Epochs of data to be emitted (e.g. emitting the last one second of data every 100ms).
* @method epoch
* @example eeg$.pipe(epoch({ duration: 1024, interval: 100, samplingRate: 256 }))
* @param {Object} options - Epoching options
* @param {number} [options.duration=256] Number of samples to include in each epoch
* @param {number} [options.interval=100] Time (ms) between emitted Epochs
* @param {number} [options.samplingRate=256] Sampling rate
* @param {string} [options.dataProp='data'] Name of the key associated with eeg data
* @returns {Observable} Epoch
*/
const epoch = ({ duration, interval, samplingRate, dataProp = defaultDataProp }) => (0, rxjs_1.pipe)((0, operators_1.bufferCount)(interval), (0, operators_1.scan)((acc, val) => acc.concat(val).slice(acc.length < duration ? 0 : -duration)), (0, operators_1.filter)((samplesArray) => samplesArray.length === duration), (0, exports.bufferToEpoch)({ samplingRate, dataProp }));
exports.epoch = epoch;