detectSteps method

List<Step> detectSteps({
  1. required List<double> acc,
  2. required double threshold,
  3. int smoothingWindow = 10,
  4. int minStepIntervalMs = 300,
})

Detects individual steps in accelerometer data using peak detection.

Implements a step detection algorithm optimized for walking analysis:

  1. Applies smoothing using a moving average window
  2. Detects peaks in the smoothed acceleration data
  3. Validates peaks against threshold and timing constraints
  4. Prevents false positives with minimum step interval

The algorithm uses state tracking to avoid detecting multiple peaks for the same step and enforces a minimum interval between consecutive steps.

Returns a list of Step objects containing the timestamp indices of detected steps.

Implementation

List<Step> detectSteps({
  required List<double> acc,
  required double threshold,
  int smoothingWindow = 10,
  int minStepIntervalMs = 300,
}) {
  // Apply smoothing using moving average window
  List<double> smoothedAcc = List<double>.filled(acc.length, 0.0);
  for (int i = 0; i < acc.length; i++) {
    double sum = 0.0;
    int count = 0;
    // Calculate moving average within window bounds
    for (
      int j = (i - smoothingWindow ~/ 2).clamp(0, acc.length - 1);
      j <= (i + smoothingWindow ~/ 2).clamp(0, acc.length - 1);
      j++
    ) {
      sum += acc[j];
      count++;
    }
    smoothedAcc[i] = sum / count;
  }

  // Calculate minimum interval between steps in samples
  final minStepIntervalSamples =
      (minStepIntervalMs / (1000 / config.accelerometerSamplingRate)).round();
  int lastStepIndex = -minStepIntervalSamples;

  List<Step> steps = [];
  bool stepDetected = false; // State tracking to prevent duplicate detection

  // Scan for peaks that indicate steps
  for (int i = 1; i < smoothedAcc.length - 1; i++) {
    // Check if current point is a local maximum
    bool isPeak =
        smoothedAcc[i] > smoothedAcc[i - 1] &&
        smoothedAcc[i] > smoothedAcc[i + 1];

    // Validate peak against threshold and timing constraints
    if (isPeak && smoothedAcc[i] > threshold && !stepDetected) {
      // Ensure minimum interval since last detected step
      if ((i - lastStepIndex) >= minStepIntervalSamples) {
        steps.add(Step(i));
        lastStepIndex = i;
        stepDetected = true; // Set flag to prevent duplicate detection
      }
    } else if (smoothedAcc[i] < threshold) {
      stepDetected = false; // Reset flag when signal drops below threshold
    }
  }

  return steps;
}