Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dodgy LR balance in one-leg drills #3473

Closed
tkswe88 opened this issue Jun 4, 2020 · 12 comments
Closed

dodgy LR balance in one-leg drills #3473

tkswe88 opened this issue Jun 4, 2020 · 12 comments

Comments

@tkswe88
Copy link

tkswe88 commented Jun 4, 2020

There seem to be some problems in GC when analyzing data from one-leg drills. To my understanding, a left-leg drill should give an L/R balance value of 100, whereas a right-leg drill should give an L/R balance of 0.
From Favero Assioma Duo power meter pedals, the L/R balance values seem to be correctly imported to the tabular Edit view in GC. However, the L/R balance values are always wrong (at 50%) in the graphical "Ride" view during right leg drills, as shown here:
LR_balance_old
The test protocol was as follows:
5-6 min left-leg drill
7-8 min right-leg drill
9-10 min left-leg drill
11-12 min right-leg drill
13-14 min left-leg drill
15-16 min right-leg drill
You will notice that the right-leg drills do not show as such, but are displayed as perfectly balanced efforts. So, right-leg drills are not properly prepared for plotting in GC.

I had a go at fixing this issue and the following changes, shown below as output of a diff to the original files (*_bak.cpp), seem to lead to the correct or at least a better result:

diff src/Charts/AllPlot_bak.cpp src/Charts/AllPlot.cpp

l. 2042
old: if (!objects->balanceArray.empty()) totalBalance += (objects->balanceArray[i]>0?objects->balanceArray[i]:50);
new: if (!objects->balanceArray.empty()) totalBalance += ((objects->balanceArray[i]>=0 && objects->balanceArray[i]<=100)?objects->balanceArray[i]:50);

l. 2112
old: totalBalance -= (dp.lrbalance>0?dp.lrbalance:50);
new: totalBalance -= ((dp.lrbalance>=0 && dp.lrbalance<=100)?dp.lrbalance:50);

