Skip to content

Commit f964d67

Browse files
miraicatcagnulein
andauthored
feat(ios): derive Fake Treadmill speed from Apple Watch cadence (#4594)
* feat(ios): derive treadmill speed from Apple Watch cadence in Fake Treadmill mode Adds opt-in setting `applewatch_as_treadmill_speed` (default off) that derives Speed from Apple Watch step cadence inside cadenceFromAppleWatch() when fakedevice_treadmill is also enabled. Reuses cadence_sensor_speed_ratio ("Wheel Ratio") for the multiplier and the existing virtual treadmill / RSC broadcast pipeline. Useful for indoor walking and slow jogging with apps like Kinomap or Zwift when no physical treadmill is connected. Dual-gated behind both new toggles so normal treadmill users are unaffected. When cadence reports exactly zero (user stopped), Speed is also zeroed; a negative cadence (no fresh sample) leaves Speed unchanged. Settings appended at the end of qzsettings and settings.qml per src/CLAUDE.md. * docs(ios): correct wheel ratio range in applewatch_as_treadmill_speed help text Real-world testing showed the original 0.025-0.04 range was below typical slow jog speeds at 180 spm cadence (max ~3.6 km/h). Updated to 0.04-0.15 which covers walking through running. --------- Co-authored-by: Roberto Viola <Cagnulein@gmail.com>
1 parent 88c97c0 commit f964d67

4 files changed

Lines changed: 62 additions & 2 deletions

File tree

src/devices/treadmill.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,11 +612,31 @@ bool treadmill::cadenceFromAppleWatch() {
612612
.startsWith(QStringLiteral("Disabled"))) {
613613
long appleWatchCadence = h.stepCadence();
614614
qDebug() << QStringLiteral("Current Cadence: ") << QString::number(appleWatchCadence);
615+
// Slow-jogging mode: derive Speed from Apple Watch cadence when running on Fake
616+
// Treadmill. Gated on both toggles so this only runs in the explicit Fake Treadmill
617+
// use case and does not affect normal treadmill users by default.
618+
const bool appleWatchTreadmillSpeed =
619+
settings.value(QZSettings::applewatch_as_treadmill_speed,
620+
QZSettings::default_applewatch_as_treadmill_speed).toBool() &&
621+
settings.value(QZSettings::fakedevice_treadmill,
622+
QZSettings::default_fakedevice_treadmill).toBool();
615623
if (appleWatchCadence > 0) {
616624
evaluateStepCount();
617625
Cadence = appleWatchCadence;
626+
if (appleWatchTreadmillSpeed) {
627+
const double ratio =
628+
settings.value(QZSettings::cadence_sensor_speed_ratio,
629+
QZSettings::default_cadence_sensor_speed_ratio).toDouble();
630+
Speed = appleWatchCadence * (ratio > 0.0 ? ratio : 0.0);
631+
}
618632
return true;
619633
}
634+
if (appleWatchCadence == 0 && appleWatchTreadmillSpeed) {
635+
// Cadence reported as exactly zero (user stopped); zero Speed too so the virtual
636+
// treadmill broadcast doesn't ghost the previous value. Negative values are
637+
// treated as "no fresh sample" and leave Speed unchanged.
638+
Speed = 0;
639+
}
620640
return false;
621641
}
622642
#endif

src/qzsettings.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1086,9 +1086,10 @@ const QString QZSettings::step_gain = QStringLiteral("step_gain");
10861086
const QString QZSettings::proform_carbon_tlx_treadmill = QStringLiteral("proform_carbon_tlx_treadmill");
10871087
const QString QZSettings::proform_carbon_tlx_v84_314_treadmill = QStringLiteral("proform_carbon_tlx_v84_314_treadmill");
10881088
const QString QZSettings::proform_carbon_tl_PFTL59723_6 = QStringLiteral("proform_carbon_tl_PFTL59723_6");
1089+
const QString QZSettings::applewatch_as_treadmill_speed = QStringLiteral("applewatch_as_treadmill_speed");
10891090

10901091

1091-
const uint32_t allSettingsCount = 887;
1092+
const uint32_t allSettingsCount = 888;
10921093

10931094
QVariant allSettings[allSettingsCount][2] = {
10941095
{QZSettings::cryptoKeySettingsProfiles, QZSettings::default_cryptoKeySettingsProfiles},
@@ -1999,6 +2000,7 @@ QVariant allSettings[allSettingsCount][2] = {
19992000
{QZSettings::proform_carbon_tlx_treadmill, QZSettings::default_proform_carbon_tlx_treadmill},
20002001
{QZSettings::proform_carbon_tlx_v84_314_treadmill, QZSettings::default_proform_carbon_tlx_v84_314_treadmill},
20012002
{QZSettings::proform_carbon_tl_PFTL59723_6, QZSettings::default_proform_carbon_tl_PFTL59723_6},
2003+
{QZSettings::applewatch_as_treadmill_speed, QZSettings::default_applewatch_as_treadmill_speed},
20022004
};
20032005

20042006
void QZSettings::qDebugAllSettings(bool showDefaults) {

src/qzsettings.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2989,6 +2989,15 @@ class QZSettings {
29892989
static const QString proform_carbon_tl_PFTL59723_6;
29902990
static constexpr bool default_proform_carbon_tl_PFTL59723_6 = false;
29912991

2992+
/**
2993+
* @brief When enabled together with fakedevice_treadmill, derives treadmill Speed from
2994+
* Apple Watch step cadence using cadence_sensor_speed_ratio. Lets users do indoor walking
2995+
* or slow jogging without a physical treadmill, broadcasting cadence-driven speed via
2996+
* the existing virtual treadmill / RSC pipeline. Default off; iOS-only effect.
2997+
*/
2998+
static const QString applewatch_as_treadmill_speed;
2999+
static constexpr bool default_applewatch_as_treadmill_speed = false;
3000+
29923001
/**
29933002
* @brief Write the QSettings values using the constants from this namespace.
29943003
* @param showDefaults Optionally indicates if the default should be shown with the key.

src/settings.qml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1312,12 +1312,13 @@ import Qt.labs.platform 1.1
13121312

13131313
property double power_sensor_speed_inclination_coeff_a: 0.0
13141314
property double power_sensor_speed_inclination_coeff_b: 0.0
1315-
property bool proform_carbon_tlx_v84_314_treadmill: false
1315+
property bool proform_carbon_tlx_v84_314_treadmill: false
13161316
property bool cscbike_custom_resistance_power_table: false
13171317
property real cscbike_custom_resistance_level_1: 1
13181318
property real cscbike_custom_watt_1: 100
13191319
property real cscbike_custom_resistance_level_2: 15
13201320
property real cscbike_custom_watt_2: 300
1321+
property bool applewatch_as_treadmill_speed: false
13211322
}
13221323

13231324

@@ -14634,6 +14635,34 @@ import Qt.labs.platform 1.1
1463414635
color: Material.color(Material.Lime)
1463514636
}
1463614637

14638+
IndicatorOnlySwitch {
14639+
id: appleWatchAsTreadmillSpeedDelegate
14640+
text: qsTr("Use Apple Watch Cadence for Fake Treadmill Speed")
14641+
spacing: 0
14642+
bottomPadding: 0
14643+
topPadding: 0
14644+
rightPadding: 0
14645+
leftPadding: 0
14646+
clip: false
14647+
checked: settings.applewatch_as_treadmill_speed
14648+
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
14649+
Layout.fillWidth: true
14650+
onClicked: { settings.applewatch_as_treadmill_speed = checked; window.settings_restart_to_apply = true; }
14651+
}
14652+
14653+
Label {
14654+
text: qsTr("iOS only. For Fake Treadmill mode: when no physical treadmill is connected, derives Speed from Apple Watch step cadence using the Wheel Ratio under Accessories > Cadence Sensor Options. The cycling default is far too high for running - try 0.04-0.15 depending on pace, from walking to running, and tune to taste. Useful with apps like Kinomap or Zwift. Default is off.")
14655+
font.bold: true
14656+
font.italic: true
14657+
font.pixelSize: Qt.application.font.pixelSize - 2
14658+
textFormat: Text.PlainText
14659+
wrapMode: Text.WordWrap
14660+
verticalAlignment: Text.AlignVCenter
14661+
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
14662+
Layout.fillWidth: true
14663+
color: Material.color(Material.Lime)
14664+
}
14665+
1463714666
IndicatorOnlySwitch {
1463814667
id: fakeEllipticalDelegate
1463914668
text: qsTr("Fake Elliptical")

0 commit comments

Comments
 (0)