Visual Servoing Platform  version 3.6.1 under development (2024-10-09)
testSPC.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * Test various Statistical Process Control methods.
32  */
33 
39 #include <iostream>
40 #include <string>
41 
42 #include <visp3/core/vpStatisticalTestEWMA.h>
43 #include <visp3/core/vpStatisticalTestHinkley.h>
44 #include <visp3/core/vpStatisticalTestMeanAdjustedCUSUM.h>
45 #include <visp3/core/vpStatisticalTestShewhart.h>
46 #include <visp3/core/vpStatisticalTestSigma.h>
47 
48 #ifdef ENABLE_VISP_NAMESPACE
49 using namespace VISP_NAMESPACE_NAME;
50 #endif
51 
52 bool initializeShewhartTest(const float &mean, const float &stdev, const bool &verbose, const std::string &testName, vpStatisticalTestShewhart &tester)
53 {
54  const bool activateWECOrules = true;
55  tester.init(activateWECOrules, vpStatisticalTestShewhart::CONST_ALL_WECO_ACTIVATED, mean, stdev);
56  bool isInitOK = true;
59  unsigned int i = 0;
60  while ((i < vpStatisticalTestShewhart::NB_DATA_SIGNAL - 1) && isInitOK) {
61  drift = tester.testDownUpwardMeanDrift(mean);
62  isInitOK = (drift == EXPECTED_DRIFT_INIT);
63  if (isInitOK) {
64  ++i;
65  }
66  }
67  if (!isInitOK) {
68  if (verbose) {
69  std::cerr << "\t" << testName << " test initialization failed: " << std::endl;
70  std::cerr << "\t\ts(t) = " << tester.getSignal() << std::endl;
71  float limitDown = 0.f, limitUp = 0.f;
72  tester.getLimits(limitDown, limitUp);
73  std::cerr << "\t\tlim_- = " << limitDown << std::endl;
74  std::cerr << "\t\tlim_+ = " << limitUp << std::endl;
75  std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
76  std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT_INIT) << std::endl;
77  }
78  }
79  return isInitOK;
80 }
81 
82 void usage(const char *name)
83 {
84  std::cout << "SYNOPSIS " << std::endl;
85  std::cout << name << "[--mean <value>] [--stdev <value>] [-v, --verbose] [-h, --help]" << std::endl;
86  std::cout << "\nOPTIONS" << std::endl;
87  std::cout << " --mean" << std::endl;
88  std::cout << " Permits to set the mean of the input signal." << std::endl;
89  std::cout << std::endl;
90  std::cout << " --stdev" << std::endl;
91  std::cout << " Permits to set the standard deviation of the input signal." << std::endl;
92  std::cout << std::endl;
93  std::cout << " -v, --verbose" << std::endl;
94  std::cout << " Activate verbose mode." << std::endl;
95  std::cout << std::endl;
96  std::cout << " -h, --help" << std::endl;
97  std::cout << " Display the help." << std::endl;
98  std::cout << std::endl;
99 }
100 
101 bool getOptions(int argc, const char **argv, float &opt_mean, float &opt_stdev, bool &opt_verbose)
102 {
103  int i = 1;
104  while (i < argc) {
105  std::string argname(argv[i]);
106  if ((argname == "--mean") && ((i + 1) < argc)) {
107  opt_mean = static_cast<float>(std::atof(argv[i + 1]));
108  ++i;
109  }
110  else if ((argname == "--stdev") && ((i + 1) < argc)) {
111  opt_stdev = static_cast<float>(std::atof(argv[i + 1]));
112  ++i;
113  }
114  else if ((argname == "-v") || (argname == "--verbose")) {
115  opt_verbose = true;
116  }
117  else if ((argname == "-h") || (argname == "--help")) {
118  usage(argv[0]);
119  return false;
120  }
121  else if ((argname == "-c") || (argname == "-d")) {
122  // Arguments given by CTest by default, do nothing
123  }
124  else {
125  usage(argv[0]);
126  std::cerr << "Error: unrecognised argument \"" << argv[i] << "\"" << std::endl;
127  return false;
128  }
129  ++i;
130  }
131  return true;
132 }
133 
134 int main(int argc, const char **argv)
135 {
136  float opt_mean = 0.f;
137  float opt_stdev = 1.f;
138  bool opt_verbose = false;
139 
140  bool isParsingOk = getOptions(argc, argv, opt_mean, opt_stdev, opt_verbose);
141  if (!isParsingOk) {
142  return EXIT_FAILURE;
143  }
144 
145  bool success = true;
146 
147  // vpStatisticalTestEWMA tests
148  {
149  if (opt_verbose) {
150  std::cout << "------ vpStatisticalTestEWMA tests ------" << std::endl;
151  }
152  const float alpha = 0.1f;
153  vpStatisticalTestEWMA tester(alpha);
154 
155  // ---- Upward drift test ----
156  {
157  tester.init(alpha, opt_mean, opt_stdev);
158 
159  // w(t = 1) >= mu + 3 sigma sqrt(alpha / (2 - alpha))
160  // <=> alpha s(t=1) + (1 - alpha) mu >= mu + 3 sigma sqrt(alpha / (2 - alpha))
161  // <=> s(t=1) >= (1 / alpha) (alpha mu + 3 sigma sqrt(alpha / (2 - alpha))
162  float signal = (1.f / alpha) * (alpha * opt_mean + 3.f * opt_stdev * std::sqrt(alpha / (2.f - alpha)));
163  signal += 0.5f; // To be sure we are greater than the threshold
166  success = false;
167  if (opt_verbose) {
168  std::cerr << "Upward drift test failed: " << std::endl;
169  std::cerr << "\tw(t) = " << tester.getWt() << std::endl;
170  float limitDown = 0.f, limitUp = 0.f;
171  tester.getLimits(limitDown, limitUp);
172  std::cerr << "\tlimit_up = " << limitUp << std::endl;
173  }
174  }
175  else if (opt_verbose) {
176  std::cout << "Upward drift test succeeded." << std::endl;
177  }
178  }
179 
180  // ---- Downward drift test ----
181  {
182  tester.init(alpha, opt_mean, opt_stdev);
183  // w(t = 1) <= mu - 3 sigma sqrt(alpha / (2 - alpha))
184  // <=> alpha s(t=1) + (1 - alpha) mu <= mu - 3 sigma sqrt(alpha / (2 - alpha))
185  // <=> s(t=1) <= (1 / alpha) (alpha mu - 3 sigma sqrt(alpha / (2 - alpha))
186  float signal = (1.f / alpha) * (alpha * opt_mean - 3.f * opt_stdev * std::sqrt(alpha / (2.f - alpha)));
187  signal -= 0.5f; // To be sure we are greater than the threshold
190  success = false;
191  if (opt_verbose) {
192  std::cerr << "Downward drift test failed: " << std::endl;
193  std::cerr << "\tw(t) = " << tester.getWt() << std::endl;
194  float limitDown = 0.f, limitUp = 0.f;
195  tester.getLimits(limitDown, limitUp);
196  std::cerr << "\tlimit_down = " << limitDown << std::endl;
197  }
198  }
199  else if (opt_verbose) {
200  std::cout << "Downward drift test succeeded." << std::endl;
201  }
202  }
203  }
204 
205  // vpStatisticalTestHinkley tests
206  {
207  if (opt_verbose) {
208  std::cout << "------ vpStatisticalTestHinkley tests ------" << std::endl;
209  }
210 
211  const float h = 4.76f, k = 1.f;
212  vpStatisticalTestHinkley tester(h, k, opt_mean, opt_stdev);
213  const unsigned int HINKLEY_NB_DATA = 4;
214  const float HINKLEY_SAMPLE = 2.f * opt_stdev;
215 
216  // Hinkley's test upward drift
217  {
218  const float HINKLEY_DATA[HINKLEY_NB_DATA] = { HINKLEY_SAMPLE, HINKLEY_SAMPLE, HINKLEY_SAMPLE, HINKLEY_SAMPLE };
220  bool isTestOk = true;
221  unsigned int id = 0;
223  while ((id < HINKLEY_NB_DATA) && isTestOk) {
224  drift = tester.testDownUpwardMeanDrift(HINKLEY_DATA[id]);
225  isTestOk = (drift == HINKLEY_EXPECTED_RES[id]);
226  if (isTestOk) {
227  ++id;
228  }
229  }
230  if (!isTestOk) {
231  success = false;
232  if (opt_verbose) {
233  std::cerr << "Upward drift test failed: " << std::endl;
234  float Tk = tester.getTk(), Nk = tester.getNk();
235  std::cerr << "T(k) = " << Tk << " | N(k) = " << Nk << " => S+(k) = " << Tk - Nk << std::endl;
236  float limitDown = 0.f, limitUp = 0.f;
237  tester.getLimits(limitDown, limitUp);
238  std::cerr << "lim_+ = " << limitUp << std::endl;
239  std::cerr << "drift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
240  std::cerr << "expected drift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(HINKLEY_EXPECTED_RES[id]) << std::endl;
241  }
242  }
243  else if (opt_verbose) {
244  std::cout << "Upward drift test succeeded." << std::endl;
245  }
246  }
247 
248  // Hinkley's test downward drift
249  {
250  const float HINKLEY_DATA[HINKLEY_NB_DATA] = { -1.f * HINKLEY_SAMPLE, -1.f * HINKLEY_SAMPLE, -1.f * HINKLEY_SAMPLE, -1.f * HINKLEY_SAMPLE };
252  bool isTestOk = true;
253  unsigned int id = 0;
255  while ((id < HINKLEY_NB_DATA) && isTestOk) {
256  drift = tester.testDownUpwardMeanDrift(HINKLEY_DATA[id]);
257  isTestOk = (drift == HINKLEY_EXPECTED_RES[id]);
258  if (isTestOk) {
259  ++id;
260  }
261  }
262  if (!isTestOk) {
263  success = false;
264  if (opt_verbose) {
265  std::cerr << "Downward drift test failed: " << std::endl;
266  float Sk = tester.getSk(), Mk = tester.getMk();
267  std::cerr << "S(k) = " << Sk << " | M(k) = " << Mk << " => S+(k) = " << Mk - Sk << std::endl;
268  float limitDown = 0.f, limitUp = 0.f;
269  tester.getLimits(limitDown, limitUp);
270  std::cerr << "lim_- = " << limitDown << std::endl;
271  std::cerr << "drift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
272  std::cerr << "expected drift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(HINKLEY_EXPECTED_RES[id]) << std::endl;
273  }
274  }
275  else if (opt_verbose) {
276  std::cout << "Downward drift test succeeded." << std::endl;
277  }
278  }
279  }
280 
281  // vpStatisticalTestMeanAdjustedCUSUM tests
282  {
283  if (opt_verbose) {
284  std::cout << "------ vpStatisticalTestMeanAdjustedCUSUM tests ------" << std::endl;
285  }
286 
287  const float h = 4.76f, k = 1.f;
289  tester.init(h, k, opt_mean, opt_stdev);
290  const unsigned int CUSUM_NB_DATA = 4;
291  const float CUSUM_SAMPLE = 2.f * opt_stdev;
292 
293  // Mean adjusted CUSUM test upward drift
294  {
295  const float CUSUM_DATA[CUSUM_NB_DATA] = { CUSUM_SAMPLE, CUSUM_SAMPLE, CUSUM_SAMPLE, CUSUM_SAMPLE };
297  bool isTestOk = true;
298  unsigned int id = 0;
300  while ((id < CUSUM_NB_DATA) && isTestOk) {
301  drift = tester.testDownUpwardMeanDrift(CUSUM_DATA[id]);
302  isTestOk = (drift == CUSUM_EXPECTED_RES[id]);
303  if (isTestOk) {
304  ++id;
305  }
306  }
307  if (!isTestOk) {
308  success = false;
309  if (opt_verbose) {
310  std::cerr << "Upward drift test failed: " << std::endl;
311  std::cerr << "\tS+(k) = " << tester.getTestSignalPlus() << std::endl;
312  float limitDown = 0.f, limitUp = 0.f;
313  tester.getLimits(limitDown, limitUp);
314  std::cerr << "\tlim_+ = " << limitUp << std::endl;
315  std::cerr << "\tdrift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
316  std::cerr << "\texpected drift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(CUSUM_EXPECTED_RES[id]) << std::endl;
317  }
318  }
319  else if (opt_verbose) {
320  std::cout << "Upward drift test succeeded." << std::endl;
321  }
322  }
323 
324  // Mean adjusted CUSUM test upward drift
325  {
326  const float CUSUM_DATA[CUSUM_NB_DATA] = { -1.f * CUSUM_SAMPLE, -1.f * CUSUM_SAMPLE, -1.f * CUSUM_SAMPLE, -1.f * CUSUM_SAMPLE };
328  bool isTestOk = true;
329  unsigned int id = 0;
331  while ((id < CUSUM_NB_DATA) && isTestOk) {
332  drift = tester.testDownUpwardMeanDrift(CUSUM_DATA[id]);
333  isTestOk = (drift == CUSUM_EXPECTED_RES[id]);
334  if (isTestOk) {
335  ++id;
336  }
337  }
338  if (!isTestOk) {
339  success = false;
340  if (opt_verbose) {
341  std::cerr << "Downward drift test failed: " << std::endl;
342  std::cerr << "\tS-(k) = " << tester.getTestSignalMinus() << std::endl;
343  float limitDown = 0.f, limitUp = 0.f;
344  tester.getLimits(limitDown, limitUp);
345  std::cerr << "\tlim_- = " << limitDown << std::endl;
346  std::cerr << "\tdrift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
347  std::cerr << "\texpected drift type : " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(CUSUM_EXPECTED_RES[id]) << std::endl;
348  }
349  }
350  else if (opt_verbose) {
351  std::cout << "Downward drift test succeeded." << std::endl;
352  }
353  }
354  }
355 
356  // vpStatisticalTestShewhart tests
357  {
358  if (opt_verbose) {
359  std::cout << "------ vpStatisticalTestShewhart tests ------" << std::endl;
360  }
361  const bool activateWECOrules = true;
362  vpStatisticalTestShewhart tester(activateWECOrules, vpStatisticalTestShewhart::CONST_ALL_WECO_ACTIVATED, opt_mean, opt_stdev);
363 
364  // Upward drift test
365  {
366  if (opt_verbose) {
367  std::cout << "Upward drift tests" << std::endl;
368  }
369 
370  // 3-sigma test
371  {
372  bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "3-sigma", tester);
373  if (!isInitOK) {
374  success = false;
375  }
376  else {
377  const float signal = 3.5f * opt_stdev;
382  if ((drift != EXPECTED_DRIFT) || (alarm != EXPECTED_ALARM)) {
383  success = false;
384  if (opt_verbose) {
385  std::cerr << "\t3-sigma test failed: " << std::endl;
386  std::vector<float> s = tester.getSignals();
387  std::cerr << "\t\ts(t) = [ ";
388  for (size_t i = 0; i < s.size(); ++i) {
389  std::cerr << s[i] << " ";
390  }
391  std::cerr << "]" << std::endl;
392  float limitDown = 0.f, limitUp = 0.f;
393  tester.getLimits(limitDown, limitUp);
394  std::cerr << "\t\tlim_+ = " << limitUp << std::endl;
395  std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
396  std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT) << std::endl;
397  std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl;
398  std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM) << std::endl;
399  }
400  }
401  else if (opt_verbose) {
402  std::cout << "\t3-sigma test succeeded " << std::endl;
403  }
404  }
405  }
406 
407  // 2-sigma test
408  {
409  bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "2-sigma", tester);
410  if (!isInitOK) {
411  success = false;
412  }
413  else {
414  const unsigned int NB_SAMPLES = 3;
415  const float DATA[NB_SAMPLES] = { 2.75f * opt_stdev, 1.5f * opt_stdev, 2.5f * opt_stdev };
420  unsigned int i = 0;
421  bool isTestOk = true;
422  while ((i < NB_SAMPLES) && isTestOk) {
423  drift = tester.testDownUpwardMeanDrift(DATA[i]);
424  alarm = tester.getAlarm();
425  isTestOk = ((drift == EXPECTED_DRIFT[i]) && (alarm == EXPECTED_ALARM[i]));
426  if (isTestOk) {
427  ++i;
428  }
429  }
430 
431  if (!isTestOk) {
432  success = false;
433  if (opt_verbose) {
434  std::cerr << "\t2-sigma test failed: " << std::endl;
435  std::vector<float> s = tester.getSignals();
436  std::cerr << "\t\ts(t) = [ ";
437  for (size_t j = 0; j < s.size(); ++j) {
438  std::cerr << s[j] << " ";
439  }
440  std::cerr << "]" << std::endl;
441  float limitDown = 0.f, limitUp = 0.f;
442  tester.getLimits(limitDown, limitUp);
443  std::cerr << "\t\tlim_+ = " << limitUp << std::endl;
444  std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
445  std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT[i]) << std::endl;
446  std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl;
447  std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM[i]) << std::endl;
448  }
449  }
450  else if (opt_verbose) {
451  std::cout << "\t2-sigma test succeeded " << std::endl;
452  }
453  }
454  }
455 
456  // 1-sigma test
457  {
458  bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "1-sigma", tester);
459  if (!isInitOK) {
460  success = false;
461  }
462  else {
463  const unsigned int NB_SAMPLES = 5;
464  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 };
469  unsigned int i = 0;
470  bool isTestOk = true;
471  while ((i < NB_SAMPLES) && isTestOk) {
472  drift = tester.testDownUpwardMeanDrift(DATA[i]);
473  alarm = tester.getAlarm();
474  isTestOk = ((drift == EXPECTED_DRIFT[i]) && (alarm == EXPECTED_ALARM[i]));
475  if (isTestOk) {
476  ++i;
477  }
478  }
479 
480  if (!isTestOk) {
481  success = false;
482  if (opt_verbose) {
483  std::cerr << "\t1-sigma test failed: " << std::endl;
484  std::vector<float> s = tester.getSignals();
485  std::cerr << "\t\ts(t) = [ ";
486  for (size_t j = 0; j < s.size(); ++j) {
487  std::cerr << s[j] << " ";
488  }
489  std::cerr << "]" << std::endl;
490  float limitDown = 0.f, limitUp = 0.f;
491  tester.getLimits(limitDown, limitUp);
492  std::cerr << "\t\tlim_+ = " << limitUp << std::endl;
493  std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
494  std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT[i]) << std::endl;
495  std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl;
496  std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM[i]) << std::endl;
497  }
498  }
499  else if (opt_verbose) {
500  std::cout << "\t1-sigma test succeeded " << std::endl;
501  }
502  }
503  }
504 
505  // Same-side test
506  {
507  bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "Same-side", tester);
508  if (!isInitOK) {
509  success = false;
510  }
511  else {
512  const unsigned int NB_SAMPLES = 8;
513  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 };
518  unsigned int i = 0;
519  bool isTestOk = true;
520  while ((i < NB_SAMPLES) && isTestOk) {
521  drift = tester.testDownUpwardMeanDrift(DATA[i]);
522  alarm = tester.getAlarm();
523  isTestOk = ((drift == EXPECTED_DRIFT[i]) && (alarm == EXPECTED_ALARM[i]));
524  if (isTestOk) {
525  ++i;
526  }
527  }
528 
529  if (!isTestOk) {
530  success = false;
531  if (opt_verbose) {
532  std::cerr << "\tSame-side test failed: " << std::endl;
533  std::vector<float> s = tester.getSignals();
534  std::cerr << "\t\ts(t) = [ ";
535  for (size_t j = 0; j < s.size(); ++j) {
536  std::cerr << s[j] << " ";
537  }
538  std::cerr << "]" << std::endl;
539  float limitDown = 0.f, limitUp = 0.f;
540  tester.getLimits(limitDown, limitUp);
541  std::cerr << "\t\tlim_+ = " << limitUp << std::endl;
542  std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
543  std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT[i]) << std::endl;
544  std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl;
545  std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM[i]) << std::endl;
546  }
547  }
548  else if (opt_verbose) {
549  std::cout << "\tSame-side test succeeded " << std::endl;
550  }
551  }
552  }
553  }
554 
555  // Downward drift test
556  {
557  if (opt_verbose) {
558  std::cout << "Downward drift tests" << std::endl;
559  }
560 
561  // 3-sigma test
562  {
563  bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "3-sigma", tester);
564  if (!isInitOK) {
565  success = false;
566  }
567  else {
568  const float signal = -3.5f * opt_stdev;
573  if ((drift != EXPECTED_DRIFT) || (alarm != EXPECTED_ALARM)) {
574  success = false;
575  if (opt_verbose) {
576  std::cerr << "\t3-sigma test failed: " << std::endl;
577  std::vector<float> s = tester.getSignals();
578  std::cerr << "\t\ts(t) = [ ";
579  for (size_t j = 0; j < s.size(); ++j) {
580  std::cerr << s[j] << " ";
581  }
582  std::cerr << "]" << std::endl;
583  float limitDown = 0.f, limitUp = 0.f;
584  tester.getLimits(limitDown, limitUp);
585  std::cerr << "\t\tlim_- = " << limitDown << std::endl;
586  std::cerr << "\t\tlim_+ = " << limitUp << std::endl;
587  std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
588  std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT) << std::endl;
589  std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl;
590  std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM) << std::endl;
591  }
592  }
593  else if (opt_verbose) {
594  std::cout << "\t3-sigma test succeeded " << std::endl;
595  }
596  }
597  }
598 
599  // 2-sigma test
600  {
601  bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "2-sigma", tester);
602  if (!isInitOK) {
603  success = false;
604  }
605  else {
606  const unsigned int NB_SAMPLES = 3;
607  const float DATA[NB_SAMPLES] = { -2.75f * opt_stdev, -1.5f * opt_stdev, -2.5f * opt_stdev };
612  unsigned int i = 0;
613  bool isTestOk = true;
614  while ((i < NB_SAMPLES) && isTestOk) {
615  drift = tester.testDownUpwardMeanDrift(DATA[i]);
616  alarm = tester.getAlarm();
617  isTestOk = ((drift == EXPECTED_DRIFT[i]) && (alarm == EXPECTED_ALARM[i]));
618  if (isTestOk) {
619  ++i;
620  }
621  }
622 
623  if (!isTestOk) {
624  success = false;
625  if (opt_verbose) {
626  std::cerr << "\t2-sigma test failed: " << std::endl;
627  std::vector<float> s = tester.getSignals();
628  std::cerr << "\t\ts(t) = [ ";
629  for (size_t j = 0; j < s.size(); ++j) {
630  std::cerr << s[j] << " ";
631  }
632  std::cerr << "]" << std::endl;
633  float limitDown = 0.f, limitUp = 0.f;
634  tester.getLimits(limitDown, limitUp);
635  std::cerr << "\t\tlim_- = " << limitDown << std::endl;
636  std::cerr << "\t\tlim_+ = " << limitUp << std::endl;
637  std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
638  std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT[i]) << std::endl;
639  std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl;
640  std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM[i]) << std::endl;
641  }
642  }
643  else if (opt_verbose) {
644  std::cout << "\t2-sigma test succeeded " << std::endl;
645  }
646  }
647  }
648 
649  // 1-sigma test
650  {
651  bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "1-sigma", tester);
652  if (!isInitOK) {
653  success = false;
654  }
655  else {
656  const unsigned int NB_SAMPLES = 5;
657  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 };
662  unsigned int i = 0;
663  bool isTestOk = true;
664  while ((i < NB_SAMPLES) && isTestOk) {
665  drift = tester.testDownUpwardMeanDrift(DATA[i]);
666  alarm = tester.getAlarm();
667  isTestOk = ((drift == EXPECTED_DRIFT[i]) && (alarm == EXPECTED_ALARM[i]));
668  if (isTestOk) {
669  ++i;
670  }
671  }
672 
673  if (!isTestOk) {
674  success = false;
675  if (opt_verbose) {
676  std::cerr << "\t1-sigma test failed: " << std::endl;
677  std::vector<float> s = tester.getSignals();
678  std::cerr << "\t\ts(t) = [ ";
679  for (size_t j = 0; j < s.size(); ++j) {
680  std::cerr << s[j] << " ";
681  }
682  std::cerr << "]" << std::endl;
683  float limitDown = 0.f, limitUp = 0.f;
684  tester.getLimits(limitDown, limitUp);
685  std::cerr << "\t\tlim_- = " << limitDown << std::endl;
686  std::cerr << "\t\tlim_+ = " << limitUp << std::endl;
687  std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
688  std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT[i]) << std::endl;
689  std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl;
690  std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM[i]) << std::endl;
691  }
692  }
693  else if (opt_verbose) {
694  std::cout << "\t1-sigma test succeeded " << std::endl;
695  }
696  }
697  }
698 
699  // Same-side test
700  {
701  bool isInitOK = initializeShewhartTest(opt_mean, opt_stdev, opt_verbose, "Same-side", tester);
702  if (!isInitOK) {
703  success = false;
704  }
705  else {
706  const unsigned int NB_SAMPLES = 8;
707  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 };
712  unsigned int i = 0;
713  bool isTestOk = true;
714  while ((i < NB_SAMPLES) && isTestOk) {
715  drift = tester.testDownUpwardMeanDrift(DATA[i]);
716  alarm = tester.getAlarm();
717  isTestOk = ((drift == EXPECTED_DRIFT[i]) && (alarm == EXPECTED_ALARM[i]));
718  if (isTestOk) {
719  ++i;
720  }
721  }
722 
723  if (!isTestOk) {
724  success = false;
725  if (opt_verbose) {
726  std::cerr << "\tSame-side test failed: " << std::endl;
727  std::vector<float> s = tester.getSignals();
728  std::cerr << "\t\ts(t) = [ ";
729  for (size_t j = 0; j < s.size(); ++j) {
730  std::cerr << s[j] << " ";
731  }
732  std::cerr << "]" << std::endl;
733  float limitDown = 0.f, limitUp = 0.f;
734  tester.getLimits(limitDown, limitUp);
735  std::cerr << "\t\tlim_+ = " << limitUp << std::endl;
736  std::cerr << "\t\tlim_- = " << limitDown << std::endl;
737  std::cerr << "\t\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
738  std::cerr << "\t\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT[i]) << std::endl;
739  std::cerr << "\t\tdetected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(alarm) << std::endl;
740  std::cerr << "\t\texpected alarm = " << vpStatisticalTestShewhart::vpWecoRulesAlarmToString(EXPECTED_ALARM[i]) << std::endl;
741  }
742  }
743  else if (opt_verbose) {
744  std::cout << "\tSame-side test succeeded " << std::endl;
745  }
746  }
747  }
748  }
749  }
750 
751  // vpStatisticalTestSigma tests
752  {
753  if (opt_verbose) {
754  std::cout << "------ vpStatisticalTestSigma tests ------" << std::endl;
755  }
756  const float h = 3.f;
757  vpStatisticalTestSigma tester(h, opt_mean, opt_stdev);
758 
759  // Upward drift test
760  {
761  const float signal = 3.5f * opt_stdev;
764  if (drift != EXPECTED_DRIFT) {
765  success = false;
766  if (opt_verbose) {
767  std::cerr << "Upward drift test failed: " << std::endl;
768  std::cerr << "\ts(t) = " << tester.getSignal() << std::endl;
769  float limitDown = 0.f, limitUp = 0.f;
770  tester.getLimits(limitDown, limitUp);
771  std::cerr << "\tlim_+ = " << limitUp << std::endl;
772  std::cerr << "\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
773  std::cerr << "\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT) << std::endl;
774  }
775  }
776  else if (opt_verbose) {
777  std::cout << "Upward drift test succeeded " << std::endl;
778  }
779  }
780 
781  // Downward drift test
782  {
783  const float signal = -3.5f * opt_stdev;
786  if (drift != EXPECTED_DRIFT) {
787  success = false;
788  if (opt_verbose) {
789  std::cerr << "Downward drift test failed: " << std::endl;
790  std::cerr << "\ts(t) = " << tester.getSignal() << std::endl;
791  float limitDown = 0.f, limitUp = 0.f;
792  tester.getLimits(limitDown, limitUp);
793  std::cerr << "\tlim_- = " << limitDown << std::endl;
794  std::cerr << "\tdetected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(drift) << std::endl;
795  std::cerr << "\texpected drift = " << vpStatisticalTestAbstract::vpMeanDriftTypeToString(EXPECTED_DRIFT) << std::endl;
796  }
797  }
798  else if (opt_verbose) {
799  std::cout << "Downward drift test succeeded " << std::endl;
800  }
801  }
802  }
803 
804  if (success) {
805  std::cout << "Test succeed" << std::endl;
806  }
807  else {
808  std::cout << "Test failed" << std::endl;
809  return EXIT_FAILURE;
810  }
811  return EXIT_SUCCESS;
812 }
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
vpWecoRulesAlarm getAlarm() const
Get the alarm raised by the last test due to the WECO's rules.
std::vector< float > getSignals() const
Get the NB_DATA_SIGNAL last signal values, sorted from the latest [0] to the newest [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.