l. 2202
old: if (balance == 0) {
new: if (balance < 0 || balance > 100) {

l. 2313
old: if (dp->lrbalance == RideFile::NA || (dp->lrbalance == 0)) {
new: if (dp->lrbalance == RideFile::NA || (dp->lrbalance < 0) || (dp->lrbalance > 100)) {

diff src/Metrics/BasicRideMetrics_bak.cpp src/Metrics/BasicRideMetrics.cpp

l. 2803
old: if (point->lte && point->watts > 0.0f && point->cad && point->lrbalance > 0.0f && point->lrbalance < 100.0f) {
new: if (point->lte && point->watts > 0.0f && point->cad && point->lrbalance >= 0.0f && point->lrbalance <= 100.0f) {

l. 2857
old: if (point->rte && point->watts > 0.0f && point->cad && point->lrbalance > 0.0f && point->lrbalance < 100.0f) {
new: if (point->rte && point->watts > 0.0f && point->cad && point->lrbalance >= 0.0f && point->lrbalance <= 100.0f) {

l. 2911
old: if (point->lps && point->watts > 0.0f && point->cad && point->lrbalance > 0.0f && point->lrbalance < 100.0f) {
new: if (point->lps && point->watts > 0.0f && point->cad && point->lrbalance >= 0.0f && point->lrbalance <= 100.0f) {

l. 2965
old: if (point->rps && point->watts > 0.0f && point->cad && point->lrbalance > 0.0f && point->lrbalance < 100.0f) {
new: if (point->rps && point->watts > 0.0f && point->cad && point->lrbalance >= 0.0f && point->lrbalance <= 100.0f) {

Uisng these changes, the graphical display looks like this:
LR_balance_new

The modifications in BasicRideMetrics.cpp are related to issues #2955 and #3104. To get correct values for one-leg drills, it seems necessary to include L/R balance values of 0 and 100. It was great, if @jgpallero and @amtriathlon could have a look at this.

I am unsure whether my modifications are a complete or just a partial fix, and I do not have any insight in how general this problem is. But, I guess it is a general one.

@jgpallero
Copy link
Contributor

jgpallero commented Jun 5, 2020 via email

@tkswe88
Copy link
Author

tkswe88 commented Jun 5, 2020

@jgpallero: Many thanks for the comment. Please send the screenshot as well.

@jgpallero
Copy link
Contributor

Oh, excuse me. I answered via gmail and I can see the attached files are not present here. Now I attach here the screenshot and also a couple of files containing a ride and a small Matlab/Octave script with different computations of the L/R balance
balance
m.zip

@tkswe88
Copy link
Author

tkswe88 commented Jun 18, 2020

@jgpallero, many thanks for your recent post and sorry for being late with my response. I have some general difficulties accepting the idea that the L/R balance value should be 0 if no force is applied. At least from what the Favero Assioma Duos produce, right-leg drills give an L/R balance of 0, whereas left-leg drills give an L/R balance of 100. So, 0 values are meaningful, when power is applied (in right-leg drills), and, in my opinion, 0 values should not have a potential double meaning to indicate periods when no power is applied. Further, all L/R balance values are equally meaningless, when no power is applied. However, to give a "neutral" impression in the plots assigning an L/R balance of 50 may be more appealing to the eye than 0 in this case.

To me it seems, the best way of computing averages, would be to check for both power and cadence being larger than 0 and to allow for L/R balance in the interval [0,100], i.e. including end points to allow for one-leg drills. This approach would only leave rows 71 - 74 from your example as problematic, where very low force is applied. One possible way around this would be to check for the force being larger than a small value, say 10 W, rather than 0 W. It might be meaningful to have a user-provided threshold value. Except for the last point, I think I could provide suggestions for changes to the code. However, as said before, I may have missed a few instances in the code, where changes would have to be made.

@amtriathlon
Copy link
Member

It was great, if @jgpallero and @amtriathlon could have a look at this.

Can you attach the file recorded during the test?

@jgpallero
Copy link
Contributor

@jgpallero, many thanks for your recent post and sorry for being late with my response. I have some general difficulties accepting the idea that the L/R balance value should be 0 if no force is applied. At least from what the Favero Assioma Duos produce, right-leg drills give an L/R balance of 0, whereas left-leg drills give an L/R balance of 100. So, 0 values are meaningful, when power is applied (in right-leg drills), and, in my opinion, 0 values should not have a potential double meaning to indicate periods when no power is applied.

The FIT file format only stores balance for one leg and the value for the other is computed through substraction of 100. This is documented in the file Profile.xslx in the FIT file format documentation (https://www.thisisant.com/resources/fit-sdk/). The FIT file can store the balance for the left leg or for the right, but not for both. GC stores the balance referred to the left leg. So, a 0 value has two possible meanings: no force applied at all of force applied only to the right pedal. This ambiguity can be overcomed, as you suggest, checking for watts>0 and cadence>0, and this is taken into account in
https://github.com/GoldenCheetah/GoldenCheetah/blob/master/src/Metrics/LeftRightBalance.cpp#L63

To me it seems, the best way of computing averages, would be to check for both power and cadence being larger than 0 and to allow for L/R balance in the interval [0,100], i.e. including end points to allow for one-leg drills. This approach would only leave rows 71 - 74 from your example as problematic, where very low force is applied. One possible way around this would be to check for the force being larger than a small value, say 10 W, rather than 0 W. It might be meaningful to have a user-provided threshold value. Except for the last point, I think I could provide suggestions for changes to the code. However, as said before, I may have missed a few instances in the code, where changes would have to be made.

The problem is that although watts>0 and cadence>0 is used, Favero stores randomly values of 100 in lrbalance. I've made some checks using a small threshold as you suggest (you can made tests using the Matlab code I uploaded in my last mail), but the results are not good. The only solution I've found for a correct balance computation is to exclude 0 and 100 values

@tkswe88
Copy link
Author

tkswe88 commented Jun 30, 2020

To @amtriathlon and @jgpallero, please find attached the fit recorded during the one-leg drills on a home trainer.
2020-05-18-21-47-57_2020_05_18_21_47_57.zip

@amtriathlon
Copy link
Member

The problem is that although watts>0 and cadence>0 is used, Favero stores randomly values of 100 in lrbalance. I've made some checks using a small threshold as you suggest (you can made tests using the Matlab code I uploaded in my last mail), but the results are not good. The only solution I've found for a correct balance computation is to exclude 0 and 100 values

I didn't followed all the discussion, tl:dr, but to adapt the code to the fact "Favero stores randomly values of 100 in lrbalance" doesn't seem a good solution to me. Data processors are the tools to fix anomalies in data.

@jgpallero
Copy link
Contributor

The problem is that although watts>0 and cadence>0 is used, Favero stores randomly values of 100 in lrbalance. I've made some checks using a small threshold as you suggest (you can made tests using the Matlab code I uploaded in my last mail), but the results are not good. The only solution I've found for a correct balance computation is to exclude 0 and 100 values

I didn't followed all the discussion, tl:dr, but to adapt the code to the fact "Favero stores randomly values of 100 in lrbalance" doesn't seem a good solution to me. Data processors are the tools to fix anomalies in data.

Yes, I totally agree

@tkswe88
Copy link
Author

tkswe88 commented Jul 28, 2020

@jgpallero and @amtriathlon: I also agree that anomalies should be fixed using data processors.

However, to me the problem seems more fundamental than taking care of random lrbalance values of 100 from Favero pedals for low values of force. As the code is now, I think, it does not correctly plot right-leg drills in the Ride panel, it does not correctly compute averages of l/r balance, l/r pedal smoothness and l/r torque effectiveness for right-leg or left-leg drills. More importantly, I think this problem will occur for many power meters not just the Assioma Duos. I just do not have other dual-sided PMs to test this with.
@jgpallero: I had a look at your example and matlab scripts and also realised that setting a small power threshold does not help. Would it be an option to weight the l/r balance values by the energy expenditure for the period representative of each sample (i.e. the sample spacing, typically 1 s) and then divide the weighted sum by total energy expenditure? Doing this you would down-weight the samples with low power values and avoid setting a threshold value.

I realised I forgot to mention the change I made to LeftRightBalance.cpp, which is as follows:

diff LeftRightBalance_bak.cpp LeftRightBalance.cpp
63c63
old: if (((point->watts > 0.0f && point->cad) || (point->rcontact && point->rcad)) && point->lrbalance > 0.0f && point->lrbalance < 100.0f) {
new: if (((point->watts > 0.0f && point->cad) || (point->rcontact && point->rcad)) && point->lrbalance >= 0.0f && point->lrbalance <= 100.0f) {

@amtriathlon
Copy link
Member

Fixed by b29f72d, the attached example now looks like this:
Screenshot from 2021-01-14 20-21-47

amtriathlon added a commit that referenced this issue Jan 14, 2021
Invalid values are now mapped to RideFile::NA on import,
0 and 100 are valid values and should not be excluded.
Fixes #3473
@tkswe88
Copy link
Author

tkswe88 commented Jan 15, 2021

@grauser @amtriathlon Many thanks for fixing this issue. Splendid work!

ssj2son-gohan-ne added a commit to ssj2son-gohan-ne/GoldenCheetah that referenced this issue Jan 15, 2021
* master:
  FixGaps: improve interpolation of left/right balance for smart recording Similar to 2b6473d
  LRBalance related metrics check for RideFile::NA Invalid values are now mapped to RideFile::NA on import, 0 and 100 are valid values and should not be excluded. Fixes GoldenCheetah#3473
  TRAIN - Fix LRBalance from ANT+ channels LRBalance is left side contribution while the ANT+ messages carries right side pedalPower contribution, so it needs to be converted. When not available RideFile::NA is used since 0 means 100% right. Complementes b29f72d, Fixes GoldenCheetah#3017
  Estimate CP for Runs - Increase distance precision to 3 digits Distance is in km/mi and 1 digit is not enough to use timed tests to exhaustion, for distance based TTs likely this is not an issue.
  Fit : improve interpolation of left/right balance for smart recording
  FIT : add some manufacturers
  RideFitFile :   - Add Right contribution bit in FIT exported   - Don't asssume left data if right side not confirmed   - Plot 0 left/Balance
  Daum: Improve support for more cockpits (GoldenCheetah#3775)
  User PIE and BAR Charts honor x-axis Date and Time settings So labels/categories are displayed in Time/Date format in a similar way to Line and Scatter charts.
  Add screenshots for the wiki - New Metric Trends Curves [skip ci]
  Upgraded embedded Python to 3.7.9 for Windows builds [skip travis]
human705 pushed a commit to human705/GoldenCheetah that referenced this issue Jan 20, 2021
Invalid values are now mapped to RideFile::NA on import,
0 and 100 are valid values and should not be excluded.
Fixes GoldenCheetah#3473
human705 pushed a commit to human705/GoldenCheetah that referenced this issue Feb 9, 2021
Invalid values are now mapped to RideFile::NA on import,
0 and 100 are valid values and should not be excluded.
Fixes GoldenCheetah#3473
sisao pushed a commit to sisao/GoldenCheetah that referenced this issue Jun 10, 2021
Invalid values are now mapped to RideFile::NA on import,
0 and 100 are valid values and should not be excluded.
Fixes GoldenCheetah#3473
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants