adds unit tests to verify the rowing engine with real rower recordings

This commit is contained in:
Lars Berning 2021-04-21 19:56:42 +02:00
parent 55cde69ce6
commit 5dddeb2ff5
7 changed files with 151 additions and 3 deletions

View File

@ -0,0 +1,69 @@
'use strict'
/*
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
*/
import { test } from 'uvu'
import * as assert from 'uvu/assert'
import loglevel from 'loglevel'
import rowerProfiles from '../../config/rowerProfiles.js'
import { createRowingEngine } from './RowingEngine.js'
import { replayRowingSession } from '../tools/RowingRecorder.js'
const log = loglevel.getLogger('RowingEngine.test')
log.setLevel('warn')
const createWorkoutEvaluator = function () {
const strokes = []
function handleStroke (stroke) {
strokes.push(stroke)
log.info(`stroke: ${strokes.length}, power: ${Math.round(stroke.power)}w, duration: ${stroke.duration.toFixed(2)}s, ` +
` drivePhase: ${stroke.durationDrivePhase.toFixed(2)}s, distance: ${stroke.distance.toFixed(2)}m`)
}
function handleStrokeStateChanged () {}
function handlePause () {}
function getNumOfStrokes () {
return strokes.length
}
function getMaxStrokePower () {
return strokes.map((stroke) => stroke.power).reduce((acc, power) => Math.max(acc, power))
}
function getMinStrokePower () {
return strokes.map((stroke) => stroke.power).reduce((acc, power) => Math.max(acc, power))
}
return {
handleStroke,
handleStrokeStateChanged,
handlePause,
getNumOfStrokes,
getMaxStrokePower,
getMinStrokePower
}
}
test('sample data for WRX700 should produce plausible results with rower profile', async () => {
const rowingEngine = createRowingEngine(rowerProfiles.WRX700)
const workoutEvaluator = createWorkoutEvaluator()
rowingEngine.notify(workoutEvaluator)
await replayRowingSession(rowingEngine.handleRotationImpulse, { filename: 'recordings/WRX700_2magnets.csv' })
assert.is(workoutEvaluator.getNumOfStrokes(), 16, 'number of strokes does not meet expectation')
// todo: maximum power of the first stroke is too high because it does not contain a recovery part
// should fix that in the RowingEngine and adjust the maximum power here to 220
assert.ok(workoutEvaluator.getMaxStrokePower() < 370, `maximum stroke power should be below 370w, but is ${workoutEvaluator.getMaxStrokePower()}w`)
assert.ok(workoutEvaluator.getMinStrokePower() > 50, `minimum stroke power should be above 50w, but is ${workoutEvaluator.getMinStrokePower()}w`)
})
test('sample data for DKNR320 should produce plausible results with rower profile', async () => {
const rowingEngine = createRowingEngine(rowerProfiles.DKNR320)
const workoutEvaluator = createWorkoutEvaluator()
rowingEngine.notify(workoutEvaluator)
await replayRowingSession(rowingEngine.handleRotationImpulse, { filename: 'recordings/DKNR320.csv' })
assert.is(workoutEvaluator.getNumOfStrokes(), 10, 'number of strokes does not meet expectation')
// todo: maximum power of the first stroke is too high because it does not contain a recovery part
// should fix that in the RowingEngine and adjust the maximum power here to 200
assert.ok(workoutEvaluator.getMaxStrokePower() < 370, `maximum stroke power should be below 370w, but is ${workoutEvaluator.getMaxStrokePower()}w`)
assert.ok(workoutEvaluator.getMinStrokePower() > 75, `minimum stroke power should be above 75w, but is ${workoutEvaluator.getMinStrokePower()}w`)
})
test.run()

View File

