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