feat(train): a ride reads the bike's speed and cadence sensor and power meter (ADR 0030, D49)
11d, its sensors: a ride's Record sheet offers the CSC sensor (0x1816) and the power meter (0x1818), found nearby and remembered as the strap is; the person's devices are now one map of DeviceKind to DeviceStore. The service parses CSC and Cycling Power measurements (JVM-tested, counters' rollover, coasting), writes the wheel's revolutions, the crank's cadence and the watts to the file and to series of their own. The recording screen shows cadence and watts; the page the power's chart, average, highest and normalised power and the cadence's chart and average; GPX, TCX and FIT carry them. A plan's watts target reads the power meter too. The wheel's speed and distance follow. Not tried with a real sensor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
57cbd97fd1
commit
5c13e6f3c5
2 changed files with 1209 additions and 62 deletions
|
|
@ -133,6 +133,49 @@ void main() {
|
|||
expect(analysis.elevationGainMetres, 12);
|
||||
});
|
||||
|
||||
group("a ride's sensors (D49)", () {
|
||||
test('normalised power: steady watts are their own, surges weigh more', () {
|
||||
expect(
|
||||
normalisedPowerOf([
|
||||
for (var s = 0; s < 120; s++) (200, Duration(seconds: s)),
|
||||
]),
|
||||
200,
|
||||
);
|
||||
final surges = [
|
||||
for (var s = 0; s < 600; s++)
|
||||
((s ~/ 60).isEven ? 100 : 300, Duration(seconds: s)),
|
||||
];
|
||||
// Averaging 200 W, but the minutes at 300 W weigh more.
|
||||
expect(normalisedPowerOf(surges), greaterThan(220));
|
||||
expect(normalisedPowerOf(surges.take(50).toList()), isNull);
|
||||
});
|
||||
|
||||
test("a ride's page has its power, normalised power and cadence; "
|
||||
'coasting left out of the cadence pedalled', () {
|
||||
final ride = Recording(
|
||||
activity: Activity.cycling,
|
||||
startedAt: start,
|
||||
fixes: [for (var s = 0; s <= 300; s += 10) north(s, s * 8)],
|
||||
power: [
|
||||
for (var s = 0; s <= 300; s++) Reading(time: at(s), value: 200),
|
||||
],
|
||||
pedalling: [
|
||||
for (var s = 0; s <= 300; s++)
|
||||
Reading(time: at(s), value: s < 280 ? 90 : 0),
|
||||
],
|
||||
endedAt: at(300),
|
||||
);
|
||||
|
||||
final analysis = analysed(ride);
|
||||
expect(analysis.averageWatts, 200);
|
||||
expect(analysis.maxWatts, 200);
|
||||
expect(analysis.normalisedPower, 200);
|
||||
expect(analysis.averageCadence, 90);
|
||||
expect(analysis.pedalling, isNotEmpty);
|
||||
expect(analysis.watts, isNotEmpty);
|
||||
});
|
||||
});
|
||||
|
||||
group("the barometer's elevation (D49)", () {
|
||||
/// The pressure, in tenths of a pascal, [metres] above the standard sea
|
||||
/// level.
|
||||
|
|
|
|||
|
|
@ -281,6 +281,32 @@ void main() {
|
|||
expect(session[6], 14);
|
||||
});
|
||||
|
||||
test("a ride's crank cadence and watts go out with each point (D49)", () {
|
||||
final ride = Recording(
|
||||
activity: Activity.cycling,
|
||||
startedAt: start,
|
||||
fixes: [for (var s = 0; s <= 60; s += 10) north(s, s * 8)],
|
||||
pedalling: [
|
||||
for (var s = 0; s <= 60; s++) Reading(time: at(s), value: 92),
|
||||
],
|
||||
power: [for (var s = 0; s <= 60; s++) Reading(time: at(s), value: 230)],
|
||||
endedAt: at(60),
|
||||
);
|
||||
final (rode, rideSeries) = recordedWorkout(ride);
|
||||
final rideTrack = exportTrackOf(rode, rideSeries);
|
||||
expect(rideTrack.points.first.cadence, 92);
|
||||
expect(rideTrack.points.first.watts, 230);
|
||||
final tcx = XmlDocument.parse(tcxOf(rode, rideTrack));
|
||||
expect(tcx.findAllElements('Cadence').first.innerText, '92');
|
||||
expect(tcx.findAllElements('Watts').first.innerText, '230');
|
||||
final record = [
|
||||
for (final message in readFit(fitOf(rode, rideTrack)))
|
||||
if (message.global == 20) message.fields,
|
||||
].first;
|
||||
expect(record[4], 92);
|
||||
expect(record[7], 230);
|
||||
});
|
||||
|
||||
test('the file is named by when the workout started, in UTC', () {
|
||||
expect(
|
||||
exportNameOf(content, ExportFormat.fit),
|
||||
|
|
|
|||
Loading…
Reference in a new issue