public HrvResults ProcessData(HrvRawData rawHrvData) { base.ProcessData(rawHrvData); double[] valid_intervals_durations = null; List <CardioInterval> valid_intervals = null; // recorded data contains cardio intervals // this is the case when the inspection was performed // using a cardio intervals sensor // (e.g. Bluetooth HR sensor with RR intervals) if (InputDataContainsCardioIntervals(rawHrvData)) { var heartRythmData = ReadCardioIntervalsFromResultSet(rawHrvData); valid_intervals = heartRythmData.Item1; HrvResults.RATED_HR_MARKS = heartRythmData.Item2.ToArray(); } else { ChannelData data = null, adc_data = null; double[] hrv_marks = null; SelectAndProcessSignal(m_settings.RejectLowQualitySignalAreas, rawHrvData, out hrv_marks, out data, out adc_data); if ((null == data) || (null == adc_data)) { throw new DataProcessingException(strings.NoSignalInSourceData); } HrvResults.CRV_CARDIO_SIGNAL = new PhysioSignalView( data.GetDataAsFloat(), (int)data.BitsPerSample, (float)data.SamplingRate, data.PhysioSignalType ); HrvResults.CRV_CARDIO_SIGNAL_ADC = new PhysioSignalView( adc_data.GetDataAsFloat(), (int)adc_data.BitsPerSample, (float)adc_data.SamplingRate, adc_data.PhysioSignalType ); // отметки пульса с оценкой достоверности var hc_marks_to_rate = new List <RatedContractionMark>(hrv_marks.Length); for (int i = 0; i < hrv_marks.Length; ++i) { var mark = new RatedContractionMark { Position = hrv_marks[i], Valid = false }; hc_marks_to_rate.Add(mark); } // получаем достоверные интервалы в миллисекундах, // "хорошие" сокращения будут помечены как хорошие (по обоим параметрам), var converted_peaks = PeaksFilter.ConvertPeaksToIntervalsWithRejectionAndRatePeaks( hc_marks_to_rate, data.SamplingRate, m_settings.GetApplicableMinIntervalLength(), m_settings.GetApplicableMaxIntervalLength(), m_settings.GetApplicableMaxIntervalDeltaRelative(), adc_data); valid_intervals = converted_peaks.extracted_intervals; if (converted_peaks.extracted_intervals.Count < 5) { throw new DataProcessingException(string.Format(strings.too_few_intervals_detected_in_signal, valid_intervals.Count)); } // это все отметки пульса HrvResults.CRV_HR_MARKS = hrv_marks; // это отметки пульса с оценками качества HrvResults.RATED_HR_MARKS = converted_peaks.rated_heart_contraction_marks.ToArray(); // Спектр сигнала ФПГ HrvResults.SignalSpectrum = PpgSpectrumAnalyzer.CalculateSpectrum(data.Data, data.SamplingRate); } // только длительности интервалов valid_intervals_durations = (from interval in valid_intervals select interval.duration).ToArray(); var peakAmoDistrib = default(MinMaxModeDescriptor); // кардио-интервалы подготовлены try { var mathStatData = new StatData(); // считаем статистику по кардио-интервалам Calculator.CalcStatistics(valid_intervals_durations, mathStatData); Calculator.MakeProbabilityDensity(valid_intervals_durations, mathStatData, 440, 2000, 4); // The standard approach Calculator.MakeDistribution(valid_intervals_durations, mathStatData, 0, 2500, 50); // Find the maximized & minimized mode amplitude values peakAmoDistrib = MinMaxModeAmpFinder.GetPeakAmoDistrib(valid_intervals_durations, 50, 0, 2500, 1); HrvResults.CRV_STAT = _mapper.Map <Methods.ObjectModel.Statistics.StatData>(mathStatData); } catch (ArgumentException) { throw new DataProcessingException( string.Format(strings.too_few_valid_intervals_detected_in_signal, valid_intervals_durations.Length)); } HrvResults.Indicators.HRV_Triangular_Index = CalcHrvTriangularIndex(valid_intervals_durations); double AMo = Statistics.distribution.mode_amplitude; // Безразмерная -- в долях от 1 double Mo = Statistics.distribution.mode / 1000; // В секундах!!! //double AMo = peakAmoDistrib.ModeAmp; // Безразмерная -- в долях от 1 //double Mo = peakAmoDistrib.Mode / 1000; // В секундах!!! double M = Statistics.m / 1000; // В секундах!!! double dNN = Statistics.varRange / 1000; // В секундах!!! double Sigma = Statistics.sigma / 1000; // В секундах!!! var _AMo = Statistics.distribution.mode_amplitude; // Безразмерная -- в долях от 1 var _Mo = Statistics.distribution.mode / 1000; // В секундах!!! var AmoMax = peakAmoDistrib.MaxModeDescriptor.ModeAmp; // maximized Mode Amplitude var MoMax = peakAmoDistrib.MaxModeDescriptor.Mode / 1000; // Mode when the Mode amplitude is maximized var AmoMin = peakAmoDistrib.MinModeDescriptor.ModeAmp; // minimized Mode Amplitude var MoMin = peakAmoDistrib.MinModeDescriptor.Mode / 1000; // Mode when the Mode amplitude is minimized var AmoMid = peakAmoDistrib.MidModeDescriptor.ModeAmp; // mean Mode Amplitude var MoMid = peakAmoDistrib.MidModeDescriptor.Mode / 1000; // Mode when the Mode amplitude is close to mean // Вычислим индексы Баевского // умножаем на 100, т.к. "по Баевскому" АМо указывается в %%. HrvResults.Indicators.IN = AMo * 100 / (2.0 * Mo * dNN); HrvResults.Indicators.IN_Max = AmoMax * 100 / (2.0 * MoMax * dNN); HrvResults.Indicators.IN_MaxMode = MoMax; HrvResults.Indicators.IN_Min = AmoMin * 100 / (2.0 * MoMin * dNN); HrvResults.Indicators.IN_MinMode = MoMin; HrvResults.Indicators.IN_Mid = AmoMid * 100 / (2.0 * MoMid * dNN); HrvResults.Indicators.IN_MidMode = MoMid; // TODO: выяснить, что правильно, что нет: m_BaevskyStatistics._VPR = 1.f / (fM * fX); HrvResults.Indicators.VPR = 1.0 / (Mo * dNN); HrvResults.Indicators.PAPR = AMo * 100 / Mo; HrvResults.Indicators.IVR = AMo * 100 / dNN; // Психофизиологическая цена [адаптации] // АМ [%%] / ( Мат. ожидание [с] * сигма [с] ) // умножаем на 100, т.к. АМо д.б. в %%. HrvResults.Indicators.PPPA = AMo * 100 / (M * Sigma); // Передать в результаты теста также и сами кардиоинтервалы // с моментами их появления в записи HrvResults.CRV_INTERVALS = valid_intervals.ToArray(); // Спектр кардиоинтервалов HrvResults.IntervalsSpectrum = HrvSpectrumAnalyzer.MakeCardioIntervalsSpectrum(valid_intervals); double sum = 0.0; double n50 = (valid_intervals[0].duration > 50) ? 1 : 0; for (int i = 1; i < valid_intervals.Count; i++) { double diff = valid_intervals[i].duration - valid_intervals[i - 1].duration; sum += Math.Pow(diff, 2); if (diff > 50) { n50++; } } HrvResults.Indicators.pNN50 = 100.0 * n50 / (valid_intervals.Count - 1); HrvResults.Indicators.RMSSD = Math.Sqrt(sum); HrvResults.CRV_SCATTEROGRAMM_PARAMETERS = GetScatterParams(valid_intervals_durations); HrvResults.ResultsReliability = GetResultsReliability(HrvResults); return((HrvResults)ProcessorOutputData); }
private static double Estimate_AverageInterval( List <RatedContractionMark> hr_marks_to_rate, double data_rate, double min_interval_length, double max_interval_length, double max_relative_delta ) { double[] coordinates = new double[hr_marks_to_rate.Count]; for (int i = 0; i < coordinates.Length; ++i) { coordinates[i] = hr_marks_to_rate[i].Position; } // это черновой список интервалов, учитывающий всю последовательность // интервалов, получающихся из всей последовательности сокращений за исключением // интервалов, не удовлетворяющих ограничениям min_interval_length, max_interval_length // и max_relative_delta. // при проверке первого интервала на относительное изменение величина предыдущего интервала // принимается равной значению моды черновой последовательности кардио-интервалов. var draft_intervals = PeaksFilter.CoordinatesToIntervals(coordinates, data_rate); var draft_durations = new List <double>(draft_intervals.Count); foreach (CardioInterval draft_interval_1 in draft_intervals) { draft_durations.Add(draft_interval_1.duration); } double[] draft_durations_array = draft_durations.ToArray(); var stat = new PskOnline.Math.Statistics.StatData(); PskOnline.Math.Statistics.Calculator.CalcStatistics(draft_durations_array, stat); PskOnline.Math.Statistics.Calculator.MakeProbabilityDensity(draft_durations_array, stat, 400, 2000, 4); double ave = 0; double prev_interval_duration = stat.probability_density.mode; int draft_count = 0; bool bLengthOk = false; bool bRDeltaOk = false; foreach (CardioInterval draft_interval in draft_intervals) { bLengthOk = (draft_interval.duration >= min_interval_length) && (draft_interval.duration <= max_interval_length); bRDeltaOk = (max_relative_delta >= (System.Math.Abs(prev_interval_duration - draft_interval.duration) / prev_interval_duration)); if (bLengthOk && bRDeltaOk) { // учтем "хороший" интервал ave += draft_interval.duration; ++draft_count; prev_interval_duration = draft_interval.duration; } } if (draft_count == 0) { throw new DataProcessingException( string.Format(strings.too_few_valid_intervals_detected_in_signal, draft_count)); } return(ave / ((double)draft_count)); }