Compare commits

...

3 Commits
master ... 1280

Author SHA1 Message Date
Roberto Viola
9d4f266ccf Update project.pbxproj 2026-02-09 15:58:57 +01:00
Roberto Viola
aee47698f8 Merge branch 'master' into claude/implement-issue-4167-AsHMi 2026-02-09 15:49:08 +01:00
Claude
eb123d5d8c Add Stryd/Runn speed correction gain setting to prevent oscillations #4167
When using a Runn/Stryd sensor for speed instead of treadmill's built-in
speed, there's a latency between the treadmill changing speed and the
sensor detecting it. This caused QZ to over-correct, leading to speed
oscillations ("yo-yo" effect).

This adds a new "Speed Correction Gain" setting (0.0 - 1.0) that controls
how aggressively QZ applies speed corrections. A value of 1.0 applies
the full correction immediately (previous behavior), while lower values
(e.g., 0.3) apply gradual corrections to allow smooth convergence without
oscillations.

https://claude.ai/code/session_01A3c7YGsoALtmFszVZ8rLxm
2026-01-29 10:21:30 +00:00
5 changed files with 72 additions and 11 deletions

View File

@@ -4593,7 +4593,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1279;
CURRENT_PROJECT_VERSION = 1280;
DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES;
DEVELOPMENT_TEAM = 6335M7T29D;
ENABLE_BITCODE = NO;
@@ -4794,7 +4794,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1279;
CURRENT_PROJECT_VERSION = 1280;
DEBUG_INFORMATION_FORMAT = dwarf;
DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES;
DEVELOPMENT_TEAM = 6335M7T29D;
@@ -5031,7 +5031,7 @@
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1279;
CURRENT_PROJECT_VERSION = 1280;
DEVELOPMENT_TEAM = 6335M7T29D;
ENABLE_BITCODE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -5127,7 +5127,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1279;
CURRENT_PROJECT_VERSION = 1280;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 6335M7T29D;
ENABLE_BITCODE = YES;
@@ -5219,7 +5219,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1279;
CURRENT_PROJECT_VERSION = 1280;
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
ENABLE_BITCODE = YES;
ENABLE_PREVIEWS = YES;
@@ -5335,7 +5335,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1279;
CURRENT_PROJECT_VERSION = 1280;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
ENABLE_BITCODE = YES;
@@ -5445,7 +5445,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = QZWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1279;
CURRENT_PROJECT_VERSION = 1280;
DEVELOPMENT_TEAM = 6335M7T29D;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@@ -5536,7 +5536,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = QZWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1279;
CURRENT_PROJECT_VERSION = 1280;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 6335M7T29D;
ENABLE_NS_ASSERTIONS = NO;

View File

