Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions src/homeform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7793,6 +7793,8 @@ void homeform::update() {
(bluetoothManager->device()->elapsedTime().hour() * 3600);
uint8_t delta = 10;
bool trainprogram_pid_pushy = settings.value(QZSettings::trainprogram_pid_pushy, QZSettings::default_trainprogram_pid_pushy).toBool();
double trainprogram_pid_hr_pushy_zone_limit = settings.value(QZSettings::trainprogram_pid_hr_pushy_zone_limit, QZSettings::default_trainprogram_pid_hr_pushy_zone_limit).toDouble();
double trainprogram_pid_hr_recovery_zone_limit = settings.value(QZSettings::trainprogram_pid_hr_recovery_zone_limit, QZSettings::default_trainprogram_pid_hr_recovery_zone_limit).toDouble();
bool fromTrainProgram = trainProgram && trainProgram->currentRow().zoneHR >= 0;
double maxSpeed = 30;
double minSpeed = 0;
Expand Down Expand Up @@ -7882,15 +7884,47 @@ void homeform::update() {
newSpeed,
((treadmill *)bluetoothManager->device())->currentInclination().value());
pid_heart_zone_small_inc_counter = 0;
} else if (currentSpeed < maxSpeed && trainprogram_pid_pushy) {
pid_heart_zone_small_inc_counter++;
if (fabs(((float)zone) - currentHRZone) < 0.5 && pid_heart_zone_small_inc_counter > (10 * fabs(((float)zone) - currentHRZone))) {
double newSpeed = std::min(currentSpeed + step, maxSpeed);
} else if (trainprogram_pid_pushy) {
double pushyZoneLimit = (double)zone + trainprogram_pid_hr_pushy_zone_limit;
// Slowdown threshold is symmetric: midpoint between pushyZoneLimit and zone top
// e.g. pushy=0.8: slowdown at zone+0.9, neutral band [0.8, 0.9]
double pushySlowdownThreshold = (double)zone + (1.0 + trainprogram_pid_hr_pushy_zone_limit) / 2.0;
double pushyHRZone = currentHRZone;
if (zone == 1) {
double zone1Limit =
settings.value(QZSettings::heart_rate_zone1, QZSettings::default_heart_rate_zone1)
.toDouble();
double zone1LowerLimit = qBound(0.0, trainprogram_pid_hr_recovery_zone_limit, zone1Limit - 1.0);
double effectiveZone1Width = zone1Limit - zone1LowerLimit;
if (effectiveZone1Width > 0.0) {
double maxHeartRate = heartRateMax();
double currentHRPercent =
(bluetoothManager->device()->currentHeart().value() * 100.0) / maxHeartRate;
pushyHRZone =
1.0 + ((currentHRPercent - zone1LowerLimit) / effectiveZone1Width);
pushyHRZone = qBound(1.0, pushyHRZone, 1.9999);
}
}
double distanceToNextZone = ((double)zone + 1.0) - pushyHRZone;
if (pushyHRZone > pushySlowdownThreshold && currentSpeed > minSpeed) {
double newSpeed = std::max(currentSpeed - step, minSpeed);
((treadmill *)bluetoothManager->device())
->changeSpeedAndInclination(
newSpeed,
((treadmill *)bluetoothManager->device())->currentInclination().value());
pid_heart_zone_small_inc_counter = 0;
} else if (pushyHRZone < pushyZoneLimit && distanceToNextZone > 0.0 && currentSpeed < maxSpeed) {
pid_heart_zone_small_inc_counter++;
if (pid_heart_zone_small_inc_counter > (10 * distanceToNextZone)) {
double newSpeed = std::min(currentSpeed + step, maxSpeed);
((treadmill *)bluetoothManager->device())
->changeSpeedAndInclination(
newSpeed,
((treadmill *)bluetoothManager->device())->currentInclination().value());
pid_heart_zone_small_inc_counter = 0;
}
} else {
pid_heart_zone_small_inc_counter++;
}
}
} else if (bluetoothManager->device()->deviceType() == BIKE) {
Expand Down
6 changes: 5 additions & 1 deletion src/qzsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,8 @@ const QString QZSettings::watt_bike_emulator = QStringLiteral("watt_bike_emulato
const QString QZSettings::restore_specific_gear = QStringLiteral("restore_specific_gear");
const QString QZSettings::skipLocationServicesDialog = QStringLiteral("skipLocationServicesDialog");
const QString QZSettings::trainprogram_pid_pushy = QStringLiteral("trainprogram_pid_pushy");
const QString QZSettings::trainprogram_pid_hr_pushy_zone_limit = QStringLiteral("trainprogram_pid_hr_pushy_zone_limit");
const QString QZSettings::trainprogram_pid_hr_recovery_zone_limit = QStringLiteral("trainprogram_pid_hr_recovery_zone_limit");
const QString QZSettings::min_inclination = QStringLiteral("min_inclination");
const QString QZSettings::proform_performance_300i = QStringLiteral("proform_performance_300i");
const QString QZSettings::proform_performance_400i = QStringLiteral("proform_performance_400i");
Expand Down Expand Up @@ -1245,7 +1247,7 @@ const QString QZSettings::default_shortcut_start_stop = QStringLiteral("");
const QString QZSettings::shortcut_stop = QStringLiteral("shortcut_stop");
const QString QZSettings::default_shortcut_stop = QStringLiteral("");

const uint32_t allSettingsCount = 972;
const uint32_t allSettingsCount = 974;

QVariant allSettings[allSettingsCount][2] = {
{QZSettings::cryptoKeySettingsProfiles, QZSettings::default_cryptoKeySettingsProfiles},
Expand Down Expand Up @@ -2001,6 +2003,8 @@ QVariant allSettings[allSettingsCount][2] = {
{QZSettings::restore_specific_gear, QZSettings::default_restore_specific_gear},
{QZSettings::skipLocationServicesDialog, QZSettings::default_skipLocationServicesDialog},
{QZSettings::trainprogram_pid_pushy, QZSettings::default_trainprogram_pid_pushy},
{QZSettings::trainprogram_pid_hr_pushy_zone_limit, QZSettings::default_trainprogram_pid_hr_pushy_zone_limit},
{QZSettings::trainprogram_pid_hr_recovery_zone_limit, QZSettings::default_trainprogram_pid_hr_recovery_zone_limit},
{QZSettings::min_inclination, QZSettings::default_min_inclination},
{QZSettings::proform_performance_300i, QZSettings::default_proform_performance_300i},
{QZSettings::proform_performance_400i, QZSettings::default_proform_performance_400i},
Expand Down
6 changes: 6 additions & 0 deletions src/qzsettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -2476,6 +2476,12 @@ class QZSettings {
static const QString trainprogram_pid_pushy;
static constexpr bool default_trainprogram_pid_pushy = true;

static const QString trainprogram_pid_hr_pushy_zone_limit;
static constexpr double default_trainprogram_pid_hr_pushy_zone_limit = 0.8;

static const QString trainprogram_pid_hr_recovery_zone_limit;
static constexpr double default_trainprogram_pid_hr_recovery_zone_limit = 60.0;

static const QString min_inclination;
static constexpr double default_min_inclination = -999.0;

Expand Down
28 changes: 27 additions & 1 deletion src/settings-catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://qdomyos-zwift.local/settings-catalog.schema.json",
"schemaVersion": 1,
"format": "qdomyos-zwift-settings-catalog",
"settingCount": 960,
"settingCount": 962,
"pages": [
{
"key": "page_custom_gear_table",
Expand Down Expand Up @@ -9228,6 +9228,32 @@
"defaultExpression": "true",
"options": null
},
{
"key": "trainprogram_pid_hr_recovery_zone_limit",
"name": "PID Recovery Zone Lower Limit",
"description": "Lower HR boundary (% of max HR) that defines the bottom of Zone 1 for 'Pushy' mode. Below this percentage the treadmill is at the bottom of the recovery area. Default: 60.",
"parent": "Training Program Options",
"type": "number",
"qmlType": "real",
"control": "textfield",
"visible": true,
"defaultValue": 60.0,
"defaultExpression": "60.0",
"options": null
},
{
"key": "trainprogram_pid_hr_pushy_zone_limit",
"name": "PID Pushy Zone Limit",
"description": "Fraction of zone above the target zone where 'Pushy' mode stops pushing. 0.8 means the PID stops pushing at zone+0.8. Default: 0.8.",
"parent": "Training Program Options",
"type": "number",
"qmlType": "real",
"control": "textfield",
"visible": true,
"defaultValue": 0.8,
"defaultExpression": "0.8",
"options": null
},
{
"key": "min_inclination",
"name": "Minimum Inclination",
Expand Down
72 changes: 71 additions & 1 deletion src/settings.qml
Original file line number Diff line number Diff line change
Expand Up @@ -1703,9 +1703,11 @@ import AndroidStatusBar 1.0
property real trainprogram_rest_speed: 420
property bool trainprogram_sound_on_segment: false
property bool tile_watt_color_enabled: true
property bool tile_pace_color_enabled: true
property bool tile_pace_color_enabled: true
property bool treadmill_force_running_activity: false
property bool proform_treadmill_105_cst: false
property real trainprogram_pid_hr_pushy_zone_limit: 0.8
property real trainprogram_pid_hr_recovery_zone_limit: 60.0
}


Expand Down Expand Up @@ -8585,6 +8587,74 @@ import AndroidStatusBar 1.0
color: Material.color(Material.Lime)
}

RowLayout {
spacing: 10
Label {
text: qsTr("PID Recovery Zone Lower Limit (%):")
Layout.fillWidth: true
}
TextField {
id: pidHrRecoveryZoneLimitTextField
text: settings.trainprogram_pid_hr_recovery_zone_limit
horizontalAlignment: Text.AlignRight
Layout.fillHeight: false
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
onActiveFocusChanged: if(this.focus) this.cursorPosition = this.text.length
}
Button {
text: "OK"
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
onClicked: { settings.trainprogram_pid_hr_recovery_zone_limit = parseFloat(pidHrRecoveryZoneLimitTextField.text); toast.show("Setting saved!"); }
}
}

Label {
text: qsTr("Lower HR boundary (% of max HR) that defines the bottom of Zone 1 for 'Pushy' mode. Below this percentage the treadmill is at the bottom of the recovery area. Default: 60.")
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)
}

RowLayout {
spacing: 10
Label {
text: qsTr("PID Pushy Zone Limit:")
Layout.fillWidth: true
}
TextField {
id: pidHrPushyZoneLimitTextField
text: settings.trainprogram_pid_hr_pushy_zone_limit
horizontalAlignment: Text.AlignRight
Layout.fillHeight: false
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
onActiveFocusChanged: if(this.focus) this.cursorPosition = this.text.length
}
Button {
text: "OK"
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
onClicked: { settings.trainprogram_pid_hr_pushy_zone_limit = parseFloat(pidHrPushyZoneLimitTextField.text); toast.show("Setting saved!"); }
}
}

Label {
text: qsTr("Fraction of zone above the target zone where 'Pushy' mode stops pushing. 0.8 means the PID stops pushing at zone+0.8. Default: 0.8.")
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("PID Ignore Inclination")
spacing: 0
Expand Down
Loading