@ -62,7 +62,7 @@ peripheralManager.on('control', (event) => {
const gpioTimerService = fork('./app/gpio/GpioTimerService.js') const gpioTimerService = fork('./app/gpio/GpioTimerService.js')
gpioTimerService.on('message', (dataPoint) => { gpioTimerService.on('message', (dataPoint) => {
rowingEngine.handleRotationImpulse(dataPoint) rowingEngine.handleRotationImpulse(dataPoint)
// fs.appendFile('recordings/wrx700_2magnets_long.csv', `${dataPoint}\n`, (err) => { if (err) log.error(err) }) // fs.appendFile('recordings/WRX700_2magnets.csv', `${dataPoint}\n`, (err) => { if (err) log.error(err) })
}) })
const rowingEngine = createRowingEngine(config.rowerSettings) const rowingEngine = createRowingEngine(config.rowerSettings)

View File

@ -8,7 +8,6 @@ This is the very minimalistic Backlog for further development of this project.
* validate FTMS with more training applications and harden implementation (i.e. Holofit and Coxswain) * validate FTMS with more training applications and harden implementation (i.e. Holofit and Coxswain)
* record a longer rowing session and analyze two encountered problems: 1) rarely the stroke rate doubles for a short duration (might be a problem with stroke detection when measurements are imprecise), 2) in one occasion the measured power jumped to a very high value after a break (40000 watts) * record a longer rowing session and analyze two encountered problems: 1) rarely the stroke rate doubles for a short duration (might be a problem with stroke detection when measurements are imprecise), 2) in one occasion the measured power jumped to a very high value after a break (40000 watts)
* add support for ANT+ heart rate monitors with USB dongles * add support for ANT+ heart rate monitors with USB dongles
* presets for rowing machine specific config parameters
* add an option to automatically feed the measured damping constant back into the rowing engine * add an option to automatically feed the measured damping constant back into the rowing engine
## Later ## Later

81
recordings/DKNR320.csv Normal file
View File

@ -0,0 +1,81 @@
0.54800057
0.268049458
0.216636979
0.231423722
0.284531614
0.360516235
0.482000949
0.43154357
0.211636619
0.183476903
0.211438686
0.258253277
0.322601712
0.419122022
0.503416119
0.247547705
0.183943279
0.18996022
0.22674499
0.280166413
0.353291919
0.465764768
0.399939714
0.194878642
0.164347067
0.183165805
0.221021301
0.270787329
0.340818407
0.445469766
0.387934208
0.195407159
0.166335799
0.186449225
0.226147521
0.277143086
0.349279683
0.460192588
0.370720743
0.188971811
0.163149731
0.182686216
0.220796962
0.270990489
0.340495458
0.445263279
0.451159591
0.202756547
0.158435164
0.164154601
0.199352942
0.243400048
0.301640659
0.3847461
0.509093228
0.298548183
0.17824294
0.162219424
0.186324723
0.22541504
0.278458293
0.350756165
0.462008358
0.405086177
0.19120966
0.162582118
0.18241563
0.220221933
0.269459088
0.339834293
0.443984581
0.462005699
0.21064753
0.161225062
0.167056112
0.20001449
0.243339132
0.301283162
0.385138631
0.520107711
0.822650475
1 0.54800057
2 0.268049458
3 0.216636979
4 0.231423722
5 0.284531614
6 0.360516235
7 0.482000949
8 0.43154357
9 0.211636619
10 0.183476903
11 0.211438686
12 0.258253277
13 0.322601712
14 0.419122022
15 0.503416119
16 0.247547705
17 0.183943279
18 0.18996022
19 0.22674499
20 0.280166413
21 0.353291919
22 0.465764768
23 0.399939714
24 0.194878642
25 0.164347067
26 0.183165805
27 0.221021301
28 0.270787329
29 0.340818407
30 0.445469766
31 0.387934208
32 0.195407159
33 0.166335799
34 0.186449225
35 0.226147521
36 0.277143086
37 0.349279683
38 0.460192588
39 0.370720743
40 0.188971811
41 0.163149731
42 0.182686216
43 0.220796962
44 0.270990489
45 0.340495458
46 0.445263279
47 0.451159591
48 0.202756547
49 0.158435164
50 0.164154601
51 0.199352942
52 0.243400048
53 0.301640659
54 0.3847461
55 0.509093228
56 0.298548183
57 0.17824294
58 0.162219424
59 0.186324723
60 0.22541504
61 0.278458293
62 0.350756165
63 0.462008358
64 0.405086177
65 0.19120966
66 0.162582118
67 0.18241563
68 0.220221933
69 0.269459088
70 0.339834293
71 0.443984581
72 0.462005699
73 0.21064753
74 0.161225062
75 0.167056112
76 0.20001449
77 0.243339132
78 0.301283162
79 0.385138631
80 0.520107711
81 0.822650475

View File

@ -1,4 +1,3 @@
3.47124274
0.370380355 0.370380355
0.211917846 0.211917846
0.169332723 0.169332723
1 3.47124274 0.370380355
3.47124274
1 0.370380355 0.370380355
2 0.211917846 0.211917846
3 0.169332723 0.169332723