//OTHER devices follow a general format: /* connect -> init device and scripts disconnect -> close device connection onconnect -> onconnect callback you can customize ondata -> ondata callback you can customize ondisconnect -> disconnect callback you can customize codec -> optionally used to transform streams e.g. on a separate thread, libraries like muse-js already do it for us so we can just customize ondata to handle output, or use the codec to do some kind of special math on a thread */ import { WebglLinePlotProps } from "webgl-plot-utils"; import { FilterSettings } from "../../util/BiquadFilters"; import { ByteParser } from "../../util/ByteParser"; import {MuseClient} from './dependencies/muse.esm' const sps = 250; export const museSettings = { //include muse-js and import {MuseClient} from 'muse-js' for this to work sps, //base eeg sps, accelerometer is something else I think, I dunno deviceType:'USB_CUSTOM', deviceName:'muse', connect:(settings:any={}) => { return new Promise(async (res,rej) => { let _id = `muse${Math.floor(Math.random()*1000000000000000)}`; //if(typeof MuseClient === 'undefined') { document.head.insertAdjacentHTML('beforeend',``) } let client = new MuseClient(); let info = { _id, client, settings:Object.assign(Object.assign({},museSettings),settings) //e.g. customize ondisconnect } client.enableAux = true; await client.connect(); await client.start(); let eegts; client.eegReadings.subscribe((reading:{ index: number; electrode: number; // 0 to 4 timestamp: number; // milliseconds since epoch samples: number[]; // 12 samples each time }) => { (reading as any).origin = 'eeg'; if(reading.electrode === 0) { eegts = ByteParser.genTimestamps(12,250); } if(!eegts) eegts = ByteParser.genTimestamps(12,250); reading.timestamp = eegts; //sync timestamps across samples info.settings.ondata(reading); }); client.telemetryData.subscribe((reading:{ sequenceId: number; batteryLevel: number; fuelGaugeVoltage: number; temperature: number; }) => { (reading as any).origin = 'telemetry'; info.settings.ondata(reading); }); client.gyroscopeData.subscribe((reading:{ sequenceId: number; samples: {x:number,y:number,z:number}[]; }) => { (reading as any).origin = 'gyro'; info.settings.ondata(reading); }) client.accelerometerData.subscribe((reading:{ sequenceId: number; samples: {x:number,y:number,z:number}[]; }) => { (reading as any).origin = 'accelerometer'; info.settings.ondata(reading); }); if(client.enablePPG) { client.ppgData.subscribe((reading:{ index: number; ppgChannel: number; // 0 to 2 timestamp: number; // milliseconds since epoch samples: number[]; // 6 samples each time }) => { (reading as any).origin = 'ppg'; info.settings.ondata(reading); }); } if(info.settings.onconnect) info.settings.onconnect(info); res(info); }) }, codec:(reading:any) => { //remap outputs to more or less match the rest of our formatting let origin = reading.origin; if(origin === 'eeg') { return { [reading.electrode]:reading.samples, timestamp:Date.now() } } else if (origin === 'gyro') { let transformed = {gx:[] as any,gy:[] as any,gz:[] as any, timestamp:Date.now()}; reading.samples.forEach((s:any) => { transformed.gx.push(s.x); transformed.gy.push(s.y); transformed.gz.push(s.z); }); return transformed; } else if (origin === 'accelerometer') { let transformed = {ax:[] as any,ay:[] as any,az:[] as any, timestamp:Date.now()}; reading.samples.forEach((s:any) => { transformed.ax.push(s.x); transformed.ay.push(s.y); transformed.az.push(s.z); }); return transformed; } else if (origin === 'ppg') { return { [`ppg${reading.ppgChannel}`]:reading.samples, timestamp:Date.now() }; } else if (origin === 'telemetry') { return reading; } }, disconnect:(info) => { info.client.disconnect(); }, onconnect:(info)=>{ console.log('muse connected!', info); }, beforedisconnect:(info) => {}, ondisconnect:(info)=>{ console.log('muse disconnected!', info); }, ondata:(data:any)=>{ console.log(data); //direct from teh device output }, //read:(info:any,command?:any)=>{}, //write:(info:any,command?:any)=>{} } let defaultsetting = { sps, useDCBlock:true, useBandpass:true, bandpassLower:3, bandpassUpper:45 }; export const museFilterSettings:{[key:string]:FilterSettings} = { '0':JSON.parse(JSON.stringify(defaultsetting)), //twos compliment 2^23 '1':JSON.parse(JSON.stringify(defaultsetting)), '2':JSON.parse(JSON.stringify(defaultsetting)), '3':JSON.parse(JSON.stringify(defaultsetting)), '4':JSON.parse(JSON.stringify(defaultsetting)) } export const museChartSettings:Partial = { lines:{ '0':{nSec:10, sps, units:'uV'}, '1':{nSec:10, sps, units:'uV'}, '2':{nSec:10, sps, units:'uV'}, '3':{nSec:10, sps, units:'uV'}, '4':{nSec:10, sps, units:'uV'}, 'ax':{nSec:10, sps:100, units:'mg'}, 'ay':{nSec:10, sps:100, units:'mg'}, 'az':{nSec:10, sps:100, units:'mg'}, 'gx':{nSec:10, sps:100, units:'rps'}, 'gy':{nSec:10, sps:100, units:'rps'}, 'gz':{nSec:10, sps:100, units:'rps'}, }, generateNewLines:true //to add the additional 16 channels };