// Implementation #region Implementation /// <summary> /// Constructor /// </summary> public BrainflowDataProcessor(string name, int boardId, int sampleRate) { BoardId = boardId; NumberOfChannels = BrainhatBoardShim.GetNumberOfExgChannels(boardId); SampleRate = sampleRate; Name = name; CreateChannelStdDevRunningCollection(); UnfilteredData = new List <IBFSample>(); DataToProcess = new ConcurrentQueue <IBFSample>(); NotifyAddedData = new SemaphoreSlim(0); BandPowers = new BandPowerMonitor(Name, BoardId, SampleRate); BandPowers.GetRawChunk = GetRawChunk; BandPowers.Log += OnComponentLog; RealTimeBufferLengthSeconds = 5 * 60; ActiveFilters = new Dictionary <string, RealTimeSignalProcessing>(); // default periods for periodic loops PeriodMsUpdateData = 200; // 5Hz for update data message PeriodMsUpdateProcessorStats = 5000; // every five seconds update processor stats PeriodMsUpdateStdDev = 100; // 10Hz std deviation update PeriodMsUpdateDataFilter = 200; // 5 Hz data filter update PeriodMsFlushOldData = 2000; // Flush old every two seconds ProcessingTimesQueue = new ConcurrentQueue <double>(); }
/// <summary> /// Set the file properties from the header information /// </summary> bool SetFilePropertiesFromHeader(EdfHeaderStruct header) { BoardId = header.recording_additional.Trim().GetBoardId(); switch ((BrainhatBoardIds)BoardId) { case BrainhatBoardIds.CYTON_BOARD: case BrainhatBoardIds.CYTON_DAISY_BOARD: case BrainhatBoardIds.MENTALIUM: break; default: return(false); } SampleRate = (int)(header.signalparam[0].smp_in_datarecord / (header.datarecord_duration * 1.0E-7)); DataRecordDuration = header.datarecord_duration * 1.0E-7; NumberOfChannels = BrainhatBoardShim.GetNumberOfExgChannels(BoardId); var date = new DateTime(header.startdate_year, header.startdate_month, header.startdate_day, header.starttime_hour, header.starttime_minute, header.starttime_second); date = date.AddMilliseconds(header.starttime_subsecond / 10_000); StartTime = new DateTimeOffset(date.ToUniversalTime(), TimeSpan.FromHours(0)).ToUnixTimeInDoubleSeconds(); EndTime = StartTime + (header.datarecords_in_file * (header.datarecord_duration * 1.0E-7)); return(true); }
/// <summary> /// Setup LSL outlet for the board /// </summary> void SetupLslOutletForBoard() { var numChannels = BrainhatBoardShim.GetNumberOfExgChannels(BoardId); var numAccelChannels = BrainhatBoardShim.GetNumberOfAccelChannels(BoardId); var numOtherChannels = BrainhatBoardShim.GetNumberOfOtherChannels(BoardId); var numAnalogChannels = BrainhatBoardShim.GetNumberOfAnalogChannels(BoardId); SampleSize = 2 + numChannels + numAccelChannels + numOtherChannels + numAnalogChannels; StreamInfo = new liblsl.StreamInfo(BoardId.GetSampleName(), "BFSample", SampleSize, SampleRate, liblsl.channel_format_t.cf_double64, BrainHatNetwork.NetworkUtilities.GetHostName()); StreamInfo.desc().append_child_value("manufacturer", "OpenBCI"); StreamInfo.desc().append_child_value("boardId", $"{BoardId}"); liblsl.XMLElement chns = StreamInfo.desc().append_child("channels"); chns.append_child("channel") .append_child_value("label", "SampleIndex") .append_child_value("unit", "0-255") .append_child_value("type", "index"); for (int k = 0; k < numChannels; k++) { chns.append_child("channel") .append_child_value("label", $"ExgCh{k}") .append_child_value("unit", "uV") .append_child_value("type", "EEG"); } for (int k = 0; k < numAccelChannels; k++) { chns.append_child("channel") .append_child_value("label", $"AcelCh{k}") .append_child_value("unit", "1.0") .append_child_value("type", "Accelerometer"); } for (int k = 0; k < numOtherChannels; k++) { chns.append_child("channel") .append_child_value("label", $"Other{k}"); } for (int k = 0; k < numAnalogChannels; k++) { chns.append_child("channel") .append_child_value("label", $"AngCh{k}"); } chns.append_child("channel") .append_child_value("label", "TimeStamp") .append_child_value("unit", "s"); }
// Implementation #region Implementation /// <summary> /// Constructor /// </summary> public BandPowerMonitor(string name, int boardId, int sampleRate) { BoardId = boardId; NumberOfChannels = BrainhatBoardShim.GetNumberOfExgChannels(boardId); SampleRate = sampleRate; Name = name; PeriodMilliseconds = 200; // default 5 Hz ProcessingTimes = new ConcurrentQueue <double>(); BandPowersCollection = new ConcurrentDictionary <string, IBFSample>(); BandPowerCalc = new BandPowerCalculator(BoardId, NumberOfChannels, SampleRate); InitializeMonitorForBandPowerRangeList(); }
// Implementation #region Implementation /// <summary> /// Constructor /// </summary> public RealTimeSignalProcessing(int boardId, int sampleRate, SignalFilter filter, ISignalMontage montage) { BoardId = boardId; NumberOfChannels = BrainhatBoardShim.GetNumberOfExgChannels(BoardId); SampleRate = sampleRate; Filter = filter; Montage = montage; Name = KeyName((Filter == null ? "" : Filter.Name), Montage.Name); PeriodMilliseconds = 33; FilterBufferLength = 10; ProcessingTimes = new ConcurrentQueue <double>(); FilteredData = new ConcurrentQueue <IBFSample>(); }
/// <summary> /// Write the file header /// </summary> void WriteFileHeader(StreamWriter file) { // write header file.WriteLine("%OpenBCI Raw EEG Data"); file.WriteLine($"%Number of channels = {BrainhatBoardShim.GetNumberOfExgChannels(BoardId)}"); file.WriteLine($"%Sample Rate = {SampleRate} Hz"); file.WriteLine($"%Board = {FileBoardDescription()}"); switch ((BrainhatBoardIds)BoardId) { case BrainhatBoardIds.MENTALIUM: file.WriteLine($"%ExtraBoardId = {BoardId}"); break; } file.WriteLine("%Logger = brainHat"); file.WriteLine($"%SubjectName = {Info.SubjectName}"); file.WriteLine($"%SubjectCode = {Info.SubjectCode}"); file.WriteLine($"%SubjectAdditional = {Info.SubjectAdditional}"); file.WriteLine($"%SubjectGender = {(Info.SubjectGender == 0 ? "XX" : "XY")}"); file.WriteLine($"%SubjectBirthday = {Info.SubjectBirthday.ToString("yyyy-MM-dd")}"); file.WriteLine($"%AdminCode = {Info.AdminCode}"); file.WriteLine($"%Technician = {Info.Technician}"); }
/// <summary> /// Init the board session /// </summary> private async Task InitializeBoardAsync() { try { Log?.Invoke(this, new LogEventArgs(this, "InitializeBoardAsync", $"Initializaing board", LogLevel.DEBUG)); await ReleaseBoardAsync(); RequestToggleStreamingMode = false; var useBoardId = BoardId; switch ((BrainhatBoardIds)BoardId) { case BrainhatBoardIds.MENTALIUM: useBoardId = 0; break; } TheBoard = new BoardShim(useBoardId, InputParams); SampleRate = BrainhatBoardShim.GetSampleRate(BoardId); TimeStampIndex = BrainhatBoardShim.GetTimestampChannel(BoardId); TheBoard.prepare_session(); await Task.Delay(TimeSpan.FromSeconds(1)); TheBoard.config_board("s"); BoardSettings = new CytonBoardsImplementation(); if (!await LoadBoardRegistersSettings(1)) { throw new Exception("Unable to get board register settings"); } // TODO - clean up SRB1 start setting if (StartSrb1CytonSet) { await SetSrb1Async(BoardSettings.Boards[0].Channels[0], true); if (StartSrb1DaisySet && (BrainhatBoardIds)BoardId == BrainhatBoardIds.CYTON_DAISY_BOARD && BoardSettings.Boards.Count() > 1) { await SetSrb1Async(BoardSettings.Boards[1].Channels[0], true); } BoardSettings = new CytonBoardsImplementation(); if (!await LoadBoardRegistersSettings(1)) { throw new Exception("Unable to get board register settings"); } } await StartStreamingAsync(); await Task.Delay(TimeSpan.FromSeconds(5)); ConnectToBoard?.Invoke(this, new ConnectToBoardEventArgs(BoardId, SampleRate)); } catch (Exception e) { Log?.Invoke(this, new LogEventArgs(this, "InitializeBoardAsync", e, LogLevel.ERROR)); if (TheBoard != null && TheBoard.is_prepared()) { TheBoard.release_session(); } TheBoard = null; } }