detectSitDown method

int detectSitDown({
  1. required List<SensorEvent> accData,
  2. required double accActualRate,
  3. required DateTime startTime,
  4. required DateTime minimumTime,
})

Detects the sit-down event in TUG test using accelerometer data.

Similar to stand-up detection but searches after a given start time and ensures proper timing constraints (minimum 2 seconds after detection). The algorithm:

  1. Removes gravity from y-axis acceleration
  2. Applies low-pass filtering
  3. Detects peaks in processed data after start time
  4. Validates timing by waiting 2 seconds after detection

Returns the index of the detected sit-down event, or -1 if none found.

Implementation

int detectSitDown({
  required List<SensorEvent> accData,
  required double accActualRate,
  required DateTime startTime,
  required DateTime minimumTime,
}) {
  if (accData.isEmpty) return -1;
  int smoothingWindow = 10;
  double threshold = 0.6; // Acceleration threshold for sit-down detection

  // Process accelerometer data - remove gravity from y-axis
  var processedAccData = lowPassFilter(
    accData.map((e) => e.y - 9.8).toList(),
    4.0, // Low-pass filter cutoff frequency
  );

  // Apply smoothing using moving average window (same as stand-up detection)
  List<double> smoothedAcc = List<double>.filled(
    processedAccData.length,
    0.0,
  );
  for (int i = 0; i < processedAccData.length; i++) {
    double sum = 0.0;
    int count = 0;
    // Calculate moving average within window bounds
    for (
      int j = (i - smoothingWindow ~/ 2).clamp(
        0,
        processedAccData.length - 1,
      );
      j <= (i + smoothingWindow ~/ 2).clamp(0, processedAccData.length - 1);
      j++
    ) {
      sum += processedAccData[j];
      count++;
    }
    smoothedAcc[i] = sum / count;
  }

  // Find starting index based on start time
  var startIndex = accData.indexWhere((a) => a.timestamp.isAfter(startTime));
  var sitIndex = -1;

  // Search for sit-down event after start time with timing validation
  for (int i = startIndex; i < processedAccData.length - 1; i++) {
    bool isPeak =
        processedAccData[i] > processedAccData[i - 1] &&
        processedAccData[i] > processedAccData[i + 1];

    if (isPeak && processedAccData[i] > threshold) {
      sitIndex = i; // Record potential sit-down event
    }
    if (sitIndex != -1 &&
        i - sitIndex > 2 * accActualRate &&
        accData[i].timestamp.isAfter(minimumTime)) {
      // Ensure at least 2 seconds after sit down for validation
      return sitIndex;
    }
  }

  return -1;
}