Beispiel #1
0
        public BFSampleImplementation(IBFSample copy)
        {
            SampleIndex = copy.SampleIndex;

            ExgChannels = new double[copy.NumberExgChannels];
            for (int i = 0; i < NumberExgChannels; i++)
            {
                SetExgDataForChannel(i, copy.GetExgDataForChannel(i));
            }

            AcelChannels = new double[copy.NumberAccelChannels];
            for (int i = 0; i < NumberAccelChannels; i++)
            {
                SetAccelDataForChannel(i, copy.GetAccelDataForChannel(i));
            }

            OtherChannels = new double[copy.NumberOtherChannels];
            for (int i = 0; i < NumberOtherChannels; i++)
            {
                SetOtherDataForChannel(i, copy.GetOtherDataForChannel(i));
            }

            AnalogChannels = new double[copy.NumberAnalogChannels];
            for (int i = 0; i < NumberAnalogChannels; i++)
            {
                SetAnalogDataForChannel(i, copy.GetAccelDataForChannel(i));
            }

            TimeStamp = copy.TimeStamp;
        }
Beispiel #2
0
        /// <summary>
        /// Check for Blink in the specified eye
        /// </summary>
        void CheckForBlink(IBFSample currentReading, double stdDev, double stdDevAvg, Eyes eye)
        {
            IBFSample trigger = (eye == Eyes.Left) ? BlinkLeftRisingEdgeTrigger : BlinkRightRisingEdgeTrigger;

            //  search for rising and falling edge of the signal
            if (trigger != null)
            {
                //  rising edge triggered, check for signal going below falling threashold
                if (stdDev / stdDevAvg < BlinkDownDevThreshold)
                {
                    if ((currentReading.TimeStamp - trigger.TimeStamp) > BlinkPeriodThresholdMin && (currentReading.TimeStamp - trigger.TimeStamp) < BlinkPeriodThresholdMax)
                    {
                        DetectedBlink?.Invoke(this, new DetectedBlinkEventArgs(eye, WinkState.Wink, currentReading.TimeStamp));
                        ClearTrigger(eye);
                    }
                    else
                    {
                        //  reject as noise
                        DetectedBlink?.Invoke(this, new DetectedBlinkEventArgs(eye, WinkState.Falling, currentReading.TimeStamp));
                        ClearTrigger(eye);
                    }
                }
                else if (currentReading.TimeStamp - trigger.TimeStamp > BlinkPeriodThresholdMax)
                {
                    //  taken too long, clear the rising flag
                    DetectedBlink?.Invoke(this, new DetectedBlinkEventArgs(eye, WinkState.Falling, currentReading.TimeStamp));
                    ClearTrigger(eye);
                }
            }
            else if (trigger == null /*&&  (stdDevAvg < NoisyStdDevThreshold)*/ && (stdDev / stdDevAvg > BlinkUpDevThreshold))
            {
                DetectedBlink?.Invoke(this, new DetectedBlinkEventArgs(eye, WinkState.Rising, currentReading.TimeStamp));
                SetTrigger(currentReading, eye);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Write sample header to file based on the first sample recorded
        /// </summary>
        void WriteSampleHeaderOnFirstSample(StreamWriter file, IBFSample nextReading)
        {
            string header = "Sample Index";

            //  exg channels
            for (int i = 0; i < nextReading.NumberExgChannels; i++)
            {
                header += $", EXG Channel {i}";
            }

            //  accelerometer channels
            for (int i = 0; i < nextReading.NumberExgChannels; i++)
            {
                header += $", Accel Channel {i}";
            }

            //  other channels
            for (int i = 0; i < nextReading.NumberOtherChannels; i++)
            {
                header += $", Other";
            }

            //  analog channels
            for (int i = 0; i < nextReading.NumberAnalogChannels; i++)
            {
                header += $", Analog Channel {i}";
            }

            //  time stamps
            header += ", Timestamp, Timestamp (Formatted)";

            file.WriteLine(header);
        }
Beispiel #4
0
        /// <summary>
        /// Check the sample index sequence
        /// log a warning if sample index is missing
        /// </summary>
        private void InspectSampleIndex(IBFSample sample)
        {
            if (LastSampleIndex < 0)
            {
                LastSampleIndex = (int)sample.SampleIndex;
                return;
            }

            var difference = sample.SampleIndex.SampleIndexDifference(LastSampleIndex);

            LastSampleIndex = (int)sample.SampleIndex;

            switch ((BrainhatBoardIds)BoardId)
            {
            case BrainhatBoardIds.CYTON_BOARD:
            case BrainhatBoardIds.MENTALIUM:
                if (difference > 1)
                {
                    CountMissingIndex++;
                }
                break;

            case BrainhatBoardIds.CYTON_DAISY_BOARD:
                if (difference > 2)
                {
                    CountMissingIndex++;
                }
                break;
            }
        }
 /// <summary>
 /// Add data to the file writer
 /// </summary>
 public void AddData(IBFSample data)
 {
     if (FileWritingTask != null)
     {
         Data.Enqueue(data);
         NotifyAddedData.Release();
     }
 }
Beispiel #6
0
        /// <summary>
        /// Clear out the std dev collections
        /// </summary>
        void ClearStdDevRunningCollection()
        {
            for (int i = 0; i < NumberOfChannels; i++)
            {
                ChannelSdtDevRunningCollection[i].RemoveAll();
            }

            StdDevMedians = new BFSampleImplementation(BoardId);
        }
Beispiel #7
0
        public BFSampleImplementation(IBFSample sample, int numExg, int numAcel, int numOther, int numAnalog)
        {
            ExgChannels    = new double[numExg];
            AcelChannels   = new double[numAcel];
            OtherChannels  = new double[numOther];
            AnalogChannels = new double[numAnalog];

            SampleIndex = sample.SampleIndex;
            TimeStamp   = sample.TimeStamp;
        }
Beispiel #8
0
        /// <summary>
        /// Create the collection of std deviations median
        /// </summary>
        void CreateChannelStdDevRunningCollection()
        {
            ChannelSdtDevRunningCollection = new List <ConcurrentQueue <double> >();
            //  add a collection for each channel
            for (int i = 0; i < NumberOfChannels; i++)
            {
                ChannelSdtDevRunningCollection.Add(new ConcurrentQueue <double>());
            }

            StdDevMedians = new BFSampleImplementation(BoardId);
        }
Beispiel #9
0
 /// <summary>
 /// Detect blinks function
 /// using current reading, the standard deviation from channel 0 (FP1) and channel 1 (FP2),
 /// and the average deviaiton from channel 0,1
 /// </summary>
 public void DetectBlinks(IBFSample currentReading, double stdDev0, double stdDevAvg0, double stdDev1, double stdDevAvg1)
 {
     try
     {
         CheckForBlink(currentReading, stdDev0, stdDevAvg0, Eyes.Left);
         CheckForBlink(currentReading, stdDev1, stdDevAvg1, Eyes.Right);
     }
     catch (Exception e)
     {
         Log?.Invoke(this, new LogEventArgs(this, "DetectBlinks", e, LogLevel.ERROR));
     }
 }
Beispiel #10
0
        /// <summary>
        /// Process the data in the queue
        /// </summary>
        void ProcessDataQueue(IBFSample sample)
        {
            if (sample != null)
            {
                lock (UnfilteredData)
                    UnfilteredData.Insert(0, sample);

                InspectSampleIndex(sample);

                NewSample?.Invoke(this, new BFSampleEventArgs(sample));
            }
        }
Beispiel #11
0
        void SetTrigger(IBFSample reading, Eyes eye)
        {
            switch (eye)
            {
            case Eyes.Left:
                BlinkLeftRisingEdgeTrigger = reading;
                break;

            case Eyes.Right:
                BlinkRightRisingEdgeTrigger = reading;
                break;
            }
        }
Beispiel #12
0
        void ClearTrigger(Eyes eye)
        {
            switch (eye)
            {
            case Eyes.Left:
                BlinkLeftRisingEdgeTrigger = null;
                break;

            case Eyes.Right:
                BlinkRightRisingEdgeTrigger = null;
                break;
            }
        }
Beispiel #13
0
        /// <summary>
        /// Log raw data processing performance
        /// </summary>
        void LogRawDataProcessingPerformance(IBFSample data)
        {
            RecordsCount++;
            TimeSinceLastSample.Restart();
            RawDataOffsetTime = Math.Max(Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() / 1000 - data.TimeStamp), RawDataOffsetTime);

            if (CountRecordsTimer.ElapsedMilliseconds > 5000)
            {
                Log?.Invoke(this, new LogEventArgs(HostName, this, "LogRawDataProcessingPerformance", $"{HostName} Logged {(int)(RecordsCount / CountRecordsTimer.Elapsed.TotalSeconds)} records per second. Offset time {RawDataOffsetTime.ToString("F6")} s.", LogLevel.TRACE));
                CountRecordsTimer.Restart();
                RawDataOffsetTime = 0.0;
                RecordsCount      = 0;
            }
        }
Beispiel #14
0
        //  Public Methods
        #region PublicMethods

        /// <summary>
        /// Setup the band powers range lists
        /// </summary>
        public void InitializeMonitorForBandPowerRangeList()
        {
            BandPowers = new IBFSample[BandPowerCalc.NumberOfBands];

            //  create a dictionary for the results of the band power calculation
            //  must match the number of frequency ranges list above
            BandPowersCollection = new ConcurrentDictionary <string, IBFSample>();
            for (int j = 0; j < BandPowerCalc.NumberOfBands; j++)
            {
                BandPowers[j] = new BFSampleImplementation(BoardId);

                var key = (BandPowerCalc.BandPowerCalcRangeList[j].Item1 + (BandPowerCalc.BandPowerCalcRangeList[j].Item2 - BandPowerCalc.BandPowerCalcRangeList[j].Item1) / 2).BandPowerKey();
                BandPowersCollection.TryAdd(key, BandPowers[j]);
            }
        }
Beispiel #15
0
        /// <summary>
        /// Create samples from a single data record (chunk of signals per sample)
        /// </summary>
        void CreateSamples(double[,] chunk)
        {
            double dataRecordTime = StartTime.Value + (ReadDataRecordsCount * DataRecordDuration);

            for (int i = 0; i < chunk.GetRow(0).Length; i++)
            {
                IBFSample newSample = null;
                newSample = new BFSampleImplementation(BoardId);
                if (newSample != null)
                {
                    newSample.InitializeFromSample(chunk.GetColumn(i));
                    newSample.TimeStamp = StartTime.Value + newSample.TimeStamp;
                    _Samples.Add(newSample);
                }
            }
        }
Beispiel #16
0
        /// <summary>
        /// Write a sample to file
        /// </summary>
        void WriteToFile(StreamWriter file, IBFSample nextSample)
        {
            var seconds      = (long)Math.Truncate(nextSample.TimeStamp);
            var time         = DateTimeOffset.FromUnixTimeSeconds(seconds);
            var microseconds = nextSample.TimeStamp - seconds;

            //  sample index
            var writeLine = nextSample.SampleIndex.ToString("F3");

            //  exg channels
            foreach (var nextExg in nextSample.ExgData)
            {
                writeLine += $",{nextExg:F4}";
            }

            //  accelerometer channels
            foreach (var nextAcel in nextSample.AccelData)
            {
                writeLine += $",{nextAcel:F4}";
            }

            //  other channels
            foreach (var nextOther in nextSample.OtherData)
            {
                writeLine += $",{nextOther:F4}";
            }

            //  analog channels
            foreach (var nextAnalog in nextSample.AnalogData)
            {
                writeLine += $",{nextAnalog:F4}";
            }

            //  raw time stamp
            writeLine += $",{nextSample.TimeStamp:F6}";

            //  formatted time stamp
            writeLine += string.Format(",{0}-{1}-{2} {3}:{4}:{5}.{6}", time.LocalDateTime.Year.ToString("D2"), time.LocalDateTime.Month.ToString("D2"), time.LocalDateTime.Day.ToString("D2"), time.LocalDateTime.Hour.ToString("D2"), time.LocalDateTime.Minute.ToString("D2"), time.LocalDateTime.Second.ToString("D2"), ((int)(microseconds * 1000000)).ToString("D6"));

            file.WriteLine(writeLine);
        }
        /// <summary>
        /// Calculate band powers for the range list
        /// </summary>
        public IBFSample[] CalculateBandPowers(IEnumerable <IBFSample> samples)
        {
            var bandPowers = new IBFSample[BandPowerCalcRangeList.Count];

            for (int i = 0; i < BandPowerCalcRangeList.Count; i++)
            {
                bandPowers[i] = new BFSampleImplementation(BoardId);
            }

            for (int i = 0; i < NumberOfChannels; i++)
            {
                var bandPower = CalculateBandPower(samples, SampleRate, i, BandPowerCalcRangeList);

                int j = 0;
                foreach (var nextBandPower in bandPower)
                {
                    bandPowers[j++].SetExgDataForChannel(i, nextBandPower);
                }
            }

            return(bandPowers);
        }
Beispiel #18
0
        /// <summary>
        /// Create a sample from a single line of ascii text
        /// </summary>
        IBFSample CreateSample(string nextLine)
        {
            IBFSample newSample = null;

            newSample = new BFSampleImplementation(BoardId);
            newSample.InitializeFromText(nextLine);

            //  check the timestamp for valid data, this indicates we read a partial line for example the file is actively being written
            if (Math.Abs(newSample.TimeStamp - 0) < 0.0000001)
            {
                return(null);
            }

            //  cache the start time of the first record
            if (!StartTime.HasValue)
            {
                StartTime = newSample.TimeStamp;
            }
            //  cahce the end time
            EndTime = newSample.TimeStamp;

            return(newSample);
        }
Beispiel #19
0
        /// <summary>
        /// Check the sample index sequence
        /// log a warning if sample index is missing
        /// </summary>
        void InspectSampleIndex(IBFSample sample)
        {
            var nextIndex  = (int)(sample.SampleIndex);
            var difference = sample.SampleIndex.SampleIndexDifference(LastSampleIndex);

            LastSampleIndex = nextIndex;

            switch ((BrainhatBoardIds)BoardId)
            {
            default:
                if (difference > 1)
                {
                    CountMissingIndex++;
                }
                break;

            case BrainhatBoardIds.CYTON_DAISY_BOARD:
                if (difference > 2)
                {
                    CountMissingIndex++;
                }
                break;
            }
        }
Beispiel #20
0
 public void AddData(IBFSample data)
 {
     FileWriter.AddData(data);
 }
 /// <summary>
 /// Add data sample to the broadcast queue
 /// </summary>
 public void AddData(IBFSample sample)
 {
     DataToBroadcast.Enqueue(sample);
 }
Beispiel #22
0
 public BFSampleEventArgs(IBFSample sample)
 {
     Sample = sample;
 }
Beispiel #23
0
 /// <summary>
 /// Add data to the proecssor queue
 /// </summary>
 public void AddDataToProcessor(IBFSample data)
 {
     DataToProcess.Enqueue(data);
     NotifyAddedData.Release();
 }
        /// <summary>
        /// Open the file for writing and write the header information
        /// </summary>
        void WriteHeader(IBFSample firstSample)
        {
            if (firstSample == null || WroteHeader)
            {
                return; //  invalid first sample or we already wrote the header
            }
            //  open the file for writing
            FileHandle = edfOpenFileWriteOnly(FileName, 3, firstSample.SampleSize);
            if (FileHandle < 0)
            {
                throw new Exception("Unable to open the file.");
            }

            Log?.Invoke(this, new LogEventArgs(this, "WriteHeader", $"Started recording file {FileName}.", LogLevel.INFO));

            int signalCount = 0;

            //  Signal Properties
            //
            //  sample index
            edfSetSamplesInDataRecord(FileHandle, signalCount, SampleRate);
            edfSetPhysicalMaximum(FileHandle, signalCount, 255);
            edfSetPhysicalMinimum(FileHandle, signalCount, 0);
            edfSetDigitalMaximum(FileHandle, signalCount, 8388607);
            edfSetDigitalMinimum(FileHandle, signalCount, -8388608);
            edfSetLabel(FileHandle, signalCount, "SampleIndex");
            edfSetPrefilter(FileHandle, signalCount, "");
            edfSetTransducer(FileHandle, signalCount, "");
            edfSetPhysicalDimension(FileHandle, signalCount, "counter");
            signalCount++;
            //
            //  exg channels
            for (int i = 0; i < firstSample.NumberExgChannels; i++)
            {
                edfSetSamplesInDataRecord(FileHandle, signalCount, SampleRate);
                edfSetPhysicalMaximum(FileHandle, signalCount, 187500.000);
                edfSetPhysicalMinimum(FileHandle, signalCount, -187500.000);
                edfSetDigitalMaximum(FileHandle, signalCount, 8388607);
                edfSetDigitalMinimum(FileHandle, signalCount, -8388608);
                edfSetLabel(FileHandle, signalCount, $"EXG{i}");
                edfSetPrefilter(FileHandle, signalCount, "");
                edfSetTransducer(FileHandle, signalCount, "");
                edfSetPhysicalDimension(FileHandle, signalCount, "uV");
                signalCount++;
            }
            NumberOfExgChannels = firstSample.NumberExgChannels;
            //
            //  acel channels
            for (int i = 0; i < firstSample.NumberAccelChannels; i++)
            {
                edfSetSamplesInDataRecord(FileHandle, signalCount, SampleRate);
                edfSetPhysicalMaximum(FileHandle, signalCount, 1.0);
                edfSetPhysicalMinimum(FileHandle, signalCount, -1.0);
                edfSetDigitalMaximum(FileHandle, signalCount, 8388607);
                edfSetDigitalMinimum(FileHandle, signalCount, -8388608);
                edfSetLabel(FileHandle, signalCount, $"Acel{i}");
                edfSetPrefilter(FileHandle, signalCount, "");
                edfSetTransducer(FileHandle, signalCount, "");
                edfSetPhysicalDimension(FileHandle, signalCount, "unit");
                signalCount++;
            }
            NumberOfAcelChannels = firstSample.NumberAccelChannels;
            //
            //  other channels
            for (int i = 0; i < firstSample.NumberOtherChannels; i++)
            {
                edfSetSamplesInDataRecord(FileHandle, signalCount, SampleRate);
                edfSetPhysicalMaximum(FileHandle, signalCount, 9999.0);
                edfSetPhysicalMinimum(FileHandle, signalCount, -9999.0);
                edfSetDigitalMaximum(FileHandle, signalCount, 8388607);
                edfSetDigitalMinimum(FileHandle, signalCount, -8388608);
                edfSetLabel(FileHandle, signalCount, $"Other{i}");
                edfSetPrefilter(FileHandle, signalCount, "");
                edfSetTransducer(FileHandle, signalCount, "");
                edfSetPhysicalDimension(FileHandle, signalCount, "other");
                signalCount++;
            }
            NumberOfOtherChannels = firstSample.NumberOtherChannels;
            //
            //  analog channels
            for (int i = 0; i < firstSample.NumberAnalogChannels; i++)
            {
                edfSetSamplesInDataRecord(FileHandle, signalCount, SampleRate);
                edfSetPhysicalMaximum(FileHandle, signalCount, 9999.0);
                edfSetPhysicalMinimum(FileHandle, signalCount, -9999.0);
                edfSetDigitalMaximum(FileHandle, signalCount, 8388607);
                edfSetDigitalMinimum(FileHandle, signalCount, -8388608);
                edfSetLabel(FileHandle, signalCount, $"Analog{i}");
                edfSetPrefilter(FileHandle, signalCount, "");
                edfSetTransducer(FileHandle, signalCount, "");
                edfSetPhysicalDimension(FileHandle, signalCount, "analog");
                signalCount++;
            }
            NumberOfAnalogChannels = firstSample.NumberAnalogChannels;
            //
            //  timestamp
            edfSetSamplesInDataRecord(FileHandle, signalCount, SampleRate);
            edfSetPhysicalMaximum(FileHandle, signalCount, 43200.0);
            edfSetPhysicalMinimum(FileHandle, signalCount, 0);
            edfSetDigitalMaximum(FileHandle, signalCount, 8388607);
            edfSetDigitalMinimum(FileHandle, signalCount, -8388608);
            edfSetLabel(FileHandle, signalCount, "TestTime");
            edfSetPrefilter(FileHandle, signalCount, "");
            edfSetTransducer(FileHandle, signalCount, "");
            edfSetPhysicalDimension(FileHandle, signalCount, "seconds");
            FirstTimeStamp = firstSample.TimeStamp;

            //  File Header Properties
            //
            Info.ValidateForBdf();
            edfSetStartDatetime(FileHandle, firstSample.ObservationTime.Year, firstSample.ObservationTime.Month, firstSample.ObservationTime.Day, firstSample.ObservationTime.Hour, firstSample.ObservationTime.Minute, firstSample.ObservationTime.Second);
            edfSetSubsecondStarttime(FileHandle, firstSample.ObservationTime.Millisecond * 10_000);
            edfSetPatientName(FileHandle, Info.SubjectName);
            edfSetPatientCode(FileHandle, Info.SubjectCode);
            edfSetPatientYChromosome(FileHandle, (int)Info.SubjectGender);
            edfSetPatientBirthdate(FileHandle, Info.SubjectBirthday.Year, Info.SubjectBirthday.Month, Info.SubjectBirthday.Day);
            edfSetPatientAdditional(FileHandle, Info.SubjectAdditional);
            edfSetAdminCode(FileHandle, Info.AdminCode);
            edfSetTechnician(FileHandle, Info.Technician);
            edfSetEquipment(FileHandle, Info.Device);
            edfSetRecordingAdditional(FileHandle, BoardId.GetSampleNameShort());

            //  we are ready to write data
            FileTimer.Restart();
            WroteHeader = true;
        }