Skip to content

Commit a8ec0bf

Browse files
cysimonsQhilm
authored andcommitted
Fix HR PID race condition with training program speed changes
The HR controller was reading the current speed, calculating an adjustment, then firing the change. If a training program changed speed in between these steps, the HR controller would overwrite the new speed with a stale calculation. Solution: Track when the training program changes speed via a timestamp in homeform. Both HR PID modes (zone-based and min/max) now skip adjustments for the configured delta period (typically 10 seconds) after a training program speed change, allowing the body to respond to the new speed first. Changes: - Add lastTrainingProgramSpeedChange timestamp member to homeform - Add onTrainingProgramSpeedChanged() slot to record speed change timestamps - Connect training program changeSpeed signal to the new slot - Guard both HR PID adjustment sections with recentSpeedChange check - Skip adjustments if a training program speed change occurred within delta seconds - Fix indentation to match project style (cpp and header files)
1 parent 5227002 commit a8ec0bf

3 files changed

Lines changed: 35 additions & 5 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,4 @@ src/inner_templates/googlemaps/cesium-key.js
5353
/tst/Devices/.vs
5454
src/inner_templates/googlemaps/cesium-key.js
5555
src/qdomyos-zwift.pro.user.49de507
56-
/.vs
56+
/.vs

src/homeform.cpp

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,6 +1590,8 @@ void homeform::trainProgramSignals() {
15901590
disconnect(trainProgram, &trainprogram::lap, this, &homeform::Lap);
15911591
disconnect(trainProgram, &trainprogram::changeSpeed, ((treadmill *)bluetoothManager->device()),
15921592
&treadmill::changeSpeed);
1593+
disconnect(trainProgram, &trainprogram::changeSpeed, this,
1594+
&homeform::onTrainingProgramSpeedChanged);
15931595
disconnect(trainProgram, &trainprogram::changeInclination, ((treadmill *)bluetoothManager->device()),
15941596
&treadmill::changeInclination);
15951597
disconnect(trainProgram, &trainprogram::changeNextInclination300Meters, bluetoothManager->device(),
@@ -1647,6 +1649,9 @@ void homeform::trainProgramSignals() {
16471649
connect(trainProgram, &trainprogram::stop, this, &homeform::StopFromTrainProgram);
16481650
connect(trainProgram, &trainprogram::lap, this, &homeform::Lap);
16491651
connect(trainProgram, &trainprogram::toastRequest, this, &homeform::onToastRequested);
1652+
// Connect training program speed changes to reset HR PID timer
1653+
connect(trainProgram, &trainprogram::changeSpeed, this,
1654+
&homeform::onTrainingProgramSpeedChanged);
16501655
if (bluetoothManager->device()->deviceType() == TREADMILL) {
16511656
connect(trainProgram, &trainprogram::changeSpeed, ((treadmill *)bluetoothManager->device()),
16521657
&treadmill::changeSpeed);
@@ -1738,6 +1743,13 @@ void homeform::onToastRequested(QString message) {
17381743
}
17391744
}
17401745

1746+
void homeform::onTrainingProgramSpeedChanged(double speed) {
1747+
// Record the timestamp when the training program changed speed
1748+
// This is used by the HR PID controller to avoid race conditions
1749+
lastTrainingProgramSpeedChange = QDateTime::currentDateTime();
1750+
}
1751+
1752+
17411753
QStringList homeform::tile_order() {
17421754

17431755
QStringList r;
@@ -7300,6 +7312,12 @@ void homeform::update() {
73007312

73017313
if (!stopped && !paused && bluetoothManager->device()->currentHeart().value() && zone > 0 &&
73027314
bluetoothManager->device()->currentSpeed().value() > 0.0f) {
7315+
// Skip HR PID adjustments for a period after training program changes speed
7316+
// This prevents race conditions where HR PID overwrites training program speed changes
7317+
qint64 msSinceSpeedChange = lastTrainingProgramSpeedChange.msecsTo(QDateTime::currentDateTime());
7318+
bool recentSpeedChange = (msSinceSpeedChange < (delta * 1000));
7319+
7320+
if (!recentSpeedChange) {
73037321
if (bluetoothManager->device()->deviceType() == TREADMILL) {
73047322

73057323
const double step = 0.2;
@@ -7411,6 +7429,7 @@ void homeform::update() {
74117429
((rower *)bluetoothManager->device())->changeResistance(currentResistance + step);
74127430
}
74137431
}
7432+
} // Close the if (!recentSpeedChange) block
74147433
}
74157434
}
74167435
} else if ((settings.value(QZSettings::treadmill_pid_heart_min, QZSettings::default_treadmill_pid_heart_min)
@@ -7468,6 +7487,12 @@ void homeform::update() {
74687487
<< QStringLiteral("HRmin:") << hrmin << QStringLiteral("HRmax:") << hrmax
74697488
<< QStringLiteral("fromTrainProgram:") << fromTrainProgram;
74707489

7490+
// Skip HR PID adjustments for a period after training program changes speed
7491+
// This prevents race conditions where HR PID overwrites training program speed changes
7492+
qint64 msSinceSpeedChange = lastTrainingProgramSpeedChange.msecsTo(QDateTime::currentDateTime());
7493+
bool recentSpeedChange = (msSinceSpeedChange < (delta * 1000));
7494+
7495+
if (!recentSpeedChange) {
74717496
if (bluetoothManager->device()->deviceType() == TREADMILL) {
74727497

74737498
const double step = 0.2;
@@ -7589,10 +7614,11 @@ void homeform::update() {
75897614
qDebug() << QStringLiteral("ROWING PID HR - HR < HRmin, INCREASING resistance from")
75907615
<< currentResistance << QStringLiteral("to") << (currentResistance + step);
75917616
((rower *)bluetoothManager->device())->changeResistance(currentResistance + step);
7592-
} else {
7593-
qDebug() << QStringLiteral("ROWING PID HR - No action taken (in zone or at limits)");
7594-
}
7595-
}
7617+
} else {
7618+
qDebug() << QStringLiteral("ROWING PID HR - No action taken (in zone or at limits)");
7619+
}
7620+
}
7621+
} // Close the if (!recentSpeedChange) block
75967622
}
75977623
}
75987624
}

src/homeform.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,9 @@ class homeform : public QObject {
974974
QTimer *backupTimer;
975975
QTimer *automaticShiftingTimer;
976976

977+
// HR PID controller state - tracks when training program changes speed to prevent race conditions
978+
QDateTime lastTrainingProgramSpeedChange = QDateTime::fromMSecsSinceEpoch(0);
979+
977980
// FIT backup threading
978981
QThread *fitBackupThread;
979982
FitBackupWriter *fitBackupWriter;
@@ -1089,6 +1092,7 @@ class homeform : public QObject {
10891092
void saveSessionAsTrainingProgram();
10901093
void strava_connect_clicked();
10911094
void trainProgramSignals();
1095+
void onTrainingProgramSpeedChanged(double speed);
10921096
void refresh_bluetooth_devices_clicked();
10931097
void onStravaGranted();
10941098
void onStravaAuthorizeWithBrowser(const QUrl &url);

0 commit comments

Comments
 (0)