public void Test_Dispersion_Of_Constant() { SeriesStatisticsCollector ssc = new SeriesStatisticsCollector(size); for (int i = 0; i < size; ++i) { ssc.AddValue(constantValue); } ssc.AddValue(constantValue); NUnit.Framework.Assert.AreEqual(0, ssc.GetDX(0)); NUnit.Framework.Assert.AreEqual(constantValue, ssc.GetMX(0)); }
/// <summary> /// Обрабатываем следующий отсчет АЦП /// </summary> /// <param name="stab_val"></param> /// <param name="timestamp"></param> /// <param name="timestamp_data_count"></param> private void ProcessNextSample(int stab_val, long timestamp, long timestamp_data_count) { double RezD1 = -500; double RezT = -1000; double RezD2 = -2000; try { // запоминаем историю стабилизированного сигнала this.m_Y.AddValue(stab_val); // посчитаем новое среднее значение сигнала, в текущий момент this.m_SumY -= m_Y.GetOldestValue(); this.m_SumY += stab_val; // запоминаем историю сумм (а значит, и средних значений) this.m_History_SumY.AddValue((long)this.m_SumY); // запоминаем историю стабилизированных значений this.m_SumY_front_history.AddValue(this.m_SumY); // считаем производную в текущий момент double Y_0 = this.m_History_SumY.GetValue(0); double Y_1 = this.m_History_SumY.GetValue(2); RezD1 = (1 * (Y_0 - Y_1) * 76800) / KD1; double Rez = RezD1; if (Rez < 0) { Rez = 0; } // Собираем статистику по фильтрованному сигналу m_Signal_Statistics.AddValue(this.m_SumY / ((double)this.m_Y.GetSize())); // Собираем статистику по значениям первой производной, большей 0 m_DY_Statistics.AddValue(Rez); if (0 == Rez) { // оцениваем пороговое значение для первой производной // обновляем его только до тех пор, пока не начнется фронт // полагаем его равным СКО производной стабилизированного сигнала // с некоторым подстраиваемым коэффициентом int sigma_DY = (int)(dY_Threshold_Factor * System.Math.Sqrt(m_DY_Statistics.GetDX(0))); m_Threshold_DY = System.Math.Max(sigma_DY, this.MinThreshold); } // первая производная с учетом порога RezT = Rez - m_Threshold_DY; if (RezT < 0) { RezT = 0; } this.m_SumDY -= this.m_DY.GetOldestValue(); this.m_SumDY += (long)RezT; // запоминаем историю значений первой производной, большей 0 с учетом порога this.m_DY.AddValue((int)RezT); // запоминаем историю суммы последних значений первой производной, больших чем 0 с учетом порога this.m_History_SumDY.AddValue((long)this.m_SumDY); // вычисляем вторую производную double MDY_0 = this.m_History_SumDY.GetValue(0); double MDY_1 = this.m_History_SumDY.GetValue(2); // вычисляем 2 производную RezD2 = (1 * (MDY_0 - MDY_1) * 3880) / KD2; // запоминаем историю производной this.m_History_D2Y.AddValue((int)RezD2); // проверяем пересечение нуля второй производной сверху вниз double D2Y_0 = this.m_History_D2Y.GetValue(0); System.Diagnostics.Debug.Assert(((int)RezD2) == ((int)D2Y_0)); //if( this.bFrontAlreadyDetected && ( ! zero_crossing_detector.AddValue(D2Y_0)) ) //{ // this.m_SumY_front_history.GetLastRisingFrontAmplitude() //} if (zero_crossing_detector.AddValue(D2Y_0)) { // Теперь надо ждать конца фронта, а потом оценить его высоту... // В момент пересечения нуля второй производной сверху вниз // мы знаем высоту примерно половины фронта! // Пока удовольствуемся этим -- так проще. double sumY_front_amplitude = this.m_SumY_front_history.GetLastRisingFrontAmplitude(); double detected_front_amplitude = sumY_front_amplitude / ((double)this.BY); double detected_front_duration = this.m_SumY_front_history.GetLastRisingFrontDuration(); // Момент фронта double front_sample_count = ((double)this.m_TotalCounter) - zero_crossing_detector.GetLastZeroCrossingPoint(); double delta_samples = ((double)timestamp_data_count) - front_sample_count; double delta_time_seconds = delta_samples / this.m_SamplingRate; double delta_timestamp = 1.0e6 * delta_time_seconds; long front_timestamp = timestamp - ((long)delta_timestamp); log.DebugFormat( "front: time {0} ms, duration {1} ms, amplitude {2}, raw amplitude is {3}", (int)(front_sample_count / this.m_SamplingRate * 1000.0), (int)(detected_front_duration / this.m_SamplingRate * 1000.0), (int)detected_front_amplitude, (int)sumY_front_amplitude ); if (detected_front_duration > 0) { double front_duration_ms = ((double)detected_front_duration * 1000.0 / this.m_SamplingRate); double front_duration_min_threshold_ms = 2; double front_duration_max_threshold_ms = 1000; // оценим высоту предшествующего фронта... double front_height = detected_front_amplitude; // адаптивный порог высоты фронта double front_height_threshold = this.front_amplitude_threshold_factor * System.Math.Sqrt(this.m_Signal_Statistics.GetDX(1)); // учитываем абсолютный порог минимально допустимой высоты фронта front_height_threshold = System.Math.Max(front_height_threshold, 10); if ((front_duration_ms > front_duration_min_threshold_ms)) { if ((front_duration_ms < front_duration_max_threshold_ms)) { if ( (System.Math.Abs(front_height) > front_height_threshold) ) { log.DebugFormat(".NORMAL FRONT ({0} steps), threshold is {1}", (int)front_height, (int)front_height_threshold ); // поставить метку "хорошего" фронта // Debug_AddMark(front_sample_count / this.m_SamplingRate, System.Drawing.Color.Green); // this function checks fronts and rejects those // that may have been detected due to various interferences this.OnFrontFound(front_sample_count, front_timestamp); return; } else { this.log.DebugFormat( "....front too low ({0} steps), threshold is {1}", (int)front_height, (int)front_height_threshold ); } } else { this.log.DebugFormat( "....front too long ({0} ms), max. allowed is {1} ms", (int)front_duration_ms, (int)front_duration_max_threshold_ms ); } } else { this.log.DebugFormat( "....front too short ({0} ms), min. allowed is {1} ms", (int)front_duration_ms, (int)front_duration_min_threshold_ms ); } } else { this.log.Debug( "....front too short (1 sampling period)" ); } //Debug_AddMark(front_sample_count / this.m_SamplingRate, System.Drawing.Color.Red); } } catch (Exception ex) { this.log.Error(ex); } finally { Dump(); #if ENABLE_DEBUG_MONITOR // отладка: показываем нефильтрованное значение // стабилизированного сигнала в текущий момент this.Debug_AddData(this.channelY_Crimson, stab_val); // отладка: показываем среднее значение // стабилизированного сигнала в текущий момент // (сглаженный сигнал перед дифференцированием) this.Debug_AddData(this.channelMY_Red, this.m_SumY / ((double)this.m_Y.GetSize())); // показываем производную, не обрезанную ниже уровня "0" this.Debug_AddData(this.channelDY_LightGreen, RezD1); // отладка: показываем производную, обрезанную ниже уровня порога this.Debug_AddData(this.channelDY_T_Brown, RezT); // отладка: показываем уровень порога this.Debug_AddData(this.channelT_Maroon, m_Threshold_DY); // отладка: показываем сглаженное значение первой производной this.Debug_AddData(this.channelMDY_Blue, this.m_SumDY / ((double)this.m_DY.GetSize())); // отладка: показываем вторую производную this.Debug_AddData(this.channelD2Y_Orange, RezD2); #endif } }