Files
Roberto Viola be12057c51 Add flights climbed metric to Apple Health for treadmill workouts (#3909)
* Add flights climbed metric to Apple Health for treadmill workouts

Implement calculation and tracking of flights climbed in Apple Health for
treadmill workouts on both iOS and watchOS, using the treadmill's inclination
data.

Changes:
- Add flightsClimbed static variable to WorkoutTracking class to track accumulated flights
- Add inclination parameter to addMetrics() function with default value of 0
- Calculate flights climbed based on inclination and distance delta:
  * Vertical gain = distance * sin(atan(inclination/100))
  * Flights = vertical gain / 3.048 meters (10 feet per flight)
- Add HealthKit authorization for flightsClimbed quantity type
- Write flights climbed data to HealthKit in stopWorkOut() for walking/running workouts
- Reset flights climbed counter at workout start and end
- Pass inclination from lockscreen.mm to WorkoutTracking Swift code
- Convert inclination from centesimal int16 to percentage double

The implementation only tracks flights for treadmill workouts (sport types 0 and 1
for walking and running) when inclination is greater than 0.

* Add flights climbed metric to watchOS for treadmill workouts

Extend Apple Health flights climbed tracking to watchOS companion app
for treadmill workouts using inclination data.

Changes to watchkit Extension/WatchWorkoutTracking.swift:
- Add flightsClimbed, inclination, and previousDistance static variables
- Add HealthKit authorization for flightsClimbed quantity type
- Add updateMetrics() method to calculate flights climbed in real-time:
  * Vertical gain = distance delta * sin(atan(inclination/100))
  * Flights = vertical gain / 3.048 meters (10 feet per flight)
- Write flights climbed data to HealthKit in stopWorkOut() for walking/running
- Reset flights climbed counter at workout start and end
- Combine steps, distance, and flights into single sample array

The implementation tracks flights only for walking/running workouts (sport
types 1 and 2) when inclination is greater than 0, matching iOS behavior.

* Refactor flights climbed to use QZ's existing elevationGain

Replace manual elevation calculation with QZ's built-in elevationGain metric
for improved accuracy and efficiency.

Changes:
- Add elevationGain parameter to virtualtreadmill_updateFTMS() signature
- Pass elevationGain (meters) from virtualtreadmill.cpp to iOS/watchOS
- Remove manual calculation in WorkoutTracking.swift and WatchWorkoutTracking.swift
- Simplify code by using pre-calculated metric from treadmill.cpp
- Remove unnecessary variables (previousDistance, inclination)

Benefits:
- More accurate: uses QZ's time-aware calculation with deltaTime
- More efficient: eliminates duplicate elevation calculations
- Cleaner code: reduces complexity and improves maintainability
- Consistent: aligns with QZ's existing metrics infrastructure

The elevationGain is calculated by treadmill::update_metrics() as:
  elevationAcc += (speed / 3600.0) * 1000.0 * (inclination / 100.0) * deltaTime

Flights climbed = elevationGain (meters) / 3.048 (10 feet per flight)

* Implement WatchConnectivity bridge for flights climbed on watchOS

Complete the watchOS integration by implementing WatchConnectivity bridge
to pass elevationGain from iOS to Apple Watch, enabling flights climbed
tracking on the watch.

iOS changes:
- Add elevationGain static variable to WatchKitConnection.swift
- Include elevationGain in WatchConnectivity replyHandler
- Add @objc setElevationGain() method in AppDelegate.swift
- Declare and implement setElevationGain() in lockscreen.h/mm
- Call setElevationGain() from virtualtreadmill.cpp with elevationGain value

watchOS changes:
- Add elevationGain static variable to WatchKitConnection.swift
- Extract elevationGain from iOS message in replyHandler
- Calculate flights climbed (elevationGain / 3.048) and update WorkoutTracking
- Remove unused updateMetrics() method from WatchWorkoutTracking.swift

Flow:
1. C++ treadmill.cpp calculates elevationGain from speed/inclination/deltaTime
2. virtualtreadmill.cpp passes elevationGain to iOS via lockscreen bridge
3. iOS stores elevationGain in WatchKitConnection static variable
4. When watch requests data, iOS includes elevationGain in reply message
5. Watch receives elevationGain, calculates flights climbed, updates HealthKit

Benefits:
- watchOS now receives accurate elevation data from QZ's calculations
- Flights climbed synced to Apple Health from both iOS and watchOS
- Consistent implementation across platforms
- Removes duplicate/unused code

* Fix compilation error: add previousDistance variable for distance delta calculations

Add missing previousDistance variable to WorkoutTracking class. This variable
is needed to calculate distance deltas for cycling and rowing metrics, which
is separate from the flights climbed calculation.

The previousDistance is used in:
- Cycling distance delta calculation (line 622)
- Rowing distance delta calculation (line 664)

Initialize previousDistance to 0 at workout start and update it at the end
of each addMetrics() call.

* Fix Swift static member access: use WorkoutTracking.previousDistance

Correct the static member access syntax. Since previousDistance is a static
variable, it must be accessed via the class name WorkoutTracking.previousDistance
rather than as a local variable.

Fixed in two locations:
- Line 624: cycling distance delta calculation
- Line 666: rowing distance delta calculation

* Add missing elevationGain parameter to addMetrics calls

Add elevationGain:0 parameter to three addMetrics calls in lockscreen.mm:
- workoutTrackingUpdate(): general workout tracking (line 211)
- virtualbike_updateFTMS(): bike workout tracking (line 269)
- virtualrower_updateFTMS(): rower workout tracking (line 279)

These devices don't have elevation gain relevant for flights climbed
calculation, so passing 0 is appropriate. The treadmill-specific call
in virtualtreadmill_updateFTMS() already correctly passes the actual
elevationGain value.

* Update project.pbxproj

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-27 10:09:03 +01:00
..
2022-01-27 15:51:15 +01:00
2024-03-15 15:57:40 +01:00