Change to use Lin. TS for Drag calculation

Changing the drag and recovery slope calculation from OLS to Linear Theil-Sen
This commit is contained in:
Jaap van Ekris 2024-01-24 15:50:09 +01:00 committed by GitHub
parent 02fa5293f2
commit b11a63a8fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 23 additions and 17 deletions

View File

@ -23,6 +23,7 @@
import loglevel from 'loglevel'
import { createStreamFilter } from './utils/StreamFilter.js'
import { createOLSLinearSeries } from './utils/OLSLinearSeries.js'
import { createTSLinearSeries } from './utils/FullTSLinearSeries.js'
import { createTSQuadraticSeries } from './utils/FullTSQuadraticSeries.js'
import { createWeighedSeries } from './utils/WeighedSeries.js'
@ -35,13 +36,14 @@ function createFlywheel (rowerSettings) {
const minumumTorqueBeforeStroke = rowerSettings.minumumForceBeforeStroke * (rowerSettings.sprocketRadius / 100)
const currentDt = createStreamFilter(rowerSettings.smoothing, rowerSettings.maximumTimeBetweenImpulses)
const _deltaTime = createOLSLinearSeries(flankLength)
// const _deltaTime = createTSLinearSeries(flankLength)
const _angularDistance = createTSQuadraticSeries(flankLength)
const _angularVelocityMatrix = []
const _angularAccelerationMatrix = []
const drag = createStreamFilter(rowerSettings.dragFactorSmoothing, (rowerSettings.dragFactor / 1000000))
const recoveryDeltaTime = createOLSLinearSeries()
const drag = createWeighedSeries(rowerSettings.dragFactorSmoothing, (rowerSettings.dragFactor / 1000000))
const recoveryDeltaTime = createTSLinearSeries()
const strokedetectionMinimalGoodnessOfFit = rowerSettings.minimumStrokeQuality
const minumumRecoverySlope = createStreamFilter(rowerSettings.dragFactorSmoothing, rowerSettings.minumumRecoverySlope)
const minumumRecoverySlope = createWeighedSeries(rowerSettings.dragFactorSmoothing, rowerSettings.minumumRecoverySlope)
let _deltaTimeBeforeFlank
let _angularVelocityAtBeginFlank
let _angularVelocityBeforeFlank
@ -117,8 +119,8 @@ function createFlywheel (rowerSettings) {
}
// Let's make room for a new set of values for angular velocity and acceleration
_angularVelocityMatrix[_angularVelocityMatrix.length] = createWeighedSeries()
_angularAccelerationMatrix[_angularAccelerationMatrix.length] = createWeighedSeries()
_angularVelocityMatrix[_angularVelocityMatrix.length] = createWeighedSeries(flankLength, 0)
_angularAccelerationMatrix[_angularAccelerationMatrix.length] = createWeighedSeries(flankLength, 0)
let i = 0
const goodnessOfFit = _angularDistance.goodnessOfFit()
@ -133,7 +135,7 @@ function createFlywheel (rowerSettings) {
_angularAccelerationAtBeginFlank = _angularAccelerationMatrix[0].weighedAverage()
// And finally calculate the torque
_torqueAtBeginFlank = (rowerSettings.flywheelInertia * _angularAccelerationAtBeginFlank + drag.clean() * Math.pow(_angularVelocityAtBeginFlank, 2))
_torqueAtBeginFlank = (rowerSettings.flywheelInertia * _angularAccelerationAtBeginFlank + drag.weighedAverage() * Math.pow(_angularVelocityAtBeginFlank, 2))
}
function maintainStateOnly () {
@ -153,24 +155,28 @@ function createFlywheel (rowerSettings) {
// Completion of the recovery phase
inRecoveryPhase = false
// As goodnessOfFit is calculated in-situ (for 220 datapoints on a C2) and is CPU intensive, we only calculate it only once and reuse the cached value
const goodnessOfFit = recoveryDeltaTime.goodnessOfFit()
// Calculation of the drag-factor
if (rowerSettings.autoAdjustDragFactor && recoveryDeltaTime.length() > minimumDragFactorSamples && recoveryDeltaTime.slope() > 0 && (!drag.reliable() || recoveryDeltaTime.goodnessOfFit() >= rowerSettings.minimumDragQuality)) {
drag.push(slopeToDrag(recoveryDeltaTime.slope()))
log.debug(`*** Calculated drag factor: ${(slopeToDrag(recoveryDeltaTime.slope()) * 1000000).toFixed(4)}, no. samples: ${recoveryDeltaTime.length()}, Goodness of Fit: ${recoveryDeltaTime.goodnessOfFit().toFixed(4)}`)
if (rowerSettings.autoAdjustDragFactor && recoveryDeltaTime.length() > minimumDragFactorSamples && recoveryDeltaTime.slope() > 0 && (!drag.reliable() || goodnessOfFit >= rowerSettings.minimumDragQuality)) {
drag.push(slopeToDrag(recoveryDeltaTime.slope()), goodnessOfFit)
log.debug(`*** Calculated drag factor: ${(slopeToDrag(recoveryDeltaTime.slope()) * 1000000).toFixed(4)}, no. samples: ${recoveryDeltaTime.length()}, Goodness of Fit: ${goodnessOfFit.toFixed(4)}`)
if (rowerSettings.autoAdjustRecoverySlope) {
// We are allowed to autoadjust stroke detection slope as well, so let's do that
minumumRecoverySlope.push((1 - rowerSettings.autoAdjustRecoverySlopeMargin) * recoveryDeltaTime.slope())
log.debug(`*** Calculated recovery slope: ${recoveryDeltaTime.slope().toFixed(6)}, Goodness of Fit: ${recoveryDeltaTime.goodnessOfFit().toFixed(4)}`)
minumumRecoverySlope.push((1 - rowerSettings.autoAdjustRecoverySlopeMargin) * recoveryDeltaTime.slope(), goodnessOfFit)
log.debug(`*** Calculated recovery slope: ${recoveryDeltaTime.slope().toFixed(6)}, Goodness of Fit: ${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.debug(`*** Calculated recovery slope: ${recoveryDeltaTime.slope().toFixed(6)}, Goodness of Fit: ${goodnessOfFit.toFixed(4)}, not used as autoAdjustRecoverySlope isn't set to true`)
}
} else {
if (!rowerSettings.autoAdjustDragFactor) {
// autoAdjustDampingConstant = false, thus the update is skipped, but let's log the dragfactor anyway
log.debug(`*** Calculated drag factor: ${(slopeToDrag(recoveryDeltaTime.slope()) * 1000000).toFixed(4)}, slope: ${recoveryDeltaTime.slope().toFixed(8)}, not used because autoAdjustDragFactor is not true`)
} else {
log.debug(`*** Calculated drag factor: ${(slopeToDrag(recoveryDeltaTime.slope()) * 1000000).toFixed(4)}, not used because reliability was too low. no. samples: ${recoveryDeltaTime.length()}, fit: ${recoveryDeltaTime.goodnessOfFit().toFixed(4)}`)
log.debug(`*** Calculated drag factor: ${(slopeToDrag(recoveryDeltaTime.slope()) * 1000000).toFixed(4)}, not used because reliability was too low. no. samples: ${recoveryDeltaTime.length()}, fit: ${goodnessOfFit.toFixed(4)}`)
}
}
}
@ -217,7 +223,7 @@ function createFlywheel (rowerSettings) {
function dragFactor () {
// Ths function returns the current dragfactor of the flywheel
return drag.clean()
return drag.weighedAverage()
}
function isDwelling () {
@ -243,9 +249,9 @@ function createFlywheel (rowerSettings) {
}
function isUnpowered () {
if (deltaTimeSlopeAbove(minumumRecoverySlope.clean()) && torqueAbsent() && _deltaTime.length() >= flankLength) {
if (deltaTimeSlopeAbove(minumumRecoverySlope.weighedAverage()) && torqueAbsent() && _deltaTime.length() >= flankLength) {
// We reached the minimum number of increasing currentDt values
// log.info(`*** INFO: recovery detected based on due to slope exceeding recoveryslope = ${deltaTimeSlopeAbove(minumumRecoverySlope.clean())}, exceeding minumumForceBeforeStroke = ${torqueAbsent()}`)
// log.info(`*** INFO: recovery detected based on due to slope exceeding recoveryslope = ${deltaTimeSlopeAbove(minumumRecoverySlope.weighedAverage())}, exceeding minumumForceBeforeStroke = ${torqueAbsent()}`)
return true
} else {
return false
@ -253,7 +259,7 @@ function createFlywheel (rowerSettings) {
}
function isPowered () {
if ((deltaTimeSlopeBelow(minumumRecoverySlope.clean()) && torquePresent()) || _deltaTime.length() < flankLength) {
if ((deltaTimeSlopeBelow(minumumRecoverySlope.weighedAverage()) && torquePresent()) || _deltaTime.length() < flankLength) {
return true
} else {
return false