diff --git a/app/engine/RowingEngine.js b/app/engine/RowingEngine.js index 822dd11..3e4ed8a 100644 --- a/app/engine/RowingEngine.js +++ b/app/engine/RowingEngine.js @@ -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) // 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. - const omegaDotDivOmegaSquare = rowerSettings.omegaDotDivOmegaSquare + let omegaDotDivOmegaSquare = rowerSettings.omegaDotDivOmegaSquare // 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" @@ -49,13 +49,13 @@ function createRowingEngine (rowerSettings) { const c = rowerSettings.magicConstant // jMoment * ωdot = -kDamp * ω^2 during non-power part of stroke - const kDamp = jMoment * omegaDotDivOmegaSquare + let kDamp = jMoment * omegaDotDivOmegaSquare // s = (k/c)^(1/3)*θ const distancePerRevolution = 2.0 * Math.PI * Math.pow((kDamp / c), 1.0 / 3.0) let workoutHandler - const kDampEstimatorAverager = createWeightedAverager(3) + const kDampEstimatorAverager = createWeightedAverager(5) const flankDetector = createMovingFlankDetector(rowerSettings) let prevDt = rowerSettings.maximumTimeBetweenImpulses let kPower = 0.0 @@ -169,8 +169,15 @@ function createRowingEngine (rowerSettings) { if (strokeElapsed - driveElapsed !== 0) { kDampEstimatorAverager.pushValue(kDampEstimator / (strokeElapsed - driveElapsed)) } - log.debug(`estimated kDamp: ${jMoment * (-1 * kDampEstimatorAverager.weightedAverage())}`) - log.info(`estimated omegaDotDivOmegaSquare: ${-1 * kDampEstimatorAverager.weightedAverage()}`) + const _kDamp = jMoment * (-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({ strokeState: 'DRIVING' }) diff --git a/app/gpio/GpioTimerService.js b/app/gpio/GpioTimerService.js index 1e1a5b5..e73646c 100644 --- a/app/gpio/GpioTimerService.js +++ b/app/gpio/GpioTimerService.js @@ -15,24 +15,26 @@ import log from 'loglevel' log.setLevel(config.loglevel.default) export function createGpioTimerService () { - // 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)') - try { - // setting priority of current process - os.setPriority(-20) - } catch (err) { - log.debug('need root permission to set priority of Gpio-Thread') - } - if (Gpio.accessible) { - // mode can be rising, falling, both - const reedSensor = new Gpio(17, 'in', 'rising') + if (config.gpioHighPriority) { + // 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)') + try { + // setting priority of current process + os.setPriority(-20) + } catch (err) { + log.debug('need root permission to set priority of Gpio-Thread') + } + } + + // read the sensor data from one of the Gpio pins of Raspberry Pi + const sensor = new Gpio(config.gpioPin, 'in', 'rising') // use hrtime for time measurement to get a higher time precision let hrStartTime = process.hrtime() // 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 - reedSensor.watch((err, value) => { + sensor.watch((err, value) => { if (err) { throw err } diff --git a/config/default.config.js b/config/default.config.js index dec3569..ef2b03b 100644 --- a/config/default.config.js +++ b/config/default.config.js @@ -7,46 +7,60 @@ !!! Note that changes to this file will be OVERWRITTEN when you update to a new version 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 can use the example file from the 'install' folder. */ import rowerProfiles from './rowerProfiles.js' export default { - // available log levels: trace, debug, info, warn, error, silent + // Available log levels: trace, debug, info, warn, error, silent loglevel: { - // the default loglevel + // The default loglevel 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' }, - // selects the Bluetooth Low Energy Profile - // supported modes: FTMS, FTMSBIKE, PM5 + // Defines the GPIO Pin that is used to read the sensor data from the rowing machine + // 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', - // 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 + // Turn this on if you want support for Bluetooth Low Energy heart rate monitors + // Will currenty connect to the first device found heartrateMonitorBLE: true, - // 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: + // 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: // - Garmin USB or USB2 ANT+ or an off-brand clone of it (ID 0x1008) // - Garmin mini ANT+ (ID 0x1009) heartrateMonitorANT: false, - // 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 + // 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 ftmsRowerPeripheralName: 'OpenRowingMonitor', - // 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 + // 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 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 // please send them to us (together with a raw recording of 10 strokes) so we can add // 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 } diff --git a/config/rowerProfiles.js b/config/rowerProfiles.js index db6a73e..fe971e3 100644 --- a/config/rowerProfiles.js +++ b/config/rowerProfiles.js @@ -22,19 +22,20 @@ export default { minimumTimeBetweenImpulses: 0.014, maximumTimeBetweenImpulses: 0.5, // 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 - // Settings for the rowing phase detection (in seconds) - flankLength: 1, + flankLength: 2, numberOfErrorsAllowed: 0, - minimumDriveTime: 0.300, - minimumRecoveryTime: 0.750, + // Settings for the rowing phase detection (in seconds) + 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 - // of the stroke (some ergometers do this constantly). - // 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 - // strokes on the rower and estimate a value. + // of the stroke. + // To display it for your rowing machine, set the logging level of the RowingEngine to 'info'. Then start rowing and + // you will see the measured values in the log. + // 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, // 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). 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 // 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 @@ -66,12 +72,6 @@ export default { numOfImpulsesPerRevolution: 2, minimumTimeBetweenImpulses: 0.05, maximumTimeBetweenImpulses: 1, - maximumDownwardChange: 0.25, - maximumUpwardChange: 2, - flankLength: 2, - numberOfErrorsAllowed: 0, - minimumDriveTime: 0.500, - minimumRecoveryTime: 0.800, omegaDotDivOmegaSquare: 0.046, jMoment: 0.49, liquidFlywheel: true @@ -82,12 +82,6 @@ export default { numOfImpulsesPerRevolution: 1, minimumTimeBetweenImpulses: 0.15, maximumTimeBetweenImpulses: 0.5, - maximumDownwardChange: 0.25, - maximumUpwardChange: 1.75, - flankLength: 2, - numberOfErrorsAllowed: 0, - minimumDriveTime: 0.500, - minimumRecoveryTime: 0.800, omegaDotDivOmegaSquare: 0.019, jMoment: 0.4, liquidFlywheel: true diff --git a/install/config.js b/install/config.js index a31394e..7194ef7 100644 --- a/install/config.js +++ b/install/config.js @@ -30,5 +30,10 @@ export default { jMoment: 0.3, liquidFlywheel: false } + + // example: set a rower profile, but overwrite some settings: + rowerSettings: Object.assign(rowerProfiles.DKNR320, { + autoAdjustDampingConstant: true + }) */ }