/// <summary> /// Для обработки в реальном времени /// Анализирует данные и для каждого найденного фронта /// с допустимой высотой и длительностью /// вызывает событие HeartContractionDetected. /// Минимально необходимая высота фронта подбирается /// исходя из статистических параметров входного сигнала. /// </summary> /// <param name="data_buf"></param> public void AddData(int[] data_buf, long timestamp) { if (data_buf.Length == 0) { return; } // какое количество данных соответствует этой отметке времени? long timestamp_data_count = this.m_TotalCounter + data_buf.Length; try { #if false // отладка: показываем значение сигнала с АЦП в текущий момент this.Debug_AddData(this.channelADC_Black, data_buf); #endif // normalize amplitude m_Normalizer.NormalizeDataInPlace(data_buf); // analyze signal here... // first-point initialization handling if (true == this.m_bIsHistoryEmpty) { this.m_bIsHistoryEmpty = false; // история данных -- константа this.m_Y.InitBuffer(data_buf[0]); // сумма констант == произведение размера на константу this.m_History_SumY.InitBuffer(data_buf[0] * this.m_History_SumY.GetSize()); // Сумма констант == произведение размера на константу this.m_SumY_front_history.InitBuffer(data_buf[0] * this.m_History_SumY.GetSize()); this.m_SumY = data_buf[0] * this.m_History_SumY.GetSize(); // история производной -- "0" this.m_DY.InitBuffer(0); // сумма значений производной -- "0" this.m_History_SumDY.InitBuffer(0); // история второй производной -- "0" this.m_History_D2Y.InitBuffer(0); } for (int i = 0; i < data_buf.Length; ++i) { // increment data counter ++m_TotalCounter; ProcessNextSample(data_buf[i], timestamp, timestamp_data_count); } } catch (Exception ex) { this.log.Error(ex); } }
/// <summary> /// Для обработки в реальном времени /// Анализирует данные и для каждого найденного фронта /// вызывает событие HeartContractionDetected. /// </summary> /// <param name="data_buf"></param> public void AddData(int[] data_buf, long timestamp) { // какое количество данных соответствует этой отметке времени? long timestamp_data_count = this.m_TotalCounter + data_buf.Length; try { // filter signal and normalize amplitude m_Denoiser.FilterInPlace(data_buf); m_Normalizer.NormalizeDataInPlace(data_buf); // analyze signal here... // we shall detect rising fronts of amplitude // greater than sigma ( square root of dispersion ) // so we must calculate dispersion and keep it for last 2 seconds // of signal and update it upon receiving new data fragments if (true == this.m_HistoryEmpty) { // сброс флага m_HistoryEmpty происходит в функции AddValue this.m_PreviousValue = data_buf[0]; } int cur_val = data_buf[0]; for (int i = 0; i < data_buf.Length; ++i) { cur_val = data_buf[i]; // отдаем вновь полученное значение сборщику статистики m_SignalStatCollector.AddValue(cur_val); // и запоминаем в собственной истории данных AddHistoryPoint(cur_val); // increment data counter ++m_TotalCounter; if (this.m_IsRising) { // local maximum detection if (cur_val < this.m_PreviousValue) { // reset for detection of successive local minimum this.m_IsRising = false; this.m_Max = this.m_PreviousValue; this.m_MaxPos = this.m_TotalCounter; this.m_IsFalling = true; // end of front detected this.OnFrontAndPeakFound(timestamp, timestamp_data_count); } } else if (this.m_IsFalling) { // local minimum detection if (cur_val > this.m_PreviousValue) { // reset for detection successive local maximum this.m_IsFalling = false; this.m_Min = this.m_PreviousValue; this.m_MinPos = this.m_TotalCounter; this.m_IsRising = true; } } else if (cur_val > this.m_PreviousValue) { this.m_IsRising = true; this.m_IsFalling = false; this.m_Min = this.m_PreviousValue; this.m_MinPos = this.m_TotalCounter; } else if (cur_val < this.m_PreviousValue) { this.m_IsFalling = true; this.m_IsRising = false; this.m_Max = this.m_PreviousValue; this.m_MaxPos = this.m_TotalCounter; } // update stored previous value this.m_PreviousValue = cur_val; } } catch (Exception ex) { this.log.Error(ex); } }