feat(watch): improve lap detection with CMDeviceMotion and low-pass filtering
This commit is contained in:
@@ -393,32 +393,44 @@ class WorkoutManager: NSObject, ObservableObject, HKLiveWorkoutBuilderDelegate {
|
||||
healthStore.execute(query)
|
||||
}
|
||||
|
||||
// MARK: - Lap Detection (Accelerometer)
|
||||
// MARK: - Lap Detection (CMDeviceMotion)
|
||||
|
||||
private var filteredUserAccel: Double = 0
|
||||
private var filteredRotationX: Double = 0
|
||||
private let filterAlpha: Double = 0.3
|
||||
|
||||
private func startLapDetection() {
|
||||
guard motionManager.isAccelerometerAvailable else { return }
|
||||
motionManager.accelerometerUpdateInterval = 0.1 // 10 Hz
|
||||
motionManager.startAccelerometerUpdates()
|
||||
guard motionManager.isDeviceMotionAvailable else { return }
|
||||
motionManager.deviceMotionUpdateInterval = 0.1 // 10 Hz
|
||||
motionManager.startDeviceMotionUpdates(using: .xMagneticNorthZVertical)
|
||||
|
||||
lapDetectionTimer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true) { [weak self] _ in
|
||||
self?.processAccelerometerData()
|
||||
self?.processDeviceMotion()
|
||||
}
|
||||
}
|
||||
|
||||
private func stopLapDetection() {
|
||||
lapDetectionTimer?.invalidate()
|
||||
lapDetectionTimer = nil
|
||||
motionManager.stopAccelerometerUpdates()
|
||||
motionManager.stopDeviceMotionUpdates()
|
||||
}
|
||||
|
||||
private func processAccelerometerData() {
|
||||
guard let data = motionManager.accelerometerData else { return }
|
||||
private func processDeviceMotion() {
|
||||
guard let motion = motionManager.deviceMotion else { return }
|
||||
|
||||
let magnitude = sqrt(data.acceleration.x * data.acceleration.x +
|
||||
data.acceleration.y * data.acceleration.y +
|
||||
data.acceleration.z * data.acceleration.z)
|
||||
// userAcceleration has gravity removed — clean movement signal
|
||||
let rawUserAccel = sqrt(motion.userAcceleration.x * motion.userAcceleration.x +
|
||||
motion.userAcceleration.y * motion.userAcceleration.y +
|
||||
motion.userAcceleration.z * motion.userAcceleration.z)
|
||||
|
||||
let isStationary = magnitude < 1.1 // Near rest (~1g from gravity)
|
||||
// Low-pass filter for smoothing
|
||||
filteredUserAccel = filterAlpha * rawUserAccel + (1 - filterAlpha) * filteredUserAccel
|
||||
|
||||
// Rotation rate on x-axis detects the flip/turn at the wall
|
||||
let rawRotationX = abs(motion.rotationRate.x)
|
||||
filteredRotationX = filterAlpha * rawRotationX + (1 - filterAlpha) * filteredRotationX
|
||||
|
||||
let isStationary = filteredUserAccel < 0.05
|
||||
let now = Date()
|
||||
|
||||
if isStationary {
|
||||
@@ -439,7 +451,6 @@ class WorkoutManager: NSObject, ObservableObject, HKLiveWorkoutBuilderDelegate {
|
||||
self.lapDetected = true
|
||||
self.lastLapDetectionTime = now
|
||||
}
|
||||
// Reset after detection
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
|
||||
self.lapDetected = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user