diff --git a/modules/core/include/visp3/core/vpStatisticalTestShewhart.h b/modules/core/include/visp3/core/vpStatisticalTestShewhart.h index 5dcfc37723..584658b0ec 100644 --- a/modules/core/include/visp3/core/vpStatisticalTestShewhart.h +++ b/modules/core/include/visp3/core/vpStatisticalTestShewhart.h @@ -86,9 +86,9 @@ class VISP_EXPORT vpStatisticalTestShewhart : public vpStatisticalTestSigma static std::string vpWecoRulesAlarmToString(const vpWecoRulesAlarm &alarm); static const bool CONST_ALL_WECO_ACTIVATED[COUNT_WECO - 1]; + static const int NB_DATA_SIGNAL = 8; protected: - static const int NB_DATA_SIGNAL = 8; unsigned int m_nbDataInBuffer; /*!< Indicate how many data are available in the circular buffer.*/ float m_signal[NB_DATA_SIGNAL]; /*!< The last values of the signal.*/ bool m_activateWECOrules; /*!< If true, activate the WECO's rules (NB: it increases the sensitivity of the Shewhart diff --git a/modules/core/src/math/misc/vpStatisticalTestShewhart.cpp b/modules/core/src/math/misc/vpStatisticalTestShewhart.cpp index cc1be99cec..b30bcf0a1f 100644 --- a/modules/core/src/math/misc/vpStatisticalTestShewhart.cpp +++ b/modules/core/src/math/misc/vpStatisticalTestShewhart.cpp @@ -104,7 +104,7 @@ vpStatisticalTestAbstract::vpMeanDriftType vpStatisticalTestShewhart::detectDown // Reinit for next iteration nbAbove2SigmaLimit = 0; nbAbove1SigmaLimit = 0; - if (m_signal[id] <= m_mean && m_activatedWECOrules[SAME_SIDE_WECO]) { + if (m_signal[id] < m_mean && m_activatedWECOrules[SAME_SIDE_WECO]) { // Single-side test ++nbAboveMean; } diff --git a/modules/core/test/math/testSPC.cpp b/modules/core/test/math/testSPC.cpp new file mode 100644 index 0000000000..407330060b --- /dev/null +++ b/modules/core/test/math/testSPC.cpp @@ -0,0 +1,809 @@ +/**************************************************************************** + * + * ViSP, open source Visual Servoing Platform software. + * Copyright (C) 2005 - 2023 by Inria. All rights reserved. + * + * This software is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact Inria about acquiring a ViSP Professional + * Edition License. + * + * See https://visp.inria.fr for more information. + * + * This software was developed at: + * Inria Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * + * If you have questions regarding the use of this file, please contact + * Inria at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Test various Statistical Process Control methods. + */ + +/*! + \example testSPC.cpp + \brief Test various Statistical Process Control methods. +*/ + +#include +#include + +#include +#include +#include +#include +#include + +bool initializeShewhartTest(const float &mean, const float &stdev, const bool &verbose, const std::string &testName, vpStatisticalTestShewhart &tester) +{ + const bool activateWECOrules = true; + tester.init(activateWECOrules, vpStatisticalTestShewhart::CONST_ALL_WECO_ACTIVATED, mean, stdev); + bool isInitOK = true; + const vpStatisticalTestAbstract::vpMeanDriftType EXPECTED_DRIFT_INIT = vpStatisticalTestAbstract::MEAN_DRIFT_NONE; + vpStatisticalTestAbstract::vpMeanDriftType drift; + unsigned int i = 0; + while ((i < vpStatisticalTestShewhart::NB_DATA_SIGNAL - 1) && isInitOK) { + drift = tester.testDownUpwardMeanDrift(mean); + isInitOK = (drift == EXPECTED_DRIFT_INIT); + if (isInitOK) { + ++i; + } + } + if (!isInitOK) { + if (verbose) { + std::cerr << "\t" << testName << " test initialization failed: " << std::endl; + std::cerr << "\t\ts(t) = " << tester.getSignal() << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\t\tlim_- = " << limitDown << std::endl; + std::cerr << "\t\tlim_+ = " << limitUp << std::endl; + std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT_INIT) << std::endl; + } + } + return isInitOK; +} + +void usage(const char *name) +{ + std::cout << "SYNOPSIS " << std::endl; + std::cout << name << "[--mean ] [--stdev ] [-v, --verbose] [-h, --help]" << std::endl; + std::cout << "\nOPTIONS" << std::endl; + std::cout << " --mean" << std::endl; + std::cout << " Permits to set the mean of the input signal." << std::endl; + std::cout << std::endl; + std::cout << " --stdev" << std::endl; + std::cout << " Permits to set the standard deviation of the input signal." << std::endl; + std::cout << std::endl; + std::cout << " -v, --verbose" << std::endl; + std::cout << " Activate verbose mode." << std::endl; + std::cout << std::endl; + std::cout << " -h, --help" << std::endl; + std::cout << " Display the help." << std::endl; + std::cout << std::endl; +} + +bool getOptions(int argc, const char **argv, float &opt_mean, float &opt_stdev, bool &opt_verbose) +{ + int i = 1; + while (i < argc) { + std::string argname(argv[i]); + if ((argname == "--mean") && ((i + 1) < argc)) { + opt_mean = std::atof(argv[i + 1]); + ++i; + } + else if ((argname == "--stdev") && ((i + 1) < argc)) { + opt_stdev = std::atof(argv[i + 1]); + ++i; + } + else if ((argname == "-v") || (argname == "--verbose")) { + opt_verbose = true; + } + else if ((argname == "-h") || (argname == "--help")) { + usage(argv[0]); + return false; + } + else if ((argname == "-c") || (argname == "-d")) { + // Arguments given by CTest by default, do nothing + } + else { + usage(argv[0]); + std::cerr << "Error: unrecognised argument \"" << argv[i] << "\"" << std::endl; + return false; + } + ++i; + } + return true; +} + +int main(int argc, const char **argv) +{ + float opt_mean = 0.f; + float opt_stdev = 1.f; + bool opt_verbose = false; + + bool isParsingOk = getOptions(argc, argv, opt_mean, opt_stdev, opt_verbose); + if (!isParsingOk) { + return EXIT_FAILURE; + } + + bool success = true; + + // vpStatisticalTestEWMA tests + { + if (opt_verbose) { + std::cout << "------ vpStatisticalTestEWMA tests ------" << std::endl; + } + const float alpha = 0.1f; + vpStatisticalTestEWMA tester(alpha); + + // ---- Upward drift test ---- + { + tester.init(alpha, opt_mean, opt_stdev); + + // w(t = 1) >= mu + 3 sigma sqrt(alpha / (2 - alpha)) + // <=> alpha s(t=1) + (1 - alpha) mu >= mu + 3 sigma sqrt(alpha / (2 - alpha)) + // <=> s(t=1) >= (1 / alpha) (alpha mu + 3 sigma sqrt(alpha / (2 - alpha)) + float signal = (1.f / alpha) * (alpha * opt_mean + 3.f * opt_stdev * std::sqrt(alpha / (2.f - alpha))); + signal += 0.5f; // To be sure we are greater than the threshold + vpStatisticalTestAbstract::vpMeanDriftType drift = tester.testDownUpwardMeanDrift(signal); + if (drift != vpStatisticalTestAbstract::MEAN_DRIFT_UPWARD) { + success = false; + if (opt_verbose) { + std::cerr << "Upward drift test failed: " << std::endl; + std::cerr << "\tw(t) = " << tester.getWt() << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\tlimit_up = " << limitUp << std::endl; + } + } + else if (opt_verbose) { + std::cout << "Upward drift test succeeded." << std::endl; + } + } + + // ---- Downward drift test ---- + { + tester.init(alpha, opt_mean, opt_stdev); + // w(t = 1) <= mu - 3 sigma sqrt(alpha / (2 - alpha)) + // <=> alpha s(t=1) + (1 - alpha) mu <= mu - 3 sigma sqrt(alpha / (2 - alpha)) + // <=> s(t=1) <= (1 / alpha) (alpha mu - 3 sigma sqrt(alpha / (2 - alpha)) + float signal = (1.f / alpha) * (alpha * opt_mean - 3.f * opt_stdev * std::sqrt(alpha / (2.f - alpha))); + signal -= 0.5f; // To be sure we are greater than the threshold + vpStatisticalTestAbstract::vpMeanDriftType drift = tester.testDownUpwardMeanDrift(signal); + if (drift != vpStatisticalTestAbstract::MEAN_DRIFT_DOWNWARD) { + success = false; + if (opt_verbose) { + std::cerr << "Downward drift test failed: " << std::endl; + std::cerr << "\tw(t) = " << tester.getWt() << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\tlimit_down = " << limitDown << std::endl; + } + } + else if (opt_verbose) { + std::cout << "Downward drift test succeeded." << std::endl; + } + } + } + + // vpStatisticalTestHinkley tests + { + if (opt_verbose) { + std::cout << "------ vpStatisticalTestHinkley tests ------" << std::endl; + } + + const float h = 4.76f, k = 1.f; + vpStatisticalTestHinkley tester(h, k, opt_mean, opt_stdev); + const unsigned int HINKLEY_NB_DATA = 4; + const float HINKLEY_SAMPLE = 2.f * opt_stdev; + + // Hinkley's test upward drift + { + const float HINKLEY_DATA[HINKLEY_NB_DATA] = { HINKLEY_SAMPLE, HINKLEY_SAMPLE, HINKLEY_SAMPLE, HINKLEY_SAMPLE }; + const vpStatisticalTestAbstract::vpMeanDriftType HINKLEY_EXPECTED_RES[HINKLEY_NB_DATA] = { vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_UPWARD }; + bool isTestOk = true; + unsigned int id = 0; + vpStatisticalTestAbstract::vpMeanDriftType drift; + while ((id < HINKLEY_NB_DATA) && isTestOk) { + drift = tester.testDownUpwardMeanDrift(HINKLEY_DATA[id]); + isTestOk = (drift == HINKLEY_EXPECTED_RES[id]); + if (isTestOk) { + ++id; + } + } + if (!isTestOk) { + success = false; + if (opt_verbose) { + std::cerr << "Upward drift test failed: " << std::endl; + float Tk = tester.getTk(), Nk = tester.getNk(); + std::cerr << "T(k) = " << Tk << " | N(k) = " << Nk << " => S+(k) = " << Tk - Nk << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "lim_+ = " << limitUp << std::endl; + std::cerr << "drift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "expected drift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(HINKLEY_EXPECTED_RES[id]) << std::endl; + } + } + else if (opt_verbose) { + std::cout << "Upward drift test succeeded." << std::endl; + } + } + + // Hinkley's test downward drift + { + const float HINKLEY_DATA[HINKLEY_NB_DATA] = { -1.f * HINKLEY_SAMPLE, -1.f * HINKLEY_SAMPLE, -1.f * HINKLEY_SAMPLE, -1.f * HINKLEY_SAMPLE }; + const vpStatisticalTestAbstract::vpMeanDriftType HINKLEY_EXPECTED_RES[HINKLEY_NB_DATA] = { vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_DOWNWARD }; + bool isTestOk = true; + unsigned int id = 0; + vpStatisticalTestAbstract::vpMeanDriftType drift; + while ((id < HINKLEY_NB_DATA) && isTestOk) { + drift = tester.testDownUpwardMeanDrift(HINKLEY_DATA[id]); + isTestOk = (drift == HINKLEY_EXPECTED_RES[id]); + if (isTestOk) { + ++id; + } + } + if (!isTestOk) { + success = false; + if (opt_verbose) { + std::cerr << "Downward drift test failed: " << std::endl; + float Sk = tester.getSk(), Mk = tester.getMk(); + std::cerr << "S(k) = " << Sk << " | M(k) = " << Mk << " => S+(k) = " << Mk - Sk << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "lim_- = " << limitDown << std::endl; + std::cerr << "drift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "expected drift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(HINKLEY_EXPECTED_RES[id]) << std::endl; + } + } + else if (opt_verbose) { + std::cout << "Downward drift test succeeded." << std::endl; + } + } + } + + // vpStatisticalTestMeanAdjustedCUSUM tests + { + if (opt_verbose) { + std::cout << "------ vpStatisticalTestMeanAdjustedCUSUM tests ------" << std::endl; + } + + const float h = 4.76f, k = 1.f; + vpStatisticalTestMeanAdjustedCUSUM tester(h, k); + tester.init(h, k, opt_mean, opt_stdev); + const unsigned int CUSUM_NB_DATA = 4; + const float CUSUM_SAMPLE = 2.f * opt_stdev; + + // Mean adjusted CUSUM test upward drift + { + const float CUSUM_DATA[CUSUM_NB_DATA] = { CUSUM_SAMPLE, CUSUM_SAMPLE, CUSUM_SAMPLE, CUSUM_SAMPLE }; + const vpStatisticalTestAbstract::vpMeanDriftType CUSUM_EXPECTED_RES[CUSUM_NB_DATA] = { vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_UPWARD }; + bool isTestOk = true; + unsigned int id = 0; + vpStatisticalTestAbstract::vpMeanDriftType drift; + while ((id < CUSUM_NB_DATA) && isTestOk) { + drift = tester.testDownUpwardMeanDrift(CUSUM_DATA[id]); + isTestOk = (drift == CUSUM_EXPECTED_RES[id]); + if (isTestOk) { + ++id; + } + } + if (!isTestOk) { + success = false; + if (opt_verbose) { + std::cerr << "Upward drift test failed: " << std::endl; + std::cerr << "\tS+(k) = " << tester.getTestSignalPlus() << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\tlim_+ = " << limitUp << std::endl; + std::cerr << "\tdrift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "\texpected drift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(CUSUM_EXPECTED_RES[id]) << std::endl; + } + } + else if (opt_verbose) { + std::cout << "Upward drift test succeeded." << std::endl; + } + } + + // Mean adjusted CUSUM test upward drift + { + const float CUSUM_DATA[CUSUM_NB_DATA] = { -1.f * CUSUM_SAMPLE, -1.f * CUSUM_SAMPLE, -1.f * CUSUM_SAMPLE, -1.f * CUSUM_SAMPLE }; + const vpStatisticalTestAbstract::vpMeanDriftType CUSUM_EXPECTED_RES[CUSUM_NB_DATA] = { vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_DOWNWARD }; + bool isTestOk = true; + unsigned int id = 0; + vpStatisticalTestAbstract::vpMeanDriftType drift; + while ((id < CUSUM_NB_DATA) && isTestOk) { + drift = tester.testDownUpwardMeanDrift(CUSUM_DATA[id]); + isTestOk = (drift == CUSUM_EXPECTED_RES[id]); + if (isTestOk) { + ++id; + } + } + if (!isTestOk) { + success = false; + if (opt_verbose) { + std::cerr << "Downward drift test failed: " << std::endl; + std::cerr << "\tS-(k) = " << tester.getTestSignalMinus() << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\tlim_- = " << limitDown << std::endl; + std::cerr << "\tdrift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "\texpected drift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(CUSUM_EXPECTED_RES[id]) << std::endl; + } + } + else if (opt_verbose) { + std::cout << "Downward drift test succeeded." << std::endl; + } + } + } + + // vpStatisticalTestShewhart tests + { + if (opt_verbose) { + std::cout << "------ vpStatisticalTestShewhart tests ------" << std::endl; + } + const bool activateWECOrules = true; + vpStatisticalTestShewhart tester(activateWECOrules, vpStatisticalTestShewhart::CONST_ALL_WECO_ACTIVATED, opt_mean, opt_stdev); + + // Upward drift test + { + if (opt_verbose) { + std::cout << "Upward drift tests" << std::endl; + } + + // 3-sigma test + { + bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "3-sigma", tester); + if (!isInitOK) { + success = false; + } + else { + const float signal = 3.5f * opt_stdev; + vpStatisticalTestAbstract::vpMeanDriftType drift = tester.testDownUpwardMeanDrift(signal); + vpStatisticalTestShewhart::vpWecoRulesAlarm alarm = tester.getAlarm(); + const vpStatisticalTestAbstract::vpMeanDriftType EXPECTED_DRIFT = vpStatisticalTestAbstract::MEAN_DRIFT_UPWARD; + const vpStatisticalTestShewhart::vpWecoRulesAlarm EXPECTED_ALARM = vpStatisticalTestShewhart::THREE_SIGMA_WECO; + if ((drift != EXPECTED_DRIFT) || (alarm != EXPECTED_ALARM)) { + success = false; + if (opt_verbose) { + std::cerr << "\t3-sigma test failed: " << std::endl; + std::vector s = tester.getSignals(); + std::cerr << "\t\ts(t) = [ "; + for (size_t i = 0; i < s.size(); ++i) { + std::cerr << s[i] << " "; + } + std::cerr << "]" << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\t\tlim_+ = " << limitUp << std::endl; + std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT) << std::endl; + std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl; + std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM) << std::endl; + } + } + else if (opt_verbose) { + std::cout << "\t3-sigma test succeeded " << std::endl; + } + } + } + + // 2-sigma test + { + bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "2-sigma", tester); + if (!isInitOK) { + success = false; + } + else { + const unsigned int NB_SAMPLES = 3; + const float DATA[NB_SAMPLES] = { 2.75f * opt_stdev, 1.5f * opt_stdev, 2.5f * opt_stdev }; + const vpStatisticalTestAbstract::vpMeanDriftType EXPECTED_DRIFT[NB_SAMPLES] = { vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_UPWARD }; + const vpStatisticalTestShewhart::vpWecoRulesAlarm EXPECTED_ALARM[NB_SAMPLES] = { vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::TWO_SIGMA_WECO }; + vpStatisticalTestAbstract::vpMeanDriftType drift; + vpStatisticalTestShewhart::vpWecoRulesAlarm alarm = tester.getAlarm(); + unsigned int i = 0; + bool isTestOk = true; + while ((i < NB_SAMPLES) && isTestOk) { + drift = tester.testDownUpwardMeanDrift(DATA[i]); + alarm = tester.getAlarm(); + isTestOk = ((drift == EXPECTED_DRIFT[i]) && (alarm == EXPECTED_ALARM[i])); + if (isTestOk) { + ++i; + } + } + + if (!isTestOk) { + success = false; + if (opt_verbose) { + std::cerr << "\t2-sigma test failed: " << std::endl; + std::vector s = tester.getSignals(); + std::cerr << "\t\ts(t) = [ "; + for (size_t i = 0; i < s.size(); ++i) { + std::cerr << s[i] << " "; + } + std::cerr << "]" << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\t\tlim_+ = " << limitUp << std::endl; + std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT[i]) << std::endl; + std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl; + std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM[i]) << std::endl; + } + } + else if (opt_verbose) { + std::cout << "\t2-sigma test succeeded " << std::endl; + } + } + } + + // 1-sigma test + { + bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "1-sigma", tester); + if (!isInitOK) { + success = false; + } + else { + const unsigned int NB_SAMPLES = 5; + const float DATA[NB_SAMPLES] = { 2.75f * opt_stdev, 1.5f * opt_stdev, 0.5f * opt_stdev, 1.5f * opt_stdev, 2.5f * opt_stdev }; + const vpStatisticalTestAbstract::vpMeanDriftType EXPECTED_DRIFT[NB_SAMPLES] = { vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_UPWARD }; + const vpStatisticalTestShewhart::vpWecoRulesAlarm EXPECTED_ALARM[NB_SAMPLES] = { vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::ONE_SIGMA_WECO }; + vpStatisticalTestAbstract::vpMeanDriftType drift; + vpStatisticalTestShewhart::vpWecoRulesAlarm alarm = tester.getAlarm(); + unsigned int i = 0; + bool isTestOk = true; + while ((i < NB_SAMPLES) && isTestOk) { + drift = tester.testDownUpwardMeanDrift(DATA[i]); + alarm = tester.getAlarm(); + isTestOk = ((drift == EXPECTED_DRIFT[i]) && (alarm == EXPECTED_ALARM[i])); + if (isTestOk) { + ++i; + } + } + + if (!isTestOk) { + success = false; + if (opt_verbose) { + std::cerr << "\t1-sigma test failed: " << std::endl; + std::vector s = tester.getSignals(); + std::cerr << "\t\ts(t) = [ "; + for (size_t i = 0; i < s.size(); ++i) { + std::cerr << s[i] << " "; + } + std::cerr << "]" << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\t\tlim_+ = " << limitUp << std::endl; + std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT[i]) << std::endl; + std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl; + std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM[i]) << std::endl; + } + } + else if (opt_verbose) { + std::cout << "\t1-sigma test succeeded " << std::endl; + } + } + } + + // Same-side test + { + bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "Same-side", tester); + if (!isInitOK) { + success = false; + } + else { + const unsigned int NB_SAMPLES = 8; + const float DATA[NB_SAMPLES] = { 2.75f * opt_stdev, 0.5f * opt_stdev, 1.5f * opt_stdev, 0.5f * opt_stdev, 2.75f * opt_stdev, 0.5f * opt_stdev, 1.5f * opt_stdev, 0.5f * opt_stdev }; + const vpStatisticalTestAbstract::vpMeanDriftType EXPECTED_DRIFT[NB_SAMPLES] = { vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_UPWARD }; + const vpStatisticalTestShewhart::vpWecoRulesAlarm EXPECTED_ALARM[NB_SAMPLES] = { vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::SAME_SIDE_WECO }; + vpStatisticalTestAbstract::vpMeanDriftType drift; + vpStatisticalTestShewhart::vpWecoRulesAlarm alarm = tester.getAlarm(); + unsigned int i = 0; + bool isTestOk = true; + while ((i < NB_SAMPLES) && isTestOk) { + drift = tester.testDownUpwardMeanDrift(DATA[i]); + alarm = tester.getAlarm(); + isTestOk = ((drift == EXPECTED_DRIFT[i]) && (alarm == EXPECTED_ALARM[i])); + if (isTestOk) { + ++i; + } + } + + if (!isTestOk) { + success = false; + if (opt_verbose) { + std::cerr << "\tSame-side test failed: " << std::endl; + std::vector s = tester.getSignals(); + std::cerr << "\t\ts(t) = [ "; + for (size_t i = 0; i < s.size(); ++i) { + std::cerr << s[i] << " "; + } + std::cerr << "]" << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\t\tlim_+ = " << limitUp << std::endl; + std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT[i]) << std::endl; + std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl; + std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM[i]) << std::endl; + } + } + else if (opt_verbose) { + std::cout << "\tSame-side test succeeded " << std::endl; + } + } + } + } + + // Downward drift test + { + if (opt_verbose) { + std::cout << "Downward drift tests" << std::endl; + } + + // 3-sigma test + { + bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "3-sigma", tester); + if (!isInitOK) { + success = false; + } + else { + const float signal = -3.5f * opt_stdev; + vpStatisticalTestAbstract::vpMeanDriftType drift = tester.testDownUpwardMeanDrift(signal); + vpStatisticalTestShewhart::vpWecoRulesAlarm alarm = tester.getAlarm(); + const vpStatisticalTestAbstract::vpMeanDriftType EXPECTED_DRIFT = vpStatisticalTestAbstract::MEAN_DRIFT_DOWNWARD; + const vpStatisticalTestShewhart::vpWecoRulesAlarm EXPECTED_ALARM = vpStatisticalTestShewhart::THREE_SIGMA_WECO; + if ((drift != EXPECTED_DRIFT) || (alarm != EXPECTED_ALARM)) { + success = false; + if (opt_verbose) { + std::cerr << "\t3-sigma test failed: " << std::endl; + std::vector s = tester.getSignals(); + std::cerr << "\t\ts(t) = [ "; + for (size_t i = 0; i < s.size(); ++i) { + std::cerr << s[i] << " "; + } + std::cerr << "]" << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\t\tlim_- = " << limitDown << std::endl; + std::cerr << "\t\tlim_+ = " << limitUp << std::endl; + std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT) << std::endl; + std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl; + std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM) << std::endl; + } + } + else if (opt_verbose) { + std::cout << "\t3-sigma test succeeded " << std::endl; + } + } + } + + // 2-sigma test + { + bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "2-sigma", tester); + if (!isInitOK) { + success = false; + } + else { + const unsigned int NB_SAMPLES = 3; + const float DATA[NB_SAMPLES] = { -2.75f * opt_stdev, -1.5f * opt_stdev, -2.5f * opt_stdev }; + const vpStatisticalTestAbstract::vpMeanDriftType EXPECTED_DRIFT[NB_SAMPLES] = { vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_DOWNWARD }; + const vpStatisticalTestShewhart::vpWecoRulesAlarm EXPECTED_ALARM[NB_SAMPLES] = { vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::TWO_SIGMA_WECO }; + vpStatisticalTestAbstract::vpMeanDriftType drift; + vpStatisticalTestShewhart::vpWecoRulesAlarm alarm = tester.getAlarm(); + unsigned int i = 0; + bool isTestOk = true; + while ((i < NB_SAMPLES) && isTestOk) { + drift = tester.testDownUpwardMeanDrift(DATA[i]); + alarm = tester.getAlarm(); + isTestOk = ((drift == EXPECTED_DRIFT[i]) && (alarm == EXPECTED_ALARM[i])); + if (isTestOk) { + ++i; + } + } + + if (!isTestOk) { + success = false; + if (opt_verbose) { + std::cerr << "\t2-sigma test failed: " << std::endl; + std::vector s = tester.getSignals(); + std::cerr << "\t\ts(t) = [ "; + for (size_t i = 0; i < s.size(); ++i) { + std::cerr << s[i] << " "; + } + std::cerr << "]" << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\t\tlim_- = " << limitDown << std::endl; + std::cerr << "\t\tlim_+ = " << limitUp << std::endl; + std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT[i]) << std::endl; + std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl; + std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM[i]) << std::endl; + } + } + else if (opt_verbose) { + std::cout << "\t2-sigma test succeeded " << std::endl; + } + } + } + + // 1-sigma test + { + bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "1-sigma", tester); + if (!isInitOK) { + success = false; + } + else { + const unsigned int NB_SAMPLES = 5; + const float DATA[NB_SAMPLES] = { -2.75f * opt_stdev, -1.5f * opt_stdev, 1.5f * opt_stdev, -1.5f * opt_stdev, -2.5f * opt_stdev }; + const vpStatisticalTestAbstract::vpMeanDriftType EXPECTED_DRIFT[NB_SAMPLES] = { vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_DOWNWARD }; + const vpStatisticalTestShewhart::vpWecoRulesAlarm EXPECTED_ALARM[NB_SAMPLES] = { vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::ONE_SIGMA_WECO }; + vpStatisticalTestAbstract::vpMeanDriftType drift; + vpStatisticalTestShewhart::vpWecoRulesAlarm alarm = tester.getAlarm(); + unsigned int i = 0; + bool isTestOk = true; + while ((i < NB_SAMPLES) && isTestOk) { + drift = tester.testDownUpwardMeanDrift(DATA[i]); + alarm = tester.getAlarm(); + isTestOk = ((drift == EXPECTED_DRIFT[i]) && (alarm == EXPECTED_ALARM[i])); + if (isTestOk) { + ++i; + } + } + + if (!isTestOk) { + success = false; + if (opt_verbose) { + std::cerr << "\t1-sigma test failed: " << std::endl; + std::vector s = tester.getSignals(); + std::cerr << "\t\ts(t) = [ "; + for (size_t i = 0; i < s.size(); ++i) { + std::cerr << s[i] << " "; + } + std::cerr << "]" << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\t\tlim_- = " << limitDown << std::endl; + std::cerr << "\t\tlim_+ = " << limitUp << std::endl; + std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT[i]) << std::endl; + std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl; + std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM[i]) << std::endl; + } + } + else if (opt_verbose) { + std::cout << "\t1-sigma test succeeded " << std::endl; + } + } + } + + // Same-side test + { + bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "Same-side", tester); + if (!isInitOK) { + success = false; + } + else { + const unsigned int NB_SAMPLES = 8; + const float DATA[NB_SAMPLES] = { -2.75f * opt_stdev, -0.5f * opt_stdev, -1.5f * opt_stdev, -0.5f * opt_stdev, -2.75f * opt_stdev, -0.5f * opt_stdev, -1.5f * opt_stdev, -0.5f * opt_stdev }; + const vpStatisticalTestAbstract::vpMeanDriftType EXPECTED_DRIFT[NB_SAMPLES] = { vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_NONE, vpStatisticalTestAbstract::MEAN_DRIFT_DOWNWARD }; + const vpStatisticalTestShewhart::vpWecoRulesAlarm EXPECTED_ALARM[NB_SAMPLES] = { vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::NONE_WECO, vpStatisticalTestShewhart::SAME_SIDE_WECO }; + vpStatisticalTestAbstract::vpMeanDriftType drift; + vpStatisticalTestShewhart::vpWecoRulesAlarm alarm = tester.getAlarm(); + unsigned int i = 0; + bool isTestOk = true; + while ((i < NB_SAMPLES) && isTestOk) { + drift = tester.testDownUpwardMeanDrift(DATA[i]); + alarm = tester.getAlarm(); + isTestOk = ((drift == EXPECTED_DRIFT[i]) && (alarm == EXPECTED_ALARM[i])); + if (isTestOk) { + ++i; + } + } + + if (!isTestOk) { + success = false; + if (opt_verbose) { + std::cerr << "\tSame-side test failed: " << std::endl; + std::vector s = tester.getSignals(); + std::cerr << "\t\ts(t) = [ "; + for (size_t i = 0; i < s.size(); ++i) { + std::cerr << s[i] << " "; + } + std::cerr << "]" << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\t\tlim_+ = " << limitUp << std::endl; + std::cerr << "\t\tlim_- = " << limitDown << std::endl; + std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT[i]) << std::endl; + std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl; + std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM[i]) << std::endl; + } + } + else if (opt_verbose) { + std::cout << "\tSame-side test succeeded " << std::endl; + } + } + } + } + } + + // vpStatisticalTestSigma tests + { + if (opt_verbose) { + std::cout << "------ vpStatisticalTestSigma tests ------" << std::endl; + } + const float h = 3.f; + vpStatisticalTestSigma tester(h, opt_mean, opt_stdev); + + // Upward drift test + { + const float signal = 3.5f * opt_stdev; + vpStatisticalTestAbstract::vpMeanDriftType drift = tester.testDownUpwardMeanDrift(signal); + const vpStatisticalTestAbstract::vpMeanDriftType EXPECTED_DRIFT = vpStatisticalTestAbstract::MEAN_DRIFT_UPWARD; + if (drift != EXPECTED_DRIFT) { + success = false; + if (opt_verbose) { + std::cerr << "Upward drift test failed: " << std::endl; + std::cerr << "\ts(t) = " << tester.getSignal() << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\tlim_+ = " << limitUp << std::endl; + std::cerr << "\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT) << std::endl; + } + } + else if (opt_verbose) { + std::cout << "Upward drift test succeeded " << std::endl; + } + } + + // Downward drift test + { + const float signal = -3.5f * opt_stdev; + vpStatisticalTestAbstract::vpMeanDriftType drift = tester.testDownUpwardMeanDrift(signal); + const vpStatisticalTestAbstract::vpMeanDriftType EXPECTED_DRIFT = vpStatisticalTestAbstract::MEAN_DRIFT_DOWNWARD; + if (drift != EXPECTED_DRIFT) { + success = false; + if (opt_verbose) { + std::cerr << "Downward drift test failed: " << std::endl; + std::cerr << "\ts(t) = " << tester.getSignal() << std::endl; + float limitDown = 0.f, limitUp = 0.f; + tester.getLimits(limitDown, limitUp); + std::cerr << "\tlim_- = " << limitDown << std::endl; + std::cerr << "\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl; + std::cerr << "\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT) << std::endl; + } + } + else if (opt_verbose) { + std::cout << "Downward drift test succeeded " << std::endl; + } + } + } + + if (success) { + std::cout << "Test succeed" << std::endl; + } + else { + std::cout << "Test failed" << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/tutorial/mean-drift/tutorial-meandrift.cpp b/tutorial/mean-drift/tutorial-meandrift.cpp index 181e94906d..945f312678 100644 --- a/tutorial/mean-drift/tutorial-meandrift.cpp +++ b/tutorial/mean-drift/tutorial-meandrift.cpp @@ -43,6 +43,7 @@ #include #include +#if defined(VISP_HAVE_DISPLAY) namespace TutorialMeanDrift { //! [Enum_For_Test_Choice] @@ -695,3 +696,10 @@ int main(int argc, char *argv[]) return testOnSynthetic(opt_typeTest, parameters, opt_mean, opt_meandrift, opt_stdev); } +#else +int main() +{ + std::cerr << "Recompile ViSP with display capabilities in order to use this tutorial." << std::endl; + return EXIT_FAILURE; +} +#endif