Skip to content

Commit 6aae3b8

Browse files
author
root
committed
fix: update Kickr Run incline from FTMS data
1 parent 86d7921 commit 6aae3b8

5 files changed

Lines changed: 69 additions & 1 deletion

File tree

src/devices/wahookickruntreadmill/wahookickruntreadmill.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,32 @@ void wahookickruntreadmill::forceIncline(double requestIncline) {
138138

139139
writeFTMSControlPoint(QByteArray(reinterpret_cast<const char *>(cmd), sizeof(cmd)),
140140
QStringLiteral("forceIncline ") + QString::number(requestIncline));
141-
Inclination = requestIncline;
141+
}
142+
143+
wahookickruntreadmill::FtmsTreadmillData wahookickruntreadmill::parseFtmsTreadmillData(const QByteArray &data) {
144+
FtmsTreadmillData parsed;
145+
if (data.length() < 2)
146+
return parsed;
147+
148+
const quint16 flags = static_cast<quint8>(data.at(0)) |
149+
(static_cast<quint16>(static_cast<quint8>(data.at(1))) << 8);
150+
int index = 2;
151+
152+
// FTMS Treadmill Data fields are optional and ordered by the flags.
153+
if (!(flags & 0x0001)) // Instantaneous Speed
154+
index += 2;
155+
if (flags & 0x0002) // Average Speed
156+
index += 2;
157+
if (flags & 0x0004) // Total Distance
158+
index += 3;
159+
if (!(flags & 0x0008) || data.length() < index + 2)
160+
return parsed;
161+
162+
const quint16 rawInclination = static_cast<quint8>(data.at(index)) |
163+
(static_cast<quint16>(static_cast<quint8>(data.at(index + 1))) << 8);
164+
parsed.hasInclination = true;
165+
parsed.inclination = static_cast<qint16>(rawInclination) / 10.0;
166+
return parsed;
142167
}
143168

144169
void wahookickruntreadmill::changeSpeed(double speed) {
@@ -247,6 +272,15 @@ void wahookickruntreadmill::characteristicChanged(const QLowEnergyCharacteristic
247272
return;
248273
}
249274

275+
if (characteristic.uuid() == QBluetoothUuid(static_cast<quint16>(0x2ACD))) {
276+
const FtmsTreadmillData ftmsData = parseFtmsTreadmillData(newValue);
277+
if (ftmsData.hasInclination) {
278+
Inclination = ftmsData.inclination;
279+
emit debug(QStringLiteral("KickRun inclination=") + QString::number(Inclination.value(), 'f', 1));
280+
}
281+
return;
282+
}
283+
250284
// --- Command/response channel (same char used for writes) ---
251285
// FE 86 01 [id0 id1 id2 id3] ... → store device ID, send 87+id
252286
if (len >= 7 && (uint8_t)newValue.at(0) == 0xFE && (uint8_t)newValue.at(1) == 0x86 &&

src/devices/wahookickruntreadmill/wahookickruntreadmill.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,13 @@
4848
class wahookickruntreadmill : public treadmill {
4949
Q_OBJECT
5050
public:
51+
struct FtmsTreadmillData {
52+
bool hasInclination = false;
53+
double inclination = 0.0;
54+
};
55+
5156
wahookickruntreadmill(bool noWriteResistance, bool noHeartService);
57+
static FtmsTreadmillData parseFtmsTreadmillData(const QByteArray &data);
5258
bool connected() override;
5359
bool autoPauseWhenSpeedIsZero() override;
5460
bool autoStartWhenSpeedIsGreaterThenZero() override;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include "TestWahooKickrRunParser.h"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#pragma once
2+
3+
#include <QByteArray>
4+
#include <gtest/gtest.h>
5+
6+
#include "devices/wahookickruntreadmill/wahookickruntreadmill.h"
7+
8+
TEST(WahooKickrRunFtmsRegressionTest, ParsesActualInclinationFromStephanLoggedTreadmillData) {
9+
const auto onePercent = wahookickruntreadmill::parseFtmsTreadmillData(
10+
QByteArray::fromHex("0c002c018d00000a00ff7f"));
11+
const auto twoPercent = wahookickruntreadmill::parseFtmsTreadmillData(
12+
QByteArray::fromHex("0c002c018f00001400ff7f"));
13+
14+
ASSERT_TRUE(onePercent.hasInclination);
15+
EXPECT_NEAR(onePercent.inclination, 1.0, 0.001);
16+
ASSERT_TRUE(twoPercent.hasInclination);
17+
EXPECT_NEAR(twoPercent.inclination, 2.0, 0.001);
18+
}
19+
20+
TEST(WahooKickrRunFtmsRegressionTest, HandlesTreadmillDataWithoutAnInclinationField) {
21+
const auto data = wahookickruntreadmill::parseFtmsTreadmillData(
22+
QByteArray::fromHex("00002c01"));
23+
24+
EXPECT_FALSE(data.hasInclination);
25+
}

tst/qdomyos-zwift-tests.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ SOURCES += \
3232
Devices/TestZwiftRideController.cpp \
3333
Devices/TestApexBikeParser.cpp \
3434
Devices/TestKeepBikeParser.cpp \
35+
Devices/TestWahooKickrRunParser.cpp \
3536
main.cpp
3637

3738
# Avoid the "File too big" error building in Windows. This has happened when a template class is used with Google Test / typed tests
@@ -63,6 +64,7 @@ HEADERS += \
6364
Devices/TestSchwinn411510EParser.h \
6465
Devices/TestApexBikeParser.h \
6566
Devices/TestKeepBikeParser.h \
67+
Devices/TestWahooKickrRunParser.h \
6668
Devices/TestOctaneTreadmillZR8.h \
6769
Devices/TestSunnyfitStepper.h \
6870
Erg/ergtabletestsuite.h \

0 commit comments

Comments
 (0)