@Override public void detect(AdaptiveFilter<Long> filter) { // Preamble int threshold = getSetting(THRESHOLD_PROPERTY).intValue(); double drift = getSetting(DRIFT_PROPERTY).doubleValue(); int robustness = getSetting(ROBUSTNESS_PROPERTY).intValue(); double negativeCusum = 0; double positiveCusum = 0; List<DataPoint<Long>> residuals = filter.getResiduals().getDataAsList(); List<DataPoint<Long>> variances = filter.getVariance().getDataAsList(); for (int i = 0; i < residuals.size(); i++) { List<Detection<Long>> detections = getDetections(); Detection<Long> lastDetection = null; if (!detections.isEmpty()) { lastDetection = detections.get(detections.size() - 1); } // Get residual double residual = residuals.get(i).getValue(); double variance = variances.get(i).getValue(); // normalize residual, but avoid division by zero. Cause that's bad. if (variance != 0) { residual = Math.pow(residual, 2) / Math.pow(variance, 2); } else { residual = Math.pow(residual, 2); } // CUSUM algorithm performed on residuals positiveCusum = Math.max(0, positiveCusum + drift + residual); negativeCusum = Math.max(0, Math.abs(negativeCusum + drift - residual)); /* Abs might not be needed -->*/ if (positiveCusum > threshold || negativeCusum > threshold) { robustness--; if (robustness == 0) { if (detections.isEmpty() || lastDetection.hasStop()) { detections.add( new Detection<Long>( new Long(i), Math.round(Math.max(positiveCusum, negativeCusum)))); positiveCusum = 0; negativeCusum = 0; robustness = getSetting(ROBUSTNESS_PROPERTY).intValue(); } else { if (i - lastDetection.getTouched() > getSetting(ROBUSTNESS_PROPERTY).intValue()) { lastDetection.setStop(new Long(i)); } else { lastDetection.touch(new Long(i)); } } } } else { boolean detectionsExist = detections.size() > 0; boolean lastDetectionIsNotAlreadyStopped = !lastDetection.hasStop(); if (detectionsExist && lastDetectionIsNotAlreadyStopped) { if ((i - lastDetection.getTouched()) > robustness) { lastDetection.setStop(new Long(i)); } } robustness = getSetting(ROBUSTNESS_PROPERTY).intValue(); } } if (!getDetections().isEmpty()) { Detection<Long> lastDetection = getDetections().get(getDetections().size() - 1); if (!lastDetection.hasStop()) { lastDetection.setStop(new Long(residuals.size())); } } }