SwingWatch WatchKit Extension/MotionManager.swift
| /* | 
| Copyright (C) 2016 Apple Inc. All Rights Reserved. | 
| See LICENSE.txt for this sample’s licensing information | 
| Abstract: | 
| This class manages the CoreMotion interactions and | 
| provides a delegate to indicate changes in data. | 
| */ | 
| import Foundation | 
| import CoreMotion | 
| import WatchKit | 
| /** | 
| `MotionManagerDelegate` exists to inform delegates of motion changes. | 
| These contexts can be used to enable application specific behavior. | 
| */ | 
| protocol MotionManagerDelegate: class { | 
| func didUpdateForehandSwingCount(_ manager: MotionManager, forehandCount: Int) | 
| func didUpdateBackhandSwingCount(_ manager: MotionManager, backhandCount: Int) | 
| } | 
| class MotionManager { | 
| // MARK: Properties | 
| let motionManager = CMMotionManager() | 
| let queue = OperationQueue() | 
| let wristLocationIsLeft = WKInterfaceDevice.current().wristLocation == .left | 
| // MARK: Application Specific Constants | 
| // These constants were derived from data and should be further tuned for your needs. | 
| let yawThreshold = 1.95 // Radians | 
| let rateThreshold = 5.5 // Radians/sec | 
| let resetThreshold = 5.5 * 0.05 // To avoid double counting on the return swing. | 
| // The app is using 50hz data and the buffer is going to hold 1s worth of data. | 
| let sampleInterval = 1.0 / 50 | 
| let rateAlongGravityBuffer = RunningBuffer(size: 50) | 
| weak var delegate: MotionManagerDelegate? | 
| /// Swing counts. | 
| var forehandCount = 0 | 
| var backhandCount = 0 | 
| var recentDetection = false | 
| // MARK: Initialization | 
|     init() { | 
| // Serial queue for sample handling and calculations. | 
| queue.maxConcurrentOperationCount = 1 | 
| queue.name = "MotionManagerQueue" | 
| } | 
| // MARK: Motion Manager | 
|     func startUpdates() { | 
|         if !motionManager.isDeviceMotionAvailable { | 
|             print("Device Motion is not available.") | 
| return | 
| } | 
| // Reset everything when we start. | 
| resetAllState() | 
| motionManager.deviceMotionUpdateInterval = sampleInterval | 
|         motionManager.startDeviceMotionUpdates(to: queue) { (deviceMotion: CMDeviceMotion?, error: Error?) in | 
|             if error != nil { | 
|                 print("Encountered error: \(error!)") | 
| } | 
|             if deviceMotion != nil { | 
| self.processDeviceMotion(deviceMotion!) | 
| } | 
| } | 
| } | 
|     func stopUpdates() { | 
|         if motionManager.isDeviceMotionAvailable { | 
| motionManager.stopDeviceMotionUpdates() | 
| } | 
| } | 
| // MARK: Motion Processing | 
|     func processDeviceMotion(_ deviceMotion: CMDeviceMotion) { | 
| let gravity = deviceMotion.gravity | 
| let rotationRate = deviceMotion.rotationRate | 
| let rateAlongGravity = rotationRate.x * gravity.x // r⃗ · ĝ | 
| + rotationRate.y * gravity.y | 
| + rotationRate.z * gravity.z | 
| rateAlongGravityBuffer.addSample(rateAlongGravity) | 
|         if !rateAlongGravityBuffer.isFull() { | 
| return | 
| } | 
| let accumulatedYawRot = rateAlongGravityBuffer.sum() * sampleInterval | 
| let peakRate = accumulatedYawRot > 0 ? | 
| rateAlongGravityBuffer.max() : rateAlongGravityBuffer.min() | 
|         if (accumulatedYawRot < -yawThreshold && peakRate < -rateThreshold) { | 
| // Counter clockwise swing. | 
|             if (wristLocationIsLeft) { | 
| incrementBackhandCountAndUpdateDelegate() | 
|             } else { | 
| incrementForehandCountAndUpdateDelegate() | 
| } | 
|         } else if (accumulatedYawRot > yawThreshold && peakRate > rateThreshold) { | 
| // Clockwise swing. | 
|             if (wristLocationIsLeft) { | 
| incrementForehandCountAndUpdateDelegate() | 
|             } else { | 
| incrementBackhandCountAndUpdateDelegate() | 
| } | 
| } | 
| // Reset after letting the rate settle to catch the return swing. | 
|         if (recentDetection && abs(rateAlongGravityBuffer.recentMean()) < resetThreshold) { | 
| recentDetection = false | 
| rateAlongGravityBuffer.reset() | 
| } | 
| } | 
| // MARK: Data and Delegate Management | 
|     func resetAllState() { | 
| rateAlongGravityBuffer.reset() | 
| forehandCount = 0 | 
| backhandCount = 0 | 
| recentDetection = false | 
| updateForehandSwingDelegate() | 
| updateBackhandSwingDelegate() | 
| } | 
|     func incrementForehandCountAndUpdateDelegate() { | 
|         if (!recentDetection) { | 
| forehandCount += 1 | 
| recentDetection = true | 
|             print("Forehand swing. Count: \(forehandCount)") | 
| updateForehandSwingDelegate() | 
| } | 
| } | 
|     func incrementBackhandCountAndUpdateDelegate() { | 
|         if (!recentDetection) { | 
| backhandCount += 1 | 
| recentDetection = true | 
|             print("Backhand swing. Count: \(backhandCount)") | 
| updateBackhandSwingDelegate() | 
| } | 
| } | 
|     func updateForehandSwingDelegate() { | 
| delegate?.didUpdateForehandSwingCount(self, forehandCount:forehandCount) | 
| } | 
|     func updateBackhandSwingDelegate() { | 
| delegate?.didUpdateBackhandSwingCount(self, backhandCount:backhandCount) | 
| } | 
| } | 
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-09-13