Test various Statistical Process Control methods.
#include <iostream>
#include <string>
#include <visp3/core/vpStatisticalTestEWMA.h>
#include <visp3/core/vpStatisticalTestHinkley.h>
#include <visp3/core/vpStatisticalTestMeanAdjustedCUSUM.h>
#include <visp3/core/vpStatisticalTestShewhart.h>
#include <visp3/core/vpStatisticalTestSigma.h>
#ifdef ENABLE_VISP_NAMESPACE
#endif
bool initializeShewhartTest(
const float &mean,
const float &stdev,
const bool &verbose,
const std::string &testName,
vpStatisticalTestShewhart &tester)
{
const bool activateWECOrules = true;
bool isInitOK = true;
unsigned int i = 0;
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;
std::cerr << "\t\tlim_- = " << limitDown << std::endl;
std::cerr << "\t\tlim_+ = " << limitUp << std::endl;
}
}
return isInitOK;
}
void usage(const char *name)
{
std::cout << "SYNOPSIS " << std::endl;
std::cout << name << "[--mean <value>] [--stdev <value>] [-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 = static_cast<float>(std::atof(argv[i + 1]));
++i;
}
else if ((argname == "--stdev") && ((i + 1) < argc)) {
opt_stdev = static_cast<float>(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")) {
}
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;
{
if (opt_verbose) {
std::cout << "------ vpStatisticalTestEWMA tests ------" << std::endl;
}
const float alpha = 0.1f;
{
tester.init(alpha, opt_mean, opt_stdev);
float signal = (1.f / alpha) * (alpha * opt_mean + 3.f * opt_stdev * std::sqrt(alpha / (2.f - alpha)));
signal += 0.5f;
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;
}
}
{
tester.init(alpha, opt_mean, opt_stdev);
float signal = (1.f / alpha) * (alpha * opt_mean - 3.f * opt_stdev * std::sqrt(alpha / (2.f - alpha)));
signal -= 0.5f;
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;
}
}
}
{
if (opt_verbose) {
std::cout << "------ vpStatisticalTestHinkley tests ------" << std::endl;
}
const float h = 4.76f, k = 1.f;
const unsigned int HINKLEY_NB_DATA = 4;
const float HINKLEY_SAMPLE = 2.f * opt_stdev;
{
const float HINKLEY_DATA[HINKLEY_NB_DATA] = { HINKLEY_SAMPLE, HINKLEY_SAMPLE, HINKLEY_SAMPLE, HINKLEY_SAMPLE };
bool isTestOk = true;
unsigned int id = 0;
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;
}
}
else if (opt_verbose) {
std::cout << "Upward drift test succeeded." << std::endl;
}
}
{
const float HINKLEY_DATA[HINKLEY_NB_DATA] = { -1.f * HINKLEY_SAMPLE, -1.f * HINKLEY_SAMPLE, -1.f * HINKLEY_SAMPLE, -1.f * HINKLEY_SAMPLE };
bool isTestOk = true;
unsigned int id = 0;
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;
}
}
else if (opt_verbose) {
std::cout << "Downward drift test succeeded." << std::endl;
}
}
}
{
if (opt_verbose) {
std::cout << "------ vpStatisticalTestMeanAdjustedCUSUM tests ------" << std::endl;
}
const float h = 4.76f, k = 1.f;
tester.init(h, k, opt_mean, opt_stdev);
const unsigned int CUSUM_NB_DATA = 4;
const float CUSUM_SAMPLE = 2.f * opt_stdev;
{
const float CUSUM_DATA[CUSUM_NB_DATA] = { CUSUM_SAMPLE, CUSUM_SAMPLE, CUSUM_SAMPLE, CUSUM_SAMPLE };
bool isTestOk = true;
unsigned int id = 0;
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;
}
}
else if (opt_verbose) {
std::cout << "Upward drift test succeeded." << std::endl;
}
}
{
const float CUSUM_DATA[CUSUM_NB_DATA] = { -1.f * CUSUM_SAMPLE, -1.f * CUSUM_SAMPLE, -1.f * CUSUM_SAMPLE, -1.f * CUSUM_SAMPLE };
bool isTestOk = true;
unsigned int id = 0;
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;
}
}
else if (opt_verbose) {
std::cout << "Downward drift test succeeded." << std::endl;
}
}
}
{
if (opt_verbose) {
std::cout << "------ vpStatisticalTestShewhart tests ------" << std::endl;
}
const bool activateWECOrules = true;
{
if (opt_verbose) {
std::cout << "Upward drift tests" << std::endl;
}
{
bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "3-sigma", tester);
if (!isInitOK) {
success = false;
}
else {
const float signal = 3.5f * opt_stdev;
if ((drift != EXPECTED_DRIFT) || (alarm != EXPECTED_ALARM)) {
success = false;
if (opt_verbose) {
std::cerr << "\t3-sigma test failed: " << std::endl;
std::vector<float> 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;
}
}
else if (opt_verbose) {
std::cout << "\t3-sigma test succeeded " << std::endl;
}
}
}
{
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 };
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<float> s = tester.getSignals();
std::cerr << "\t\ts(t) = [ ";
for (size_t j = 0; j < s.size(); ++j) {
std::cerr << s[j] << " ";
}
std::cerr << "]" << std::endl;
float limitDown = 0.f, limitUp = 0.f;
tester.getLimits(limitDown, limitUp);
std::cerr << "\t\tlim_+ = " << limitUp << std::endl;
}
}
else if (opt_verbose) {
std::cout << "\t2-sigma test succeeded " << std::endl;
}
}
}
{
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 };
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<float> s = tester.getSignals();
std::cerr << "\t\ts(t) = [ ";
for (size_t j = 0; j < s.size(); ++j) {
std::cerr << s[j] << " ";
}
std::cerr << "]" << std::endl;
float limitDown = 0.f, limitUp = 0.f;
tester.getLimits(limitDown, limitUp);
std::cerr << "\t\tlim_+ = " << limitUp << std::endl;
}
}
else if (opt_verbose) {
std::cout << "\t1-sigma test succeeded " << std::endl;
}
}
}
{
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 };
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<float> s = tester.getSignals();
std::cerr << "\t\ts(t) = [ ";
for (size_t j = 0; j < s.size(); ++j) {
std::cerr << s[j] << " ";
}
std::cerr << "]" << std::endl;
float limitDown = 0.f, limitUp = 0.f;
tester.getLimits(limitDown, limitUp);
std::cerr << "\t\tlim_+ = " << limitUp << std::endl;
}
}
else if (opt_verbose) {
std::cout << "\tSame-side test succeeded " << std::endl;
}
}
}
}
{
if (opt_verbose) {
std::cout << "Downward drift tests" << std::endl;
}
{
bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "3-sigma", tester);
if (!isInitOK) {
success = false;
}
else {
const float signal = -3.5f * opt_stdev;
if ((drift != EXPECTED_DRIFT) || (alarm != EXPECTED_ALARM)) {
success = false;
if (opt_verbose) {
std::cerr << "\t3-sigma test failed: " << std::endl;
std::vector<float> s = tester.getSignals();
std::cerr << "\t\ts(t) = [ ";
for (size_t j = 0; j < s.size(); ++j) {
std::cerr << s[j] << " ";
}
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;
}
}
else if (opt_verbose) {
std::cout << "\t3-sigma test succeeded " << std::endl;
}
}
}
{
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 };
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<float> s = tester.getSignals();
std::cerr << "\t\ts(t) = [ ";
for (size_t j = 0; j < s.size(); ++j) {
std::cerr << s[j] << " ";
}
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;
}
}
else if (opt_verbose) {
std::cout << "\t2-sigma test succeeded " << std::endl;
}
}
}
{
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 };
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<float> s = tester.getSignals();
std::cerr << "\t\ts(t) = [ ";
for (size_t j = 0; j < s.size(); ++j) {
std::cerr << s[j] << " ";
}
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;
}
}
else if (opt_verbose) {
std::cout << "\t1-sigma test succeeded " << std::endl;
}
}
}
{
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 };
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<float> s = tester.getSignals();
std::cerr << "\t\ts(t) = [ ";
for (size_t j = 0; j < s.size(); ++j) {
std::cerr << s[j] << " ";
}
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;
}
}
else if (opt_verbose) {
std::cout << "\tSame-side test succeeded " << std::endl;
}
}
}
}
}
{
if (opt_verbose) {
std::cout << "------ vpStatisticalTestSigma tests ------" << std::endl;
}
const float h = 3.f;
{
const float signal = 3.5f * opt_stdev;
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;
}
}
else if (opt_verbose) {
std::cout << "Upward drift test succeeded " << std::endl;
}
}
{
const float signal = -3.5f * opt_stdev;
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;
}
}
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;
}
static std::string vpMeanDriftTypeToString(const vpMeanDriftType &type)
Cast a vpMeanDriftType into a string.
vpMeanDriftType
Enum that indicates if a drift of the mean occurred.
void getLimits(float &limitDown, float &limitUp) const
Get the upper and lower limits of the test signal.
vpMeanDriftType testDownUpwardMeanDrift(const float &signal)
Test if a downward or an upward mean drift occurred according to the new value of the signal.
Class that permits to perform Exponentially Weighted Moving Average mean drft tests.
This class implements the Hinkley's cumulative sum test.
Class that permits to perform a mean adjusted Cumulative Sum test.
Class that permits a Shewhart's test.
static const unsigned int NB_DATA_SIGNAL
void init(const bool &activateWECOrules, const bool activatedRules[COUNT_WECO - 1]=CONST_ALL_WECO_ACTIVATED, const unsigned int &nbSamplesForStats=30)
(Re)Initialize the test.
virtual float getSignal() const override
Get the last value of the signal.
static const bool CONST_ALL_WECO_ACTIVATED[COUNT_WECO - 1]
static std::string vpWecoRulesAlarmToString(const vpWecoRulesAlarm &alarm)
Class that permits a simple test comparing the current value to the standard deviation of the signal.