Skip to content

Commit 22cac3e

Browse files
committed
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.
1 parent 1df45c5 commit 22cac3e

4 files changed

Lines changed: 61 additions & 1 deletion

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
@@ -1081,9 +1081,10 @@ const QString QZSettings::step_gain = QStringLiteral("step_gain");
10811081
const QString QZSettings::proform_carbon_tlx_treadmill = QStringLiteral("proform_carbon_tlx_treadmill");
10821082
const QString QZSettings::proform_carbon_tlx_v84_314_treadmill = QStringLiteral("proform_carbon_tlx_v84_314_treadmill");
10831083
const QString QZSettings::proform_carbon_tl_PFTL59723_6 = QStringLiteral("proform_carbon_tl_PFTL59723_6");
1084+
const QString QZSettings::applewatch_as_treadmill_speed = QStringLiteral("applewatch_as_treadmill_speed");
10841085

10851086

1086-
const uint32_t allSettingsCount = 882;
1087+
const uint32_t allSettingsCount = 883;
10871088

10881089
QVariant allSettings[allSettingsCount][2] = {
10891090
{QZSettings::cryptoKeySettingsProfiles, QZSettings::default_cryptoKeySettingsProfiles},
@@ -1989,6 +1990,7 @@ QVariant allSettings[allSettingsCount][2] = {
19891990
{QZSettings::proform_carbon_tlx_treadmill, QZSettings::default_proform_carbon_tlx_treadmill},
19901991
{QZSettings::proform_carbon_tlx_v84_314_treadmill, QZSettings::default_proform_carbon_tlx_v84_314_treadmill},
19911992
{QZSettings::proform_carbon_tl_PFTL59723_6, QZSettings::default_proform_carbon_tl_PFTL59723_6},
1993+
{QZSettings::applewatch_as_treadmill_speed, QZSettings::default_applewatch_as_treadmill_speed},
19921994
};
19931995

19941996
void QZSettings::qDebugAllSettings(bool showDefaults) {

src/qzsettings.h

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

2977+
/**
2978+
* @brief When enabled together with fakedevice_treadmill, derives treadmill Speed from
2979+
* Apple Watch step cadence using cadence_sensor_speed_ratio. Lets users do indoor walking
2980+
* or slow jogging without a physical treadmill, broadcasting cadence-driven speed via
2981+
* the existing virtual treadmill / RSC pipeline. Default off; iOS-only effect.
2982+
*/
2983+
static const QString applewatch_as_treadmill_speed;
2984+
static constexpr bool default_applewatch_as_treadmill_speed = false;
2985+
29772986
/**
29782987
* @brief Write the QSettings values using the constants from this namespace.
29792988
* @param showDefaults Optionally indicates if the default should be shown with the key.

src/settings.qml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,7 @@ import Qt.labs.platform 1.1
13131313
property double power_sensor_speed_inclination_coeff_a: 0.0
13141314
property double power_sensor_speed_inclination_coeff_b: 0.0
13151315
property bool proform_carbon_tlx_v84_314_treadmill: false
1316+
property bool applewatch_as_treadmill_speed: false
13161317
}
13171318

13181319

@@ -14497,6 +14498,34 @@ import Qt.labs.platform 1.1
1449714498
color: Material.color(Material.Lime)
1449814499
}
1449914500

14501+
IndicatorOnlySwitch {
14502+
id: appleWatchAsTreadmillSpeedDelegate
14503+
text: qsTr("Use Apple Watch Cadence for Fake Treadmill Speed")
14504+
spacing: 0
14505+
bottomPadding: 0
14506+
topPadding: 0
14507+
rightPadding: 0
14508+
leftPadding: 0
14509+
clip: false
14510+
checked: settings.applewatch_as_treadmill_speed
14511+
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
14512+
Layout.fillWidth: true
14513+
onClicked: { settings.applewatch_as_treadmill_speed = checked; window.settings_restart_to_apply = true; }
14514+
}
14515+
14516+
Label {
14517+
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. For walking or slow jogging, set Wheel Ratio much lower than the cycling default, usually around 0.025-0.04. Useful with apps like Kinomap or Zwift. Default is off.")
14518+
font.bold: true
14519+
font.italic: true
14520+
font.pixelSize: Qt.application.font.pixelSize - 2
14521+
textFormat: Text.PlainText
14522+
wrapMode: Text.WordWrap
14523+
verticalAlignment: Text.AlignVCenter
14524+
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
14525+
Layout.fillWidth: true
14526+
color: Material.color(Material.Lime)
14527+
}
14528+
1450014529
IndicatorOnlySwitch {
1450114530
id: fakeEllipticalDelegate
1450214531
text: qsTr("Fake Elliptical")

0 commit comments

Comments
 (0)