diff --git a/app/engine/MovingFlankDetector.js b/app/engine/MovingFlankDetector.js index 4564d3d..daa0371 100644 --- a/app/engine/MovingFlankDetector.js +++ b/app/engine/MovingFlankDetector.js @@ -8,7 +8,7 @@ They are arranged that dataPoints[0] is the youngest, and dataPoints[flankLength] the oldest */ import loglevel from 'loglevel' -import { createMovingAverager } from './MovingAverager.js' +import { createMovingAverager } from './averager/MovingAverager.js' const log = loglevel.getLogger('RowingEngine') function createMovingFlankDetector (rowerSettings) { diff --git a/app/engine/RowingEngine.js b/app/engine/RowingEngine.js index ea8fe03..de93ab6 100644 --- a/app/engine/RowingEngine.js +++ b/app/engine/RowingEngine.js @@ -11,7 +11,7 @@ Also Dave Vernooy has some good explanations here: https://dvernooy.github.io/projects/ergware */ import loglevel from 'loglevel' -import { createMovingAverager } from './MovingAverager.js' +import { createMovingAverager } from './averager/MovingAverager.js' import { createMovingFlankDetector } from './MovingFlankDetector.js' const log = loglevel.getLogger('RowingEngine') diff --git a/app/engine/RowingStatistics.js b/app/engine/RowingStatistics.js index 92bceee..7f4ecde 100644 --- a/app/engine/RowingStatistics.js +++ b/app/engine/RowingStatistics.js @@ -5,8 +5,8 @@ This Module calculates the training specific metrics. */ import { EventEmitter } from 'events' -import { createMovingIntervalAverager } from './MovingIntervalAverager.js' -import { createWeightedAverager } from './WeightedAverager.js' +import { createMovingIntervalAverager } from './averager/MovingIntervalAverager.js' +import { createWeightedAverager } from './averager/WeightedAverager.js' import loglevel from 'loglevel' const log = loglevel.getLogger('RowingEngine') diff --git a/app/engine/MovingAverager.js b/app/engine/averager/MovingAverager.js similarity index 96% rename from app/engine/MovingAverager.js rename to app/engine/averager/MovingAverager.js index 3020cb4..3a4e853 100644 --- a/app/engine/MovingAverager.js +++ b/app/engine/averager/MovingAverager.js @@ -4,7 +4,8 @@ This Averager can calculate the moving average of a continuous flow of data points - Please note: The array contains flankLength + 1 measured currentDt's, thus flankLength number of flanks between them + Please note: The array contains flankLength + 1 measured currentDt's, thus flankLength number + of flanks between them. They are arranged that dataPoints[0] is the youngest, and dataPoints[flankLength] the oldest */ function createMovingAverager (length, initValue) { diff --git a/app/engine/averager/MovingAverager.test.js b/app/engine/averager/MovingAverager.test.js new file mode 100644 index 0000000..ede757a --- /dev/null +++ b/app/engine/averager/MovingAverager.test.js @@ -0,0 +1,44 @@ +'use strict' +/* + Open Rowing Monitor, https://github.com/laberning/openrowingmonitor +*/ +import { test } from 'uvu' +import * as assert from 'uvu/assert' + +import { createMovingAverager } from './MovingAverager.js' + +test('average should be initValue on empty dataset', () => { + const movingAverager = createMovingAverager(10, 5.5) + assert.is(movingAverager.getMovingAverage(), 5.5) +}) + +test('an averager of length 1 should return the last added value', () => { + const movingAverager = createMovingAverager(1, 3) + movingAverager.pushValue(9) + assert.is(movingAverager.getMovingAverage(), 9) +}) + +test('an averager of length 2 should return average of last 2 added elements', () => { + const movingAverager = createMovingAverager(2, 3) + movingAverager.pushValue(9) + movingAverager.pushValue(4) + assert.is(movingAverager.getMovingAverage(), 6.5) +}) + +test('elements outside of range should not be considered', () => { + const movingAverager = createMovingAverager(2, 3) + movingAverager.pushValue(9) + movingAverager.pushValue(4) + movingAverager.pushValue(3) + assert.is(movingAverager.getMovingAverage(), 3.5) +}) + +test('replacing the last element should work as expected', () => { + const movingAverager = createMovingAverager(2, 3) + movingAverager.pushValue(9) + movingAverager.pushValue(5) + movingAverager.replaceLastPushedValue(12) + assert.is(movingAverager.getMovingAverage(), 10.5) +}) + +test.run() diff --git a/app/engine/MovingIntervalAverager.js b/app/engine/averager/MovingIntervalAverager.js similarity index 94% rename from app/engine/MovingIntervalAverager.js rename to app/engine/averager/MovingIntervalAverager.js index c306f63..aa32684 100644 --- a/app/engine/MovingIntervalAverager.js +++ b/app/engine/averager/MovingIntervalAverager.js @@ -6,9 +6,10 @@ of data points for a certain (time) interval */ function createMovingIntervalAverager (movingDuration) { - let dataPoints = [] - let duration = 0.0 - let sum = 0.0 + let dataPoints + let duration + let sum + reset() function pushValue (dataValue, dataDuration) { // add the new dataPoint to the front of the array diff --git a/app/engine/MovingIntervalAverager.test.js b/app/engine/averager/MovingIntervalAverager.test.js similarity index 100% rename from app/engine/MovingIntervalAverager.test.js rename to app/engine/averager/MovingIntervalAverager.test.js diff --git a/app/engine/WeightedAverager.js b/app/engine/averager/WeightedAverager.js similarity index 100% rename from app/engine/WeightedAverager.js rename to app/engine/averager/WeightedAverager.js diff --git a/app/engine/averager/WeightedAverager.test.js b/app/engine/averager/WeightedAverager.test.js new file mode 100644 index 0000000..46f8719 --- /dev/null +++ b/app/engine/averager/WeightedAverager.test.js @@ -0,0 +1,45 @@ +'use strict' +/* + Open Rowing Monitor, https://github.com/laberning/openrowingmonitor +*/ +import { test } from 'uvu' +import * as assert from 'uvu/assert' + +import { createWeightedAverager } from './WeightedAverager.js' + +test('average should be 0 on empty dataset', () => { + const weightedAverager = createWeightedAverager(10) + assert.is(weightedAverager.weightedAverage(), 0) +}) + +test('average of one value is value', () => { + const weightedAverager = createWeightedAverager(10) + weightedAverager.pushValue(13.78) + assert.is(weightedAverager.weightedAverage(), 13.78) +}) + +test('average of a and b is (2*b + a) / 3', () => { + const weightedAverager = createWeightedAverager(10) + weightedAverager.pushValue(5) // a + weightedAverager.pushValue(2) // b + assert.is(weightedAverager.weightedAverage(), 3) +}) + +test('average should be 0 after reset', () => { + const weightedAverager = createWeightedAverager(10) + weightedAverager.pushValue(5) + weightedAverager.pushValue(2) + weightedAverager.reset() + assert.is(weightedAverager.weightedAverage(), 0) +}) + +test('average should be a after pushing a after a reset', () => { + const weightedAverager = createWeightedAverager(10) + weightedAverager.pushValue(5) + weightedAverager.pushValue(2) + weightedAverager.reset() + weightedAverager.pushValue(7) + assert.is(weightedAverager.weightedAverage(), 7) +}) + +test.run() diff --git a/docs/attribution.md b/docs/attribution.md index 44a0d44..06cc09c 100644 --- a/docs/attribution.md +++ b/docs/attribution.md @@ -6,8 +6,10 @@ Open Rowing Monitor uses some great work by others. Thank you for all the great * Dave Vernooy's project description on [ErgWare](https://dvernooy.github.io/projects/ergware) has some good information on the maths involved in a rowing ergometer. -* Bluetooth is quite a complex beast, luckily the Bluetooth SIG releases all the [specifications here](https://www.bluetooth.com/specifications/specs) +* Bluetooth is quite a complex beast, luckily the Bluetooth SIG releases all the [specifications here](https://www.bluetooth.com/specifications/specs). * The app icon is based on this [image of a rowing machine](https://thenounproject.com/term/rowing-machine/659265) by [Gan Khoon Lay](https://thenounproject.com/leremy/) licensed under [CC BY 2.0](https://creativecommons.org/licenses/by/2.0/). * The frontend uses some icons from [Font Awesome](https://fontawesome.com/), licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). + +* Thank you to [Jaap van Ekris](https://github.com/JaapvanEkris) for his contributions to this project.