/** * Handler function for raw event emitter. * Converts EEG events into lines of CSV and writes data to the rawLogFile */ internal void OnRawEEGData(EEGEvent evt) { var csv = new StringBuilder(); csv.Append(evt.timestamp.ToString("o")); csv.Append(","); csv.Append(evt.type.ToString()); if (evt.extra != null) { csv.Append(evt.extra.ToString()); } csv.Append(","); for (int i = 0; i < evt.data.Length; i++) { csv.Append(","); csv.Append(evt.data[i].ToString()); } if (file == null) { file = new AsyncStreamWriter(rawLogFile, true); } var writableCsv = csv.ToString(); file.WriteLine(writableCsv); }
protected override bool Process(object item) { EEGEvent evt = (EEGEvent)item; self.EmitRawEvent(evt); return(true); }
/** * Puts EEGEvents into the eventQueue to wait; events are flushed and handlers operate on the events */ protected void EmitData(EEGEvent evt) { //Logger.Log("EmitData type=" + evt.type); lock (eventQueue) { //Logger.Log("EmitData lock obtained"); eventQueue.Enqueue(evt); //Logger.Log("EmitData lock released"); } }
protected override bool Process(object item) { EEGEvent evt = (EEGEvent)item; var n = evt.data.Length; for (int i = 0; i < n; i++) { buffer[i] = signalFilters[i].Filter(evt.data[i]); } Add(new EEGEvent(evt.timestamp, evt.type, buffer, evt.extra)); return(true); }
/** * Thread safe function that removes events from the event queue and calls FlushEvent (without a s) on each event to send data through pipelines */ public void FlushEvents() { //Logger.Log("FlushEvents()"); lock (eventQueue) { //Logger.Log("FlushEvents lock obtained"); while (eventQueue.Count > 0) { EEGEvent evt = eventQueue.Dequeue(); FlushEvent(evt); } //Logger.Log("FlushEvents lock released"); } }
protected override bool Process(object item) { EEGEvent evt = (EEGEvent)item; for (int i = 0; i < detectors.Length; i++) { if (detectors[i].Detect(evt.data[i])) { return(true); } } Add(evt); return(true); }
/** * Runs the handler functions for each type of event on the events being flushed by the FlushEvents (with a s) in the public function */ private void FlushEvent(EEGEvent evt) { if (handlers.ContainsKey(evt.type)) { //Debug.Log("FlushEvent type=" + evt.type); List <DataHandler> h = handlers[evt.type]; foreach (DataHandler dh in h) { //try { dh(evt); //} catch (Exception e) { // Logger.Error(e); //} } } }
protected override bool Process(object item) { EEGEvent evt = (EEGEvent)item; if (evt.type != EEGDataType.EEG) { throw new Exception("FFTPipeable recieved invalid EEGEvent: " + evt); } if (evt.data.Length != channels) { throw new Exception("FFTPipeable recieved malformed EEGEvent: " + evt); } // normal case: just append data to sample buffer for (int i = 0; i < channels; i++) { var v = evt.data[i]; //v = signalFilters[i].Filter(v); samples[i].Enqueue(v); } nSamples++; if (nSamples > windowSize) { foreach (var channelSamples in samples) { channelSamples.Dequeue(); //channelSamples.Dequeue(); } nSamples--; } lastFFT++; //Logger.Log("nSamples={0}, lastFFT={1}", nSamples, lastFFT); // sample buffer is full, do FFT then reset for next round if (nSamples >= windowSize && lastFFT % fftRate == 0) { DoFFT(evt); } return(true); }
/** * Thread safe helper function for running raw event handlers on each raw event */ internal void EmitRawEvent(EEGEvent evt) { lock (rawHandlers) { if (!rawHandlers.ContainsKey(evt.type)) { return; } // Logger.Log("Emitting evt: " + evt.type); foreach (var handler in rawHandlers[evt.type]) { try { handler(evt); } catch (Exception e) { Logger.Error("Handler " + handler + " encountered exception: " + e); } } } }
/** * Collects incoming EEGEvents (one per type), and stores them in buffer. If the incoming * event's timestamp is new, attempt to send the buffer onto the IPredictor<EEGEvent[]> * If the current trainingId is non-zero, the data will be used for training on the trainingId label * If the current trainingId is zero, the data will be predicted on * @see Pipeable */ protected override bool Process(object item) { //Incoming objects must be EEGEvent EEGEvent evt = (EEGEvent)item; //Also must be a type we care about if (!types.Contains(evt.type)) { return(true); } //If we start getting data from a new time, send it to the predictor if (evt.timestamp != currentTimeStep) { CheckBufferAndPredict(); currentTimeStep = evt.timestamp; } //Add the new event into the buffer buffer[indexMap[evt.type]] = evt; return(true); }
void DoFFT(EEGEvent evt) { // Do an FFT on each channel List <double[]> fftOutput = new List <double[]>(); for (int i = 0; i < samples.Length; i++) { var channelSamples = samples[i]; var samplesCopy = channelSamples.ToArray(); // apply windowing function to samplesCopy DSP.Math.Multiply(samplesCopy, windowConstants); var cSpectrum = fft.Execute(samplesCopy); // complex side smoothing //cSpectrum = complexFilters[i].Smooth(cSpectrum); double[] lmSpectrum = DSP.ConvertComplex.ToMagnitude(cSpectrum); lmSpectrum = DSP.Math.Multiply(lmSpectrum, scaleFactor); fftOutput.Add(lmSpectrum); } for (int i = 0; i < fftOutput.Count; i++) { var rawFFT = fftOutput[i]; Add(new EEGEvent(evt.timestamp, EEGDataType.FFT_RAW, rawFFT, i)); // magnitude side smoothing var smoothedFFT = magSmoothers[i].Smooth(rawFFT); Add(new EEGEvent(evt.timestamp, EEGDataType.FFT_SMOOTHED, smoothedFFT, i)); } //var freqSpan = fft.FrequencySpan(sampleRate); // find abs powers for each band var absolutePowers = new Dictionary <EEGDataType, double[]>(); for (int i = 0; i < channels; i++) { var bins = fftOutput[i]; double deltaAbs = AbsBandPower(bins, 1, 4); double thetaAbs = AbsBandPower(bins, 4, 8); double alphaAbs = AbsBandPower(bins, 7.5, 13); double betaAbs = AbsBandPower(bins, 13, 30); double gammaAbs = AbsBandPower(bins, 30, 44); //Logger.Log("D={0}, T={1}, A={2}, B={3}, G={4}", deltaAbs, thetaAbs, alphaAbs, betaAbs, gammaAbs); GetBandList(absolutePowers, EEGDataType.ALPHA_ABSOLUTE)[i] = (alphaAbs); GetBandList(absolutePowers, EEGDataType.BETA_ABSOLUTE)[i] = (betaAbs); GetBandList(absolutePowers, EEGDataType.GAMMA_ABSOLUTE)[i] = (gammaAbs); GetBandList(absolutePowers, EEGDataType.DELTA_ABSOLUTE)[i] = (deltaAbs); GetBandList(absolutePowers, EEGDataType.THETA_ABSOLUTE)[i] = (thetaAbs); } // we can emit abs powers immediately Add(new EEGEvent(evt.timestamp, EEGDataType.ALPHA_ABSOLUTE, absolutePowers[EEGDataType.ALPHA_ABSOLUTE].ToArray())); Add(new EEGEvent(evt.timestamp, EEGDataType.BETA_ABSOLUTE, absolutePowers[EEGDataType.BETA_ABSOLUTE].ToArray())); Add(new EEGEvent(evt.timestamp, EEGDataType.GAMMA_ABSOLUTE, absolutePowers[EEGDataType.GAMMA_ABSOLUTE].ToArray())); Add(new EEGEvent(evt.timestamp, EEGDataType.DELTA_ABSOLUTE, absolutePowers[EEGDataType.DELTA_ABSOLUTE].ToArray())); Add(new EEGEvent(evt.timestamp, EEGDataType.THETA_ABSOLUTE, absolutePowers[EEGDataType.THETA_ABSOLUTE].ToArray())); // now calc and emit relative powers Add(new EEGEvent(evt.timestamp, EEGDataType.ALPHA_RELATIVE, RelBandPower(absolutePowers, EEGDataType.ALPHA_ABSOLUTE))); Add(new EEGEvent(evt.timestamp, EEGDataType.BETA_RELATIVE, RelBandPower(absolutePowers, EEGDataType.BETA_ABSOLUTE))); Add(new EEGEvent(evt.timestamp, EEGDataType.GAMMA_RELATIVE, RelBandPower(absolutePowers, EEGDataType.GAMMA_ABSOLUTE))); Add(new EEGEvent(evt.timestamp, EEGDataType.DELTA_RELATIVE, RelBandPower(absolutePowers, EEGDataType.DELTA_ABSOLUTE))); Add(new EEGEvent(evt.timestamp, EEGDataType.THETA_RELATIVE, RelBandPower(absolutePowers, EEGDataType.THETA_ABSOLUTE))); }
void UpdateConnectionStatus(EEGEvent evt) { _connectionStatus = evt.data; }
protected override bool Process(object item) { EEGEvent evt = (EEGEvent)item; var n = evt.data.Length; double[] buffer = new double[evt.data.Length]; for (int i = 0; i < n; i++) { buffer[i] = signalFilters[i].Filter(evt.data[i]); } // TODO test artifact detection if (isLearning) { // add samples to window and test if window is sufficiently large bool stillLearning = false; for (int i = 0; i < n; i++) { artifactLearningSamples[i].Enqueue(evt.data[i]); stillLearning = artifactLearningSamples[i].Count < artifactLearningSize; } // sufficient samples to fit AR model if (!stillLearning) { for (int i = 0; i < n; i++) { double[] x = artifactLearningSamples[i].ToArray(); var p = StatsUtils.EstimateAROrder(x, 50); double mean = StatsUtils.SampleMean(x); double[] arParams = StatsUtils.FitAR(p, x); Logger.Log("Estimated AR params: p={0}, c={1}, phi={2}", p, mean, string.Join(", ", arParams)); arPredictors[i] = new ARModel(mean, arParams); lastPredictions[i] = arPredictors[i].Predict(evt.data[i]); } } isLearning = stillLearning; } else { bool isArtifact = false; for (int i = 0; i < n; i++) { var x = evt.data[i]; var error = x - lastPredictions[i]; var errorDist = arError[i]; errorDist.Update(error); lastPredictions[i] = arPredictors[i].Predict(x); if (errorDist.isValid) { var errorS = Math.Sqrt(errorDist.var); var errorMaxCI95 = errorDist.mean + 2 * errorS; var errorMinCI95 = errorDist.mean - 2 * errorS; //Logger.Log("Error={0}, 95% CI = [{1}, {2}]", error, errorMinCI95, errorMaxCI95); isArtifact = error > errorMaxCI95 || error < errorMinCI95; } } if (isArtifact) { Logger.Log("Detected large amplitude artifact via AR model"); return(true); } } Add(new EEGEvent(evt.timestamp, evt.type, buffer, evt.extra)); return(true); }