adds more settings, adds auto adjust for damping
This commit is contained in:
parent
1b895972af
commit
b019fb3bd4
|
|
@ -27,7 +27,7 @@ function createRowingEngine (rowerSettings) {
|
||||||
// However I still keep it constant here, as I still have to figure out the damping physics of a water rower (see below)
|
// However I still keep it constant here, as I still have to figure out the damping physics of a water rower (see below)
|
||||||
// To measure it for your rowing machine, comment in the logging at the end of "startDrivePhase" function. Then do some
|
// To measure it for your rowing machine, comment in the logging at the end of "startDrivePhase" function. Then do some
|
||||||
// strokes on the rower and estimate a value.
|
// strokes on the rower and estimate a value.
|
||||||
const omegaDotDivOmegaSquare = rowerSettings.omegaDotDivOmegaSquare
|
let omegaDotDivOmegaSquare = rowerSettings.omegaDotDivOmegaSquare
|
||||||
|
|
||||||
// The moment of inertia of the flywheel kg*m^2
|
// The moment of inertia of the flywheel kg*m^2
|
||||||
// A way to measure it is outlined here: https://dvernooy.github.io/projects/ergware/, "Flywheel moment of inertia"
|
// A way to measure it is outlined here: https://dvernooy.github.io/projects/ergware/, "Flywheel moment of inertia"
|
||||||
|
|
@ -49,13 +49,13 @@ function createRowingEngine (rowerSettings) {
|
||||||
const c = rowerSettings.magicConstant
|
const c = rowerSettings.magicConstant
|
||||||
|
|
||||||
// jMoment * ωdot = -kDamp * ω^2 during non-power part of stroke
|
// jMoment * ωdot = -kDamp * ω^2 during non-power part of stroke
|
||||||
const kDamp = jMoment * omegaDotDivOmegaSquare
|
let kDamp = jMoment * omegaDotDivOmegaSquare
|
||||||
|
|
||||||
// s = (k/c)^(1/3)*θ
|
// s = (k/c)^(1/3)*θ
|
||||||
const distancePerRevolution = 2.0 * Math.PI * Math.pow((kDamp / c), 1.0 / 3.0)
|
const distancePerRevolution = 2.0 * Math.PI * Math.pow((kDamp / c), 1.0 / 3.0)
|
||||||
|
|
||||||
let workoutHandler
|
let workoutHandler
|
||||||
const kDampEstimatorAverager = createWeightedAverager(3)
|
const kDampEstimatorAverager = createWeightedAverager(5)
|
||||||
const flankDetector = createMovingFlankDetector(rowerSettings)
|
const flankDetector = createMovingFlankDetector(rowerSettings)
|
||||||
let prevDt = rowerSettings.maximumTimeBetweenImpulses
|
let prevDt = rowerSettings.maximumTimeBetweenImpulses
|
||||||
let kPower = 0.0
|
let kPower = 0.0
|
||||||
|
|
@ -169,8 +169,15 @@ function createRowingEngine (rowerSettings) {
|
||||||
if (strokeElapsed - driveElapsed !== 0) {
|
if (strokeElapsed - driveElapsed !== 0) {
|
||||||
kDampEstimatorAverager.pushValue(kDampEstimator / (strokeElapsed - driveElapsed))
|
kDampEstimatorAverager.pushValue(kDampEstimator / (strokeElapsed - driveElapsed))
|
||||||
}
|
}
|
||||||
log.debug(`estimated kDamp: ${jMoment * (-1 * kDampEstimatorAverager.weightedAverage())}`)
|
const _kDamp = jMoment * (-1 * kDampEstimatorAverager.weightedAverage())
|
||||||
log.info(`estimated omegaDotDivOmegaSquare: ${-1 * kDampEstimatorAverager.weightedAverage()}`)
|
const _omegaDotDivOmegaSquare = -1 * kDampEstimatorAverager.weightedAverage()
|
||||||
|
log.debug(`estimated kDamp: ${_kDamp}`)
|
||||||
|
log.info(`estimated omegaDotDivOmegaSquare: ${_omegaDotDivOmegaSquare}`)
|
||||||
|
if (rowerSettings.autoAdjustDampingConstant) {
|
||||||
|
log.debug('auto adjusting kDamp and omegaDotDivOmegaSquare to new values')
|
||||||
|
kDamp = _kDamp
|
||||||
|
omegaDotDivOmegaSquare = _omegaDotDivOmegaSquare
|
||||||
|
}
|
||||||
workoutHandler.handleStrokeStateChanged({
|
workoutHandler.handleStrokeStateChanged({
|
||||||
strokeState: 'DRIVING'
|
strokeState: 'DRIVING'
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ import log from 'loglevel'
|
||||||
log.setLevel(config.loglevel.default)
|
log.setLevel(config.loglevel.default)
|
||||||
|
|
||||||
export function createGpioTimerService () {
|
export function createGpioTimerService () {
|
||||||
|
if (Gpio.accessible) {
|
||||||
|
if (config.gpioHighPriority) {
|
||||||
// setting top (near-real-time) priority for the Gpio process, as we don't want to miss anything
|
// setting top (near-real-time) priority for the Gpio process, as we don't want to miss anything
|
||||||
log.debug('setting priority for the Gpio-service to maximum (-20)')
|
log.debug('setting priority for the Gpio-service to maximum (-20)')
|
||||||
try {
|
try {
|
||||||
|
|
@ -23,16 +25,16 @@ export function createGpioTimerService () {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.debug('need root permission to set priority of Gpio-Thread')
|
log.debug('need root permission to set priority of Gpio-Thread')
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Gpio.accessible) {
|
// read the sensor data from one of the Gpio pins of Raspberry Pi
|
||||||
// mode can be rising, falling, both
|
const sensor = new Gpio(config.gpioPin, 'in', 'rising')
|
||||||
const reedSensor = new Gpio(17, 'in', 'rising')
|
|
||||||
// use hrtime for time measurement to get a higher time precision
|
// use hrtime for time measurement to get a higher time precision
|
||||||
let hrStartTime = process.hrtime()
|
let hrStartTime = process.hrtime()
|
||||||
|
|
||||||
// assumes that GPIO-Port 17 is set to pullup and reed is connected to GND
|
// assumes that GPIO-Port 17 is set to pullup and reed is connected to GND
|
||||||
// therefore the value is 1 if the reed sensor is open
|
// therefore the value is 1 if the reed sensor is open
|
||||||
reedSensor.watch((err, value) => {
|
sensor.watch((err, value) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,46 +7,60 @@
|
||||||
!!! Note that changes to this file will be OVERWRITTEN when you update to a new version
|
!!! Note that changes to this file will be OVERWRITTEN when you update to a new version
|
||||||
of Open Rowing Monitor. !!!
|
of Open Rowing Monitor. !!!
|
||||||
|
|
||||||
To change the settings you should modify the 'config.js' in this folder. Simply copy the
|
To change the settings you should modify the 'config/config.js' file. Simply copy the
|
||||||
options that you would like to change into that file. If 'config.js' does not exist, you
|
options that you would like to change into that file. If 'config.js' does not exist, you
|
||||||
can use the example file from the 'install' folder.
|
can use the example file from the 'install' folder.
|
||||||
*/
|
*/
|
||||||
import rowerProfiles from './rowerProfiles.js'
|
import rowerProfiles from './rowerProfiles.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// available log levels: trace, debug, info, warn, error, silent
|
// Available log levels: trace, debug, info, warn, error, silent
|
||||||
loglevel: {
|
loglevel: {
|
||||||
// the default loglevel
|
// The default loglevel
|
||||||
default: 'info',
|
default: 'info',
|
||||||
// the log level of of the rowing engine (stroke detection and physics model)
|
// The log level of of the rowing engine (stroke detection and physics model)
|
||||||
RowingEngine: 'warn'
|
RowingEngine: 'warn'
|
||||||
},
|
},
|
||||||
|
|
||||||
// selects the Bluetooth Low Energy Profile
|
// Defines the GPIO Pin that is used to read the sensor data from the rowing machine
|
||||||
// supported modes: FTMS, FTMSBIKE, PM5
|
// see: https://www.raspberrypi.org/documentation/usage/gpio for the pin layout of the device
|
||||||
|
// If you want to use the internal pull-up resistor of the Raspberry Pi you should
|
||||||
|
// also configure the pin for that in /boot/config.txt, i.e. 'gpio=17=pu,ip'
|
||||||
|
// see: https://www.raspberrypi.org/documentation/configuration/config-txt/gpio.md
|
||||||
|
gpioPin: 17,
|
||||||
|
|
||||||
|
// Experimental setting: enable this to boost the system level priority of the thread that
|
||||||
|
// measures the rotation speed of the flywheel. This might improve the precision of the
|
||||||
|
// measurements (especially on rowers with a fast spinning flywheel)
|
||||||
|
gpioHighPriority: false,
|
||||||
|
|
||||||
|
// Selects the Bluetooth Low Energy Profile
|
||||||
|
// Supported modes: FTMS, FTMSBIKE, PM5
|
||||||
bluetoothMode: 'FTMS',
|
bluetoothMode: 'FTMS',
|
||||||
|
|
||||||
// turn this on if you want support for Bluetooth Low Energy heart rate monitors
|
// Turn this on if you want support for Bluetooth Low Energy heart rate monitors
|
||||||
// will currenty the hear rate and battery level of the first device found
|
// Will currenty connect to the first device found
|
||||||
heartrateMonitorBLE: true,
|
heartrateMonitorBLE: true,
|
||||||
|
|
||||||
// turn this on if you want support for ANT+ heart rate monitors
|
// Turn this on if you want support for ANT+ heart rate monitors
|
||||||
// you will need an ANT+ USB stick for this to work, the following models might work:
|
// You will need an ANT+ USB stick for this to work, the following models might work:
|
||||||
// - Garmin USB or USB2 ANT+ or an off-brand clone of it (ID 0x1008)
|
// - Garmin USB or USB2 ANT+ or an off-brand clone of it (ID 0x1008)
|
||||||
// - Garmin mini ANT+ (ID 0x1009)
|
// - Garmin mini ANT+ (ID 0x1009)
|
||||||
heartrateMonitorANT: false,
|
heartrateMonitorANT: false,
|
||||||
|
|
||||||
// defines the name that is used to announce the FTMS Rower via Bluetooth Low Energy (BLE)
|
// Defines the name that is used to announce the FTMS Rower via Bluetooth Low Energy (BLE)
|
||||||
// some rowing training applications expect that the rowing device is announced with a certain name
|
// Some rowing training applications expect that the rowing device is announced with a certain name
|
||||||
ftmsRowerPeripheralName: 'OpenRowingMonitor',
|
ftmsRowerPeripheralName: 'OpenRowingMonitor',
|
||||||
|
|
||||||
// defines the name that is used to announce the FTMS Bike via Bluetooth Low Energy (BLE)
|
// Defines the name that is used to announce the FTMS Bike via Bluetooth Low Energy (BLE)
|
||||||
// most bike training applications are fine with any device name
|
// Most bike training applications are fine with any device name
|
||||||
ftmsBikePeripheralName: 'OpenRowingBike',
|
ftmsBikePeripheralName: 'OpenRowingBike',
|
||||||
|
|
||||||
// the rower specific settings. Either choose a profile from config/rowerProfiles.js or
|
// The rower specific settings. Either choose a profile from config/rowerProfiles.js or
|
||||||
// define the settings individually. If you find good settings for a new rowing device
|
// define the settings individually. If you find good settings for a new rowing device
|
||||||
// please send them to us (together with a raw recording of 10 strokes) so we can add
|
// please send them to us (together with a raw recording of 10 strokes) so we can add
|
||||||
// the device to the profiles.
|
// the device to the profiles.
|
||||||
|
// !! Only change this setting in the config/config.js file, and leave this on DEFAULT as that
|
||||||
|
// is the fallback for the default profile settings
|
||||||
rowerSettings: rowerProfiles.DEFAULT
|
rowerSettings: rowerProfiles.DEFAULT
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,19 +22,20 @@ export default {
|
||||||
minimumTimeBetweenImpulses: 0.014,
|
minimumTimeBetweenImpulses: 0.014,
|
||||||
maximumTimeBetweenImpulses: 0.5,
|
maximumTimeBetweenImpulses: 0.5,
|
||||||
// Percentage change between successive intervals
|
// Percentage change between successive intervals
|
||||||
maximumDownwardChange: 0.2, // effectively the maximum deceleration
|
maximumDownwardChange: 0.25, // effectively the maximum deceleration
|
||||||
maximumUpwardChange: 1.75, // effectively the maximum acceleration
|
maximumUpwardChange: 1.75, // effectively the maximum acceleration
|
||||||
// Settings for the rowing phase detection (in seconds)
|
flankLength: 2,
|
||||||
flankLength: 1,
|
|
||||||
numberOfErrorsAllowed: 0,
|
numberOfErrorsAllowed: 0,
|
||||||
minimumDriveTime: 0.300,
|
// Settings for the rowing phase detection (in seconds)
|
||||||
minimumRecoveryTime: 0.750,
|
minimumDriveTime: 0.500,
|
||||||
|
minimumRecoveryTime: 0.800,
|
||||||
|
|
||||||
// Needed to determine the damping constant of the rowing machine. This value can be measured in the recovery phase
|
// Needed to determine the damping constant of the rowing machine. This value can be measured in the recovery phase
|
||||||
// of the stroke (some ergometers do this constantly).
|
// of the stroke.
|
||||||
// However I still keep it constant here, as I still have to figure out the damping physics of a water rower (see below)
|
// To display it for your rowing machine, set the logging level of the RowingEngine to 'info'. Then start rowing and
|
||||||
// To measure it for your rowing machine, comment in the logging at the end of "startDrivePhase" function. Then do some
|
// you will see the measured values in the log.
|
||||||
// strokes on the rower and estimate a value.
|
// Open Rowing Monitor can also automatically adjust this value based on the measured damping. To do so, set the setting
|
||||||
|
// autoAdjustDampingConstant to true (see below).
|
||||||
omegaDotDivOmegaSquare: 0.02,
|
omegaDotDivOmegaSquare: 0.02,
|
||||||
|
|
||||||
// The moment of inertia of the flywheel kg*m^2
|
// The moment of inertia of the flywheel kg*m^2
|
||||||
|
|
@ -43,6 +44,11 @@ export default {
|
||||||
// plausibility. Note that the power also depends on omegaDotDivOmegaSquare (see above).
|
// plausibility. Note that the power also depends on omegaDotDivOmegaSquare (see above).
|
||||||
jMoment: 0.49,
|
jMoment: 0.49,
|
||||||
|
|
||||||
|
// Set this to true, if you want to automatically update omegaDotDivOmegaSquare and kDamp based on the measured
|
||||||
|
// values in the stroke recovery phase. If your rower produces stable damping values, then this could be a good
|
||||||
|
// option to dynamically adjust your measurements to the damper setting of your rower.
|
||||||
|
autoAdjustDampingConstant: false,
|
||||||
|
|
||||||
// A constant that is commonly used to convert flywheel revolutions to a rowed distance
|
// A constant that is commonly used to convert flywheel revolutions to a rowed distance
|
||||||
// see here: http://eodg.atm.ox.ac.uk/user/dudhia/rowing/physics/ergometer.html#section9
|
// see here: http://eodg.atm.ox.ac.uk/user/dudhia/rowing/physics/ergometer.html#section9
|
||||||
// Concept2 seems to use 2.8, which they admit is an arbitrary number which came close
|
// Concept2 seems to use 2.8, which they admit is an arbitrary number which came close
|
||||||
|
|
@ -66,12 +72,6 @@ export default {
|
||||||
numOfImpulsesPerRevolution: 2,
|
numOfImpulsesPerRevolution: 2,
|
||||||
minimumTimeBetweenImpulses: 0.05,
|
minimumTimeBetweenImpulses: 0.05,
|
||||||
maximumTimeBetweenImpulses: 1,
|
maximumTimeBetweenImpulses: 1,
|
||||||
maximumDownwardChange: 0.25,
|
|
||||||
maximumUpwardChange: 2,
|
|
||||||
flankLength: 2,
|
|
||||||
numberOfErrorsAllowed: 0,
|
|
||||||
minimumDriveTime: 0.500,
|
|
||||||
minimumRecoveryTime: 0.800,
|
|
||||||
omegaDotDivOmegaSquare: 0.046,
|
omegaDotDivOmegaSquare: 0.046,
|
||||||
jMoment: 0.49,
|
jMoment: 0.49,
|
||||||
liquidFlywheel: true
|
liquidFlywheel: true
|
||||||
|
|
@ -82,12 +82,6 @@ export default {
|
||||||
numOfImpulsesPerRevolution: 1,
|
numOfImpulsesPerRevolution: 1,
|
||||||
minimumTimeBetweenImpulses: 0.15,
|
minimumTimeBetweenImpulses: 0.15,
|
||||||
maximumTimeBetweenImpulses: 0.5,
|
maximumTimeBetweenImpulses: 0.5,
|
||||||
maximumDownwardChange: 0.25,
|
|
||||||
maximumUpwardChange: 1.75,
|
|
||||||
flankLength: 2,
|
|
||||||
numberOfErrorsAllowed: 0,
|
|
||||||
minimumDriveTime: 0.500,
|
|
||||||
minimumRecoveryTime: 0.800,
|
|
||||||
omegaDotDivOmegaSquare: 0.019,
|
omegaDotDivOmegaSquare: 0.019,
|
||||||
jMoment: 0.4,
|
jMoment: 0.4,
|
||||||
liquidFlywheel: true
|
liquidFlywheel: true
|
||||||
|
|
|
||||||
|
|
@ -30,5 +30,10 @@ export default {
|
||||||
jMoment: 0.3,
|
jMoment: 0.3,
|
||||||
liquidFlywheel: false
|
liquidFlywheel: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// example: set a rower profile, but overwrite some settings:
|
||||||
|
rowerSettings: Object.assign(rowerProfiles.DKNR320, {
|
||||||
|
autoAdjustDampingConstant: true
|
||||||
|
})
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue