Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
119 commits
Select commit Hold shift + click to select a range
7e2477d
Generalisation to a more general weighted series
JaapvanEkris Dec 25, 2025
2d1d86f
Adaptation to Weighted generalisation
JaapvanEkris Dec 25, 2025
d1a33bf
Replace OLSLinearSeries with WLSLinearSeries
JaapvanEkris Dec 25, 2025
0f8c5e2
Replace OLSLinearSeries with WLSLinearSeries for calories
JaapvanEkris Dec 25, 2025
003ad85
Fix Lint errors
JaapvanEkris Dec 25, 2025
8954277
Fixed import bug
JaapvanEkris Dec 25, 2025
587c1af
Fix import path for createWLSLinearSeries function
JaapvanEkris Dec 25, 2025
29b6ff9
Based the CyclicErrorFilter based on WLS
JaapvanEkris Dec 25, 2025
eed1f6b
Fix Lint errors
JaapvanEkris Dec 25, 2025
1571ea4
Fixes ESlint errors
JaapvanEkris Dec 25, 2025
a3a5772
Removed unneeded function
JaapvanEkris Dec 25, 2025
31020a2
Fixed ESLint error
JaapvanEkris Dec 25, 2025
8447dd1
Rename of file
JaapvanEkris Dec 26, 2025
dc9f754
Update import path for createTSLinearSeries
JaapvanEkris Dec 26, 2025
44d8cdd
Rename FullTSLinearSeries.test.js to TSLinearSeries.test.js
JaapvanEkris Dec 26, 2025
1aac84b
Update import path for createTSLinearSeries
JaapvanEkris Dec 26, 2025
c7fad91
Update import path for createTSLinearSeries
JaapvanEkris Dec 26, 2025
d64ea49
Update import path for createTSQuadraticSeries
JaapvanEkris Dec 26, 2025
22a2f0a
Rename FullTSQuadraticSeries.test.js to TSQuadraticSeries.test.js
JaapvanEkris Dec 26, 2025
460fe00
Refactor TSQuadraticSeries with improved documentation
JaapvanEkris Dec 26, 2025
6064c6b
Enhance documentation with JSDoc comments
JaapvanEkris Dec 26, 2025
9d94341
Fix comment formatting in intercept function
JaapvanEkris Dec 26, 2025
41b7787
Fix total moving time in Rower test
JaapvanEkris Dec 26, 2025
94c9dce
Fix total moving time in rowing statistics test
JaapvanEkris Dec 26, 2025
952a3ff
Fix expected total linear distance in Rower tests
JaapvanEkris Dec 26, 2025
400ee3b
Fix total moving time assertions in tests
JaapvanEkris Dec 26, 2025
800a3a7
Update expected recovery drag factor in tests
JaapvanEkris Dec 26, 2025
6ef62f6
Fix test values for total distance and drag factor
JaapvanEkris Dec 26, 2025
dc9caea
Fix linear distance assertions in tests
JaapvanEkris Dec 26, 2025
4cc8c46
Fix calorie test values in SessionManager tests
JaapvanEkris Dec 26, 2025
f3d54eb
Update dragFactor test values in SessionManager tests
JaapvanEkris Dec 26, 2025
bb5d9f7
Enhance rationale and error handling in algorithms
JaapvanEkris Dec 28, 2025
5a924e0
Enhance regression analysis section with weighted methods
JaapvanEkris Dec 28, 2025
7d3d43e
Fixes ESlint errors
JaapvanEkris Dec 28, 2025
9182070
Add tests for OLS and WLS regression using Galton dataset
JaapvanEkris Dec 30, 2025
c116c93
Update slope test values for precision
JaapvanEkris Dec 30, 2025
2567aa6
Update intercept values in WLSLinearSeries tests
JaapvanEkris Dec 30, 2025
b19e88e
Update goodness of fit tests in WLSLinearSeries
JaapvanEkris Dec 30, 2025
e58b2c0
Fixed a bug where LocalGoodnessOfFit wasn;t defined for 2 datapoints
JaapvanEkris Dec 30, 2025
bd4543f
Added tests for LocalGoodnessOfFit
JaapvanEkris Dec 30, 2025
8ffe6d5
Update TSLinearSeries.js
JaapvanEkris Dec 30, 2025
fb1d14b
Update TSLinearSeries.test.js
JaapvanEkris Dec 30, 2025
6c70ee6
Update TSLinearSeries.js
JaapvanEkris Dec 30, 2025
9745b91
Update TSLinearSeries.test.js
JaapvanEkris Dec 30, 2025
86dedc7
Update TSLinearSeries.js
JaapvanEkris Dec 30, 2025
6f539c6
Fixed testing errors
JaapvanEkris Dec 30, 2025
bb0d1c6
Removed scaffold code
JaapvanEkris Dec 30, 2025
5a6f13c
Added additional tests
JaapvanEkris Dec 30, 2025
e78c9e7
Refactor applyFilter() function to make interface cleaner
JaapvanEkris Dec 31, 2025
becac80
Refactor applyFilter() function to make interface cleaner
JaapvanEkris Dec 31, 2025
86dec42
Improved comments
JaapvanEkris Dec 31, 2025
5ddeb35
Renamed cyclicErrorFilter
JaapvanEkris Dec 31, 2025
870878b
Added bugfix
JaapvanEkris Dec 31, 2025
bd869bb
Improved wording
JaapvanEkris Dec 31, 2025
faa1f10
Small Update to Installation Docs (#161)
DXCanas Dec 31, 2025
5e3f9a2
Improvement of restart/reset behaviour of the cyclicErrorFilter
JaapvanEkris Jan 1, 2026
62880fa
Improvement of the restart/reset behaviour
JaapvanEkris Jan 1, 2026
4a10b48
Improved handling of reset
JaapvanEkris Jan 1, 2026
5ea5a73
Improved JSDoc comments
JaapvanEkris Jan 1, 2026
acd79da
Added totalWeight() function
JaapvanEkris Jan 1, 2026
3f32581
Improved JSDoc comments
JaapvanEkris Jan 1, 2026
139477f
Removed totalWeight function
JaapvanEkris Jan 1, 2026
ef99480
Added a new test
JaapvanEkris Jan 1, 2026
58557f0
Improved JSDoc comments
JaapvanEkris Jan 1, 2026
296fb93
Improved JSDoc comments
JaapvanEkris Jan 1, 2026
2841506
Improved JSDoc comments
JaapvanEkris Jan 1, 2026
507098a
Added test for Goodness Of Fit and weights
JaapvanEkris Jan 1, 2026
1b8eb2d
Update WLSLinearSeries.test.js
JaapvanEkris Jan 1, 2026
b0397b9
Update WLSLinearSeries.test.js
JaapvanEkris Jan 1, 2026
42e1e7a
Update WLSLinearSeries.test.js
JaapvanEkris Jan 1, 2026
e20bb27
Update WLSLinearSeries.test.js
JaapvanEkris Jan 1, 2026
978f06a
Update WLSLinearSeries.test.js
JaapvanEkris Jan 1, 2026
4037232
Update WLSLinearSeries.test.js
JaapvanEkris Jan 1, 2026
75ac0bc
Update WLSLinearSeries.test.js
JaapvanEkris Jan 1, 2026
3c8dfc2
Update WLSLinearSeries.test.js
JaapvanEkris Jan 2, 2026
9d063ef
Additional tests added for WLSLinearSeries
JaapvanEkris Jan 2, 2026
8fcd307
Moved responsibility to become active from Flywheel.js to CEC Filter
JaapvanEkris Jan 2, 2026
40bc057
Moved responsibility to become active from Flywheel.js to CEC Filter
JaapvanEkris Jan 2, 2026
174512c
Introduction of Probobalistic drag calculation
JaapvanEkris Jan 2, 2026
43f508b
Fixed lint errors
JaapvanEkris Jan 2, 2026
bb25988
Added tests to test effect of weights
JaapvanEkris Jan 2, 2026
a0d4816
Fixed lint errors
JaapvanEkris Jan 2, 2026
3090b56
Update TSLinearSeries.test.js
JaapvanEkris Jan 2, 2026
ec08f36
Update TSLinearSeries.test.js
JaapvanEkris Jan 2, 2026
e3f2690
Added tests to test effect of weights
JaapvanEkris Jan 2, 2026
13928be
Exposed GoodnessOfFit series
JaapvanEkris Jan 2, 2026
69f1150
Added cyclicErrorFilter GoF as Weight for the drag calculation
JaapvanEkris Jan 2, 2026
4bfd36f
Refactoring code to make interface less stateless
JaapvanEkris Jan 2, 2026
4ae358b
Refactoring code to make interface less stateless
JaapvanEkris Jan 2, 2026
34f0ba9
Fixes ESlint errors
JaapvanEkris Jan 2, 2026
4dd70ef
Improved JSDoc comments
JaapvanEkris Jan 2, 2026
db85e5d
Fixes ESlint errors
JaapvanEkris Jan 2, 2026
d8c759c
Added link to documentation
JaapvanEkris Jan 2, 2026
ba8439f
Changed loglevel to more sane approach
JaapvanEkris Jan 4, 2026
da1704e
Changed loglevel to more sane approach
JaapvanEkris Jan 4, 2026
46ee353
Changed CEC filter size to be a setting
JaapvanEkris Jan 7, 2026
4866b52
Added systematicErrorNumberOfDatapoints setting
JaapvanEkris Jan 7, 2026
cc6f040
Added weights to the MovingWindowRegressor
JaapvanEkris Jan 7, 2026
07c37e7
Changed series.sum() implementation due to numerical instability
JaapvanEkris Jan 7, 2026
b42620d
Added weights to the TSLinearSeries
JaapvanEkris Jan 7, 2026
027afa0
Added weights to TSQuadraticSeries
JaapvanEkris Jan 7, 2026
51a2278
Improved variable naming consistency
JaapvanEkris Jan 7, 2026
469c5d8
Improved numeric robustness of angular distance calculation
JaapvanEkris Jan 7, 2026
9577b7f
Fixed Lint errors
JaapvanEkris Jan 7, 2026
f355fee
Fixed Lint errors
JaapvanEkris Jan 7, 2026
f64878a
Fixed lint errors
JaapvanEkris Jan 7, 2026
b2f1ed4
Fixed lint errors
JaapvanEkris Jan 7, 2026
6cebde7
Fixed lint errors
JaapvanEkris Jan 7, 2026
f7dbe8e
Performance improvement
JaapvanEkris Jan 7, 2026
2719e39
Update tests to reflect changed behaviour
JaapvanEkris Jan 7, 2026
9e2d30f
Added stresstest
JaapvanEkris Jan 7, 2026
0898bc0
Adaptation to new filter properties and weighted approach
JaapvanEkris Jan 7, 2026
b47b88a
Updated to reflect algorithm changes
JaapvanEkris Jan 8, 2026
09f9049
Fixed Lint warnings
JaapvanEkris Jan 8, 2026
54780d1
Fixed Lint warnings
JaapvanEkris Jan 8, 2026
7727f97
Fixes Lint error
JaapvanEkris Jan 8, 2026
3775ea6
Adaptation to algorithm improvements
JaapvanEkris Jan 8, 2026
3cec60b
Adaptation to improved algorithms
JaapvanEkris Jan 8, 2026
7afe195
Merge branch '0.9.7-(under-construction)' into 0.9.7d2---Probobalisti…
JaapvanEkris Jan 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 34 additions & 25 deletions app/engine/Flywheel.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,12 @@ export function createFlywheel (rowerSettings) {
const cyclicErrorFilter = createCyclicErrorFilter(rowerSettings, minimumDragFactorSamples, recoveryDeltaTime)
const strokedetectionMinimalGoodnessOfFit = rowerSettings.minimumStrokeQuality
const minimumRecoverySlope = createWeighedSeries(rowerSettings.dragFactorSmoothing, rowerSettings.minimumRecoverySlope)
let totalTime
let currentAngularDistance
let _deltaTimeBeforeFlank
let rawTime = 0
let rawNumberOfImpulses = 0
let totalTimeSpinning = 0
let totalNumberOfImpulses = 0
let _totalWork = 0
let _deltaTimeBeforeFlank = {}
let _angularVelocityAtBeginFlank
let _angularVelocityBeforeFlank
let _angularAccelerationAtBeginFlank
Expand All @@ -72,11 +75,8 @@ export function createFlywheel (rowerSettings) {
let _torqueBeforeFlank
let inRecoveryPhase
let maintainMetrics
let totalNumberOfImpulses
let totalTimeSpinning
let _totalWork
reset()

/**
* @param {float} dataPoint - The lenght of the impuls (currentDt) in seconds
* @description This function is called from Rower.js each time the sensor detected an impulse. It transforms this (via the buffers) into a robust flywheel position, speed and acceleration.
Expand Down Expand Up @@ -112,40 +112,41 @@ export function createFlywheel (rowerSettings) {
// value before the shift is certain to be part of a specific rowing phase (i.e. Drive or Recovery), once the buffer is filled completely
totalNumberOfImpulses += 1

_deltaTimeBeforeFlank = cyclicErrorFilter.clean.atSeriesBegin()
totalTimeSpinning += _deltaTimeBeforeFlank
_deltaTimeBeforeFlank = cyclicErrorFilter.atSeriesBegin()
totalTimeSpinning += _deltaTimeBeforeFlank.clean
_angularVelocityBeforeFlank = _angularVelocityAtBeginFlank
_angularAccelerationBeforeFlank = _angularAccelerationAtBeginFlank
// As drag is recalculated at the begin of the drive, we need to recalculate the torque
_torqueBeforeFlank = (rowerSettings.flywheelInertia * _angularAccelerationBeforeFlank + drag.weighedAverage() * Math.pow(_angularVelocityBeforeFlank, 2))

if (inRecoveryPhase) {
// Feed the drag calculation, as we didn't reset the Semaphore in the previous cycle based on the current flank
recoveryDeltaTime.push(totalTimeSpinning, _deltaTimeBeforeFlank)
recoveryDeltaTime.push(totalTimeSpinning, _deltaTimeBeforeFlank.clean, _deltaTimeBeforeFlank.goodnessOfFit)
// Feed the systematic error filter buffer
cyclicErrorFilter.recordRawDatapoint(totalNumberOfImpulses, totalTimeSpinning, cyclicErrorFilter.raw.atSeriesBegin())
cyclicErrorFilter.recordRawDatapoint(totalNumberOfImpulses, totalTimeSpinning, _deltaTimeBeforeFlank.raw)
} else {
// Accumulate the energy total as we are in the drive phase
_totalWork += Math.max(_torqueBeforeFlank * angularDisplacementPerImpulse, 0)
// Process a value in the systematic error filter buffer. We need to do this slowly to prevent radical changes which might disturbe the force curve etc.
cyclicErrorFilter.processNextRawDatapoint()
}
} else {
_deltaTimeBeforeFlank = 0
_deltaTimeBeforeFlank.clean = 0
_angularVelocityBeforeFlank = 0
_angularAccelerationBeforeFlank = 0
_torqueBeforeFlank = 0
}

const cleanCurrentDt = cyclicErrorFilter.applyFilter(dataPoint, totalNumberOfImpulses + flankLength)
totalTime += cleanCurrentDt.value
currentAngularDistance += angularDisplacementPerImpulse
rawTime += cleanCurrentDt.value
rawNumberOfImpulses++
const currentAngularDistance = rawNumberOfImpulses * angularDisplacementPerImpulse

// Let's feed the stroke detection algorithm
_deltaTime.push(totalTime, cleanCurrentDt.value)
_deltaTime.push(rawTime, cleanCurrentDt.value, cleanCurrentDt.goodnessOfFit)

// Calculate the metrics that are needed for more advanced metrics, like the foce curve
_angularDistance.push(totalTime, currentAngularDistance)
_angularDistance.push(rawTime, currentAngularDistance, cleanCurrentDt.goodnessOfFit)
_angularVelocityAtBeginFlank = _angularDistance.firstDerivative(0)
_angularAccelerationAtBeginFlank = _angularDistance.secondDerivative(0)
_torqueAtBeginFlank = (rowerSettings.flywheelInertia * _angularAccelerationAtBeginFlank + drag.weighedAverage() * Math.pow(_angularVelocityAtBeginFlank, 2))
Expand Down Expand Up @@ -190,10 +191,10 @@ export function createFlywheel (rowerSettings) {
if (rowerSettings.autoAdjustRecoverySlope) {
// We are allowed to autoadjust stroke detection slope as well, so let's do that
minimumRecoverySlope.push((1 - rowerSettings.autoAdjustRecoverySlopeMargin) * recoveryDeltaTime.slope(), recoveryDeltaTime.goodnessOfFit())
log.debug(`*** Calculated recovery slope: ${recoveryDeltaTime.slope().toFixed(6)}, Goodness of Fit: ${recoveryDeltaTime.goodnessOfFit().toFixed(4)}`)
log.trace(`*** Calculated recovery slope: ${recoveryDeltaTime.slope().toFixed(6)}, Goodness of Fit: ${recoveryDeltaTime.goodnessOfFit().toFixed(4)}`)
} else {
// We aren't allowed to adjust the slope, let's report the slope to help help the user configure it
log.debug(`*** Calculated recovery slope: ${recoveryDeltaTime.slope().toFixed(6)}, Goodness of Fit: ${recoveryDeltaTime.goodnessOfFit().toFixed(4)}, not used as autoAdjustRecoverySlope isn't set to true`)
log.trace(`*** Calculated recovery slope: ${recoveryDeltaTime.slope().toFixed(6)}, Goodness of Fit: ${recoveryDeltaTime.goodnessOfFit().toFixed(4)}, not used as autoAdjustRecoverySlope isn't set to true`)
}
} else {
// As the drag calculation is considered unreliable, we must skip updating the systematic error filter that depends on it
Expand Down Expand Up @@ -225,7 +226,7 @@ export function createFlywheel (rowerSettings) {
* @returns {float} the current DeltaTime BEFORE the flank
*/
function deltaTime () {
return _deltaTimeBeforeFlank
return _deltaTimeBeforeFlank.clean
}

/**
Expand Down Expand Up @@ -397,27 +398,35 @@ export function createFlywheel (rowerSettings) {
}
}

/**
* @param {float} slope - Recovery slope to be converted
* @returns {float} Dragfactor to be used in all calculations
* @description Helper function to convert a recovery slope into a dragfactor
*/
function slopeToDrag (slope) {
return ((slope * rowerSettings.flywheelInertia) / angularDisplacementPerImpulse)
}

/**
* @description This function is used for clearing all data, returning the flywheel.js to its initial state
*/
function reset () {
maintainMetrics = false
inRecoveryPhase = false
rawTime = 0
rawNumberOfImpulses = 0
totalTimeSpinning = 0
totalNumberOfImpulses = -1
_totalWork = 0
drag.reset()
cyclicErrorFilter.reset()
cyclicErrorFilter.applyFilter(0, flankLength - 1)
recoveryDeltaTime.reset()
_deltaTime.reset()
_angularDistance.reset()
totalTime = 0
currentAngularDistance = 0
totalNumberOfImpulses = -1
totalTimeSpinning = 0
_totalWork = 0
_deltaTime.push(0, 0)
_angularDistance.push(0, 0)
_deltaTimeBeforeFlank = 0
_deltaTimeBeforeFlank.clean = 0
_angularVelocityBeforeFlank = 0
_angularAccelerationBeforeFlank = 0
_torqueAtBeginFlank = 0
Expand Down
Loading
Loading