@@ -37,10 +37,12 @@ void treadmill::changeSpeed(double speed) {
if(stryd_speed_instead_treadmill && Speed.value() > 0) {
double delta = (Speed.value() - rawSpeed.value());
double maxAllowedDelta = speed * 0.20; // 20% of the speed request
double correction_gain = settings.value(QZSettings::stryd_speed_correction_gain, QZSettings::default_stryd_speed_correction_gain).toDouble();
if (std::abs(delta) <= maxAllowedDelta) {
qDebug() << "stryd_speed_instead_treadmill so override speed by " << delta;
speed -= delta;
double correctedDelta = delta * correction_gain;
qDebug() << "stryd_speed_instead_treadmill so override speed by " << correctedDelta << "(delta:" << delta << "gain:" << correction_gain << ")";
speed -= correctedDelta;
} else {
qDebug() << "Delta" << delta << "exceeds 20% threshold of" << maxAllowedDelta << "- not applying correction";
}

View File

@@ -789,6 +789,7 @@ const QString QZSettings::tile_erg_mode_enabled = QStringLiteral("tile_erg_mode_
const QString QZSettings::tile_erg_mode_order = QStringLiteral("tile_erg_mode_order");
const QString QZSettings::toorx_srx_3500 = QStringLiteral("toorx_srx_3500");
const QString QZSettings::stryd_speed_instead_treadmill = QStringLiteral("stryd_speed_instead_treadmill");
const QString QZSettings::stryd_speed_correction_gain = QStringLiteral("stryd_speed_correction_gain");
const QString QZSettings::inclination_delay_seconds = QStringLiteral("inclination_delay_seconds");
const QString QZSettings::ergDataPoints = QStringLiteral("ergDataPoints");
const QString QZSettings::default_ergDataPoints = QStringLiteral("");
@@ -1052,7 +1053,7 @@ const QString QZSettings::trainprogram_auto_lap_on_segment = QStringLiteral("tra
const QString QZSettings::kingsmith_r2_enable_hw_buttons = QStringLiteral("kingsmith_r2_enable_hw_buttons");
const uint32_t allSettingsCount = 858;
const uint32_t allSettingsCount = 859;
QVariant allSettings[allSettingsCount][2] = {
{QZSettings::cryptoKeySettingsProfiles, QZSettings::default_cryptoKeySettingsProfiles},
@@ -1711,6 +1712,7 @@ QVariant allSettings[allSettingsCount][2] = {
{QZSettings::tile_erg_mode_order, QZSettings::default_tile_erg_mode_order},
{QZSettings::toorx_srx_3500, QZSettings::default_toorx_srx_3500},
{QZSettings::stryd_speed_instead_treadmill, QZSettings::default_stryd_speed_instead_treadmill},
{QZSettings::stryd_speed_correction_gain, QZSettings::default_stryd_speed_correction_gain},
{QZSettings::inclination_delay_seconds, QZSettings::default_inclination_delay_seconds},
{QZSettings::ergDataPoints, QZSettings::default_ergDataPoints},
{QZSettings::proform_carbon_tl, QZSettings::default_proform_carbon_tl},

View File

@@ -2184,6 +2184,8 @@ class QZSettings {
static const QString stryd_speed_instead_treadmill;
static constexpr bool default_stryd_speed_instead_treadmill = false;
static const QString stryd_speed_correction_gain;
static constexpr double default_stryd_speed_correction_gain = 1.0;
static const QString inclination_delay_seconds;
static constexpr float default_inclination_delay_seconds = 0.0;

View File

@@ -1275,6 +1275,7 @@ import Qt.labs.platform 1.1
property bool domyos_treadmill_ts100: false
property bool thinkrider_controller: false
property bool weight_kg_unit: false
property real stryd_speed_correction_gain: 1.0
}
@@ -11888,6 +11889,60 @@ import Qt.labs.platform 1.1
color: Material.color(Material.Lime)
}
RowLayout {
visible: settings.stryd_speed_instead_treadmill
spacing: 10
Label {
id: labelStrydSpeedCorrectionGain
text: qsTr("Speed Correction Gain:")
Layout.fillWidth: true
}
TextField {
id: strydSpeedCorrectionGainTextField
text: settings.stryd_speed_correction_gain.toFixed(2)
horizontalAlignment: Text.AlignRight
Layout.fillHeight: false
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
inputMethodHints: Qt.ImhDigitsOnly
onActiveFocusChanged: if(this.focus) this.selectAll()
onAccepted: {
var value = parseFloat(text)
if (value >= 0.0 && value <= 1.0) {
settings.stryd_speed_correction_gain = value
} else {
text = settings.stryd_speed_correction_gain.toFixed(2)
}
}
}
Button {
id: okStrydSpeedCorrectionGainButton
text: "OK"
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
onClicked: {
var value = parseFloat(strydSpeedCorrectionGainTextField.text)
if (value >= 0.0 && value <= 1.0) {
settings.stryd_speed_correction_gain = value
} else {
strydSpeedCorrectionGainTextField.text = settings.stryd_speed_correction_gain.toFixed(2)
}
}
}
}
Label {
visible: settings.stryd_speed_instead_treadmill
text: qsTr("Controls how aggressively QZ corrects treadmill speed based on sensor readings. A value of 1.0 applies full correction immediately (may cause oscillations). Lower values (e.g., 0.3) apply gradual corrections to avoid speed fluctuations. Range: 0.0 - 1.0. Default: 1.0")
font.bold: true
font.italic: true
font.pixelSize: Qt.application.font.pixelSize - 2
textFormat: Text.PlainText
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillWidth: true
color: Material.color(Material.Lime)
}
IndicatorOnlySwitch {
text: qsTr("Use inclination from the power sensor")
spacing: 0