openrowingmonitor/docs/README_concept2_predictions.md

278 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Wattage predictions
See:
* [concept2_prediction.py](concept2_prediction.py)
* [concept2_prediction.js](concept2_prediction.js)
## Zones
See:
* Indoor Rowing Training Guide, Version 2 by Terry ONeill and Alex Skelton.
* [Interactive_Training_Plans.xlsx](Interact_Training_Plans.xlsx)
Heart rate zones are calculated based on HRR calculated from:
Resting heart rate (RHR) 58
Maximum heart rate (MHR) 165
Heart rate reserve (HRR) (MHR minus RHR) 107
| Training Band | s.min-1 | %HRR | %2k (W) | range |
|------------------------------|---------|--------|---------------|-------|
| Oxygen Utilisation 2 (UT2) | 16-20 | 65-70 | 45-60 | 15% |
| Oxygen Utilisation 1 (UT1) | 20-24 | 70-80 | 60-70 | 10% |
| Anaerobic Threshold (AT) | 24-28 | 80-85 | 70-80 | 10% |
| VO2 Max/Transport (TR) | 28-32 | 85-95 | 80-105 | 25% |
| Peak Power/Anaerobic (AN) | 32+ | 95-100 | 105-115 | 10% |
*s.min-1 is scientific notation for strokes/minute
**%2k (w) is the % of power output, measured in watts of a 2000m test from
this it is possible to calculate pace (time/500m)
***HRR% is Heart Rate Reserve %. When resting and maximum heart rates are know
it is possible to calculate the heart rate at which one should train to have
the most appropriate effect on the bodys systems.
## Example 1
### Heart rate 132 for 110W after 10 minutes
UT2 is 65%-70%, so 128-133 bpm.
132 is 4 (132 minus 128) into UT2 range of 5 (133 minus 128)
UT2 power is 45%-60% of max 2k power.
UT2 power range is 15% (60% minus 45%)
so 132 bmp should be 4 fifths into that 15% = 12%
so expected power is 110W/(45%+12%) = 193W
## Example 2
### Take the result of example 1 and a zone and provide a suitable training session
Given zone: UT2 is midpoint of range:
0.5*(45+60) = 52.5% of 192W = 101.3W
Given zone: AT:
0.5*(70+80) = 75% of 193W = 144.7W
## Examples 3
### Return a training zone from a [training table](Interactive_Training_Plans.xlsx)
See: [parse_ITP_spreadsheet.py](parse_ITP_spreadsheet.py) for a pandas
dataframe parsing and querying the spreadsheet.
Given a level 1-5, a session number and a week number return the associated
session eg 3x16'UT1
#### Available Levels
Level 1 | 3 Sessions | 26 Weeks
Level 2 | 3 Sessions | 26 Weeks
Level 2 | 4 Sessions | 26 Weeks
Level 3 | 4 Sessions | 26 Weeks
Level 4 | 4 Sessions | 26 Weeks
Level 4 | 5 Sessions | 26 Weeks
Level 4 | 6 Sessions | 26 Weeks
Level 5 | 6 Sessions | 26 Weeks
Level 5 | 7 Sessions | 26 Weeks
Level 5 | 8 Sessions | 26 Weeks
cat level-1_3-session_26-weeks.csv
```csv
"Interactive 2,000m Programme",,,
2000m 26 Week Training Programme,,,
"Athlete Level 1, 3 Sessions a Week",,,
Week,Session 1,Session 2,Session 3
1,TEST,12'UT2,15'UT2
2,14'UT2,16'UT2,18'UT2
3,17'UT2,19'UT2,20'UT2
4,10'UT1,25'UT2,30'UT2
5,12'UT1,18'UT1,8'AT
6,30'UT2,2x10'UT1,2x7'AT
7,15'UT1,20'UT2,7'AT
8,18'UT1,25UT2,9'AT
9,4x2'TR,30'UT2,2X12'UT1
10,2x2'TR,15'UT1,20'UT2
11,4x2'TR,18'UT1,25'UT2
12,6x2'TR,2X12'UT1,30'UT2
13,TEST,2x3'TR,2x7'AT
14,4x2'TR,16'UT1,25'UT2
15,2x4'TR,2x12'UT1,3x7'AT
16,6x2'TR,2x8'UT1,20'UT2
17,2x9'AT,18'UT1,30'UT2
18,2x10'AT,3x2'TR,20'UT1
19,4x1.5'AN,2x12UT1,2x8'AT
20,3x2'TR,25'UT1,2x9'AT
21,2x4'TR,30'UT2,2x10'AT
22,2x4'TR,15'UT1,2x7'AT
23,30'UT2,18'UT1,2x9'AT
24,3x2'TR,30'UT2,2x12'UT1
25,5x2'TR,6x1.5'AN,3x3'TR
26,2x1.5'AN,3x45s AN,RACE
```
Interactive 2,000m Programme
2000m 26 Week Training Programme
Athlete Level 5, 8 Sessions a Week
| Week | Session 1 | Session 2 | Session 3 | Session 4 | Session 5 | Session 6 | Session 7 | Session 8 |
|------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
| 1 | TEST | 2x15'UT1 | 55'UT2 | 2x18'UT1 | 68'UT2 | 3x15'UT1 | 55'UT2 | 2x15'UT1 |
| 2 | 65'UT2 | 2x21'UT1 | 72'UT2 | 3x16'UT1 | 80'UT2 | 4x13'UT1 | 72'UT2 | 3x12'UT1 |
| 3 | 76'UT2 | 3x17'UT1 | 85'UT2 | 3x19'UT1 | 90'UT2 | 3x20'UT1 | 85'UT2 | 2x25'UT1 |
| 4 | 45'UT2 | 2x15'UT1 | 2x6'AT | 3x12'UT1 | 2x7'AT | 3x15'UT1 | 2x10'AT | 3x12'UT2 |
| 5 | 65'UT2 | 2x24'UT1 | 4x5'AT | 3x16'UT1 | 3x8'AT | 3x18'UT1 | 2x12'AT | 3x16'UT1 |
| 6 | 76'UT2 | 2x25'UT1 | 3x7'AT | 3x19'UT1 | 3x10'AT | 5x12'UT1 | 3x8'AT | 3x18'UT1 |
| 7 | 45'UT2 | 2x15'UT1 | 2x8'AT | 3x12'UT1 | 2x10'AT | 3x15'UT1 | 2x9'AT | 2x3'TR |
| 8 | 65'UT2 | 3x14'UT1 | 2x9'AT | 75'UT2 | 2x8' AT | 4x14'UT1 | 2x7'AT | 3x4'TR |
| 9 | 76'UT2 | 3x17'UT1 | 3x7'AT | 90'UT2 | 4x5'TR | 4X15'UT1 | 3x8'AT | 4x4'TR |
| 10 | 45'UT2 | 2x15'UT1 | 2x8'AT | 60'UT2 | 4x2'TR | 3x12'UT1 | 2x9'AT | 3x3'TR |
| 11 | 65'UT2 | 3x15'UT1 | 2x10'AT | 75'UT2 | 4x3'TR | 3x15'UT1 | 2x10'AT | 4x3'TR |
| 12 | 75'UT2 | 4x15'UT1 | 3x8'AT | 90'UT2 | 4x4'TR | 4x12'UT1 | 3x10'AT | 5x4'TR |
| 13 | TEST | 2X15'UT1 | 2x8'AT | 60'UT2 | 3x1'AN | 3x15'UT1 | 2x8'AT | 4x2'TR |
| 14 | 65'UT2 | 3X15'UT1 | 2x10'AT | 75'UT2 | 4x1.5'AN | 4x12'UT1 | 3x7'AT | 6x2'TR |
| 15 | 75'UT2 | 5x12'UT1 | 3x10'AT | 90'UT2 | 6x1'AN | 5x12'UT1 | 3x10'AT | 6x4'TR |
| 16 | 45'UT2 | 2X15'UT1 | 2x9'AT | 60'UT2 | 8x45sAN | 4x14'UT1 | 2x10'AT | 5x2'TR |
| 17 | 65'UT2 | 3X15'UT1 | 3x10'AT | 75'UT2 | 6x1.5'AN | 3x12'UT1 | 3x8'AT | 6x3'TR |
| 18 | 75'UT2 | 4x15'UT1 | 4x8'AT | 90'UT2 | 8x1'AN | 2x15'UT1 | 4x9'AT | 7x4'TR |
| 19 | 45'UT2 | 2X15'UT1 | 2x10'AT | 60'UT2 | 4x1.5'AN | 3x12'UT1 | 3x8'AT | 6x2'TR |
| 20 | 65'UT2 | 3X15'UT1 | 3x12'AT | 75'UT2 | 6x1'AN | 3x15'UT1 | 3x10'AT | 7x3'TR |
| 21 | 75'UT2 | 5x12'UT1 | 5x8'AT | 90'UT2 | 8x45sAN | 5x12'UT1 | 4x10'AT | 8x4'TR |
| 22 | 45'UT2 | 2x15'UT1 | 2x10'AT | 60'UT2 | 8x1.5'AN | 3x12'UT1 | 2x7'AT | 4x2'TR |
| 23 | 65'UT2 | 3x15'UT1 | 3x8'AT | 75'UT2 | 10x45sAN | 3x15'UT1 | 3x7'AT | 4x3'TR |
| 24 | 76'UT2 | 4x15'UT1 | 4x8'AT | 90'UT2 | 2(6x1')AN | 4x15'UT1 | 2x10'AT | 4x4'TR |
| 25 | 50'UT2 | 2x12'UT1 | 6' AT | 40'UT2 | 2x1.5'AN | 20'UT2 | 2x2'TR | 3x45sAN |
| 26 | OFF | 1x15'UT1 | 5'AT | 1x3'TR | 20'UT2 | 2x2'TR | 3x45sAN | RACE |
cat level-5_8-session_26-weeks.csv
```csv
"Interactive 2,000m Programme",,,,,,,,
2000m 26 Week Training Programme,,,,,,,,
"Athlete Level 5, 8 Sessions a Week",,,,,,,,
Week,Session 1,Session 2,Session 3,Session 4,Session 5,Session 6,Session 7,Session 8
1,TEST,2x15'UT1,55'UT2,2x18'UT1,68'UT2,3x15'UT1,55'UT2,2x15'UT1
2,65'UT2,2x21'UT1,72'UT2,3x16'UT1,80'UT2,4x13'UT1,72'UT2,3x12'UT1
3,76'UT2,3x17'UT1,85'UT2,3x19'UT1,90'UT2,3x20'UT1,85'UT2,2x25'UT1
4,45'UT2,2x15'UT1,2x6'AT,3x12'UT1,2x7'AT,3x15'UT1,2x10'AT,3x12'UT2
5,65'UT2,2x24'UT1,4x5'AT,3x16'UT1,3x8'AT,3x18'UT1,2x12'AT,3x16'UT1
6,76'UT2,2x25'UT1,3x7'AT,3x19'UT1,3x10'AT,5x12'UT1,3x8'AT,3x18'UT1
7,45'UT2,2x15'UT1,2x8'AT,3x12'UT1,2x10'AT,3x15'UT1,2x9'AT,2x3'TR
8,65'UT2,3x14'UT1,2x9'AT,75'UT2,2x8' AT,4x14'UT1,2x7'AT,3x4'TR
9,76'UT2,3x17'UT1,3x7'AT,90'UT2,4x5'TR,4X15'UT1,3x8'AT,4x4'TR
10,45'UT2,2x15'UT1,2x8'AT,60'UT2,4x2'TR,3x12'UT1,2x9'AT,3x3'TR
11,65'UT2,3x15'UT1,2x10'AT,75'UT2,4x3'TR,3x15'UT1,2x10'AT,4x3'TR
12,75'UT2,4x15'UT1,3x8'AT,90'UT2,4x4'TR,4x12'UT1,3x10'AT,5x4'TR
13,TEST,2X15'UT1,2x8'AT,60'UT2,3x1'AN,3x15'UT1,2x8'AT,4x2'TR
14,65'UT2,3X15'UT1,2x10'AT,75'UT2,4x1.5'AN,4x12'UT1,3x7'AT,6x2'TR
15,75'UT2,5x12'UT1,3x10'AT,90'UT2,6x1'AN,5x12'UT1,3x10'AT,6x4'TR
16,45'UT2,2X15'UT1,2x9'AT,60'UT2,8x45sAN,4x14'UT1,2x10'AT,5x2'TR
17,65'UT2,3X15'UT1,3x10'AT,75'UT2,6x1.5'AN,3x12'UT1,3x8'AT,6x3'TR
18,75'UT2,4x15'UT1,4x8'AT,90'UT2,8x1'AN,2x15'UT1,4x9'AT,7x4'TR
19,45'UT2,2X15'UT1,2x10'AT,60'UT2,4x1.5'AN,3x12'UT1,3x8'AT,6x2'TR
20,65'UT2,3X15'UT1,3x12'AT,75'UT2,6x1'AN,3x15'UT1,3x10'AT,7x3'TR
21,75'UT2,5x12'UT1,5x8'AT,90'UT2,8x45sAN,5x12'UT1,4x10'AT,8X4'TR
22,45'UT2,2x15'UT1,2x10'AT,60'UT2,8x1.5'AN,3x12'UT1,2x7'AT,4x2'TR
23,65'UT2,3x15'UT1,3x8'AT,75'UT2,10x45sAN,3x15'UT1,3x7'AT,4x3'TR
24,76'UT2,4x15'UT1,4x8'AT,90'UT2,2(6x1')AN,4x15'UT1,2x10'AT,4x4'TR
25,50'UT2,2x12'UT1,6' AT,40'UT2,2x1.5'AN,2x15'UT1,4'TR,2x12'UT1
26,OFF,1x15'UT1,5'AT,1x3'TR,20'UT2,2x2'TR,3x45sAN,RACE
```
cat concept2_prediction.py
```python
def calculate_wattage(resting_heart_rate, maximum_heart_rate, heart_rate, base_power_output):
def get_zone_percentile(heart_rate, resting_heart_rate, maximum_heart_rate):
HRR = maximum_heart_rate - resting_heart_rate
zone = ((heart_rate - resting_heart_rate) / HRR) * 100
return round(zone, 2)
def get_hrr_range(zone):
if zone <= 65:
return (0, 65)
elif zone <= 70:
print("UT2 Zone")
return (65, 70)
elif zone <= 80:
print("UT1 Zone")
return (70, 80)
elif zone <= 85:
print("AT Zone")
return (80, 85)
elif zone <= 95:
print("TR Zone")
return (85, 95)
else:
print("AN Zone")
return (95, 100)
def get_power_range(zone):
if zone <= 65:
return (0, 45)
elif zone <= 70:
print("UT2 Zone")
return (45, 60)
elif zone <= 80:
print("UT1 Zone")
return (60, 70)
elif zone <= 85:
print("AT Zone")
return (70, 80)
elif zone <= 95:
print("TR Zone")
return (80, 105)
else:
print("AN Zone")
return (105, 115)
zone_percentage = get_zone_percentile(heart_rate, resting_heart_rate, maximum_heart_rate)
print("Zone: " + str(zone_percentage))
min_power, max_power = get_power_range(zone_percentage)
print("Min power: " + str(min_power))
print("Max power: " + str(max_power))
min_hrr, max_hrr = get_hrr_range(zone_percentage)
print("Min hrr: " + str(min_hrr))
print("Max hrr: " + str(max_hrr))
power_range = max_power - min_power
print("Power range: " + str(power_range))
hrr_range = max_hrr - min_hrr
print("HRR range: " + str(hrr_range))
print("Zone minus min hrr: " + str(zone_percentage - min_hrr))
# Calculate percentage of hrr range
hrr_percentage = ((zone_percentage - min_hrr) / hrr_range)
print("hrr %: " + str(hrr_percentage))
power_percentage = (min_power + hrr_percentage * power_range) / 100
print("Power percentage: " + str(power_percentage))
# Add base power output
# predicted_power = power_percentage + base_power_output
predicted_power = base_power_output / power_percentage
return round(predicted_power, 0)
# Example usage
resting_heart_rate = 58
maximum_heart_rate = 165
heart_rate = 132
base_power_output = 110
watts = calculate_wattage(resting_heart_rate, maximum_heart_rate, heart_rate, base_power_output)
print(f"Predicted power: {watts} watts")
```