#3 gap, grade

This commit is contained in:
Gyuri Horák 2023-10-27 16:02:37 +02:00
parent 35524dd9ba
commit cb70962403
Signed by: dyuri
GPG Key ID: 4993F07B3EAE8D38
7 changed files with 138 additions and 12 deletions

View File

@ -8,7 +8,7 @@ Trail running focused Garmin watch DataField (for myself)
- current
- max
- histogram
- pace
- pace (or speed)
- current
- average
- distance
@ -16,7 +16,8 @@ Trail running focused Garmin watch DataField (for myself)
- current
- gain
- loss
- cadence
- grade
- cadence (instead of grade) TODO
- distance to destination gauge
# Settings

View File

@ -1,8 +1,9 @@
<properties>
<property id="appVersion" type="string">1.1.2</property>
<property id="appVersion" type="string">1.2.0</property>
<property id="themeColor" type="string">0</property>
<property id="themeColor2" type="string">0088FF</property>
<property id="themeColor3" type="string">FFFF00</property>
<property id="hrDisplay" type="number">0</property>
<property id="speedNotPace" type="boolean">false</property>
<property id="tlFieldData" type="number">2</property>
</properties>

View File

@ -19,6 +19,14 @@
<listEntry value="2">@Strings.HrZone</listEntry>
</settingConfig>
</setting>
<setting propertyKey="@Properties.tlFieldData" title="@Strings.TLFieldData">
<settingConfig type="list">
<listEntry value="0">@Strings.TLFCadence</listEntry>
<listEntry value="1">@Strings.TLFGrade</listEntry>
<listEntry value="2">@Strings.TLFGAP</listEntry>
<!-- listEntry value="3">@Strings.TLFVSpeed</listEntry -->
</settingConfig>
</setting>
<setting propertyKey="@Properties.speedNotPace" title="@Strings.SpeedNotPace">
<settingConfig type="boolean" />
</setting>

View File

@ -9,4 +9,9 @@
<string id="HrPercentage">Percentage</string>
<string id="HrZone">Zone</string>
<string id="SpeedNotPace">Show speed instead of pace</string>
<string id="TLFieldData">Top-left field data</string>
<string id="TLFCadence">Cadence</string>
<string id="TLFGrade">Grade</string>
<string id="TLFGAP">Grade Adjusted Pace</string>
<string id="TLFVSpeed">Vertical Speed</string>
</strings>

31
source/GAP.mc Normal file
View File

@ -0,0 +1,31 @@
import Toybox.Lang;
import Toybox.System;
// https://medium.com/strava-engineering/an-improved-gap-model-8b07ae8886c3
const GRADE_ADJUSTMENT as Array<Array<Numeric>> = [
[-0.3, 1.5], [-0.28, 1.4], [-0.24, 1.2], [-0.2, 1.1], [-0.18, 1], [-0.16, 0.9], [-0.1, 0.85], [-0.04, 0.9],
[0, 1], [0.04, 1.1], [0.06, 1.2], [0.08, 1.3], [0.1, 1.5], [0.12, 1.6], [0.14, 1.8], [0.17, 2], [0.2, 2.3],
[0.24, 2.6], [0.28, 3], [0.32, 3.4]
];
function adjustPaceForGrade(pace, grade) {
if (grade <= GRADE_ADJUSTMENT[0][0]) {
return pace / GRADE_ADJUSTMENT[0][1];
}
var size = GRADE_ADJUSTMENT.size();
for (var i = 1; i < size; i++) {
if (grade <= GRADE_ADJUSTMENT[i][0]) {
var prev = GRADE_ADJUSTMENT[i - 1];
var next = GRADE_ADJUSTMENT[i];
var slope = (next[1] - prev[1]) / (next[0] - prev[0]);
var adjustment = prev[1] + slope * (grade - prev[0]);
return pace / adjustment;
}
}
return pace / GRADE_ADJUSTMENT[size - 1][1];
}

View File

@ -9,6 +9,10 @@ import Toybox.Application;
const HR_TYPE_PERCENT = 1;
const HR_TYPE_ZONE = 2;
const TLF_CADENCE = 0;
const TLF_GRADE = 1;
const TLF_GAP = 2;
function displayHr(hr as Number, type as Number, zones as Array<Number>) as String {
if (hr == 0) {
return "-";
@ -39,11 +43,14 @@ class RepaFieldView extends WatchUi.DataField {
hidden var themeColor3 as Number;
hidden var hrDisplayType as Number;
hidden var speedNotPace as Boolean;
hidden var tlFieldData as Number;
hidden var hrZones as Array<Number>;
hidden var hrHist as Array<Number>;
hidden var hrZoneColors as Array<Number>;
hidden var cadenceZones as Array<Number>;
hidden var cadenceZoneColors as Array<Number>;
hidden var gradeZones as Array<Number>;
hidden var gradeZoneColors as Array<Number>;
hidden var isDistanceMetric as Boolean;
hidden var isElevationMetric as Boolean;
hidden var isPaceMetric as Boolean;
@ -86,6 +93,7 @@ class RepaFieldView extends WatchUi.DataField {
hidden var egain as Number;
hidden var edrop as Number;
hidden var cadence as Number;
hidden var grade as RollingAverage;
function initialize() {
DataField.initialize();
@ -95,6 +103,7 @@ class RepaFieldView extends WatchUi.DataField {
themeColor3 = Application.Properties.getValue("themeColor3").toNumberWithBase(16);
hrDisplayType = Application.Properties.getValue("hrDisplay").toNumber();
speedNotPace = Application.Properties.getValue("speedNotPace");
tlFieldData = Application.Properties.getValue("tlFieldData").toNumber();
hrValue = 0;
ahrValue = 0;
@ -105,6 +114,8 @@ class RepaFieldView extends WatchUi.DataField {
hrZoneColors = [Graphics.COLOR_DK_GRAY, Graphics.COLOR_LT_GRAY, Graphics.COLOR_BLUE, Graphics.COLOR_GREEN, Graphics.COLOR_YELLOW, Graphics.COLOR_RED, Graphics.COLOR_DK_RED];
cadenceZones = [153, 163, 173, 183];
cadenceZoneColors = [Graphics.COLOR_RED, Graphics.COLOR_YELLOW, Graphics.COLOR_GREEN, Graphics.COLOR_BLUE, Graphics.COLOR_PURPLE];
gradeZones = [-10, -1, 1, 3, 6, 10, 15];
gradeZoneColors = [Graphics.COLOR_PINK, Graphics.COLOR_PURPLE, Graphics.COLOR_LT_GRAY, Graphics.COLOR_BLUE, Graphics.COLOR_GREEN, Graphics.COLOR_YELLOW, Graphics.COLOR_RED, Graphics.COLOR_DK_RED];
toDestination = 0.0f;
distance = 0.0f;
timer = 0;
@ -118,6 +129,7 @@ class RepaFieldView extends WatchUi.DataField {
egain = 0;
edrop = 0;
cadence = 0;
grade = new RollingAverage(10);
var settings = System.getDeviceSettings();
isDistanceMetric = settings.distanceUnits == System.UNIT_METRIC;
@ -218,18 +230,31 @@ class RepaFieldView extends WatchUi.DataField {
}
function compute(info as Activity.Info) as Void {
if(info.currentHeartRate != null) {
// update rolling values before updating normal fields
// only calculate them when some time has passed
if (info.timerTime != null && info.timerTime > 0) {
// grade
if (info.altitude != null && info.elapsedDistance != null) {
var altChange = info.altitude - altitude;
var distChange = info.elapsedDistance - distance;
if (distChange > 0) {
grade.insert(altChange / distChange);
}
}
}
// update normal fields
if (info.currentHeartRate != null) {
hrValue = info.currentHeartRate as Number;
tickHr(hrValue);
} else {
hrValue = 0;
}
if(info.averageHeartRate != null) {
if (info.averageHeartRate != null) {
ahrValue = info.averageHeartRate as Number;
} else {
ahrValue = 0;
}
if(info.maxHeartRate != null) {
if (info.maxHeartRate != null) {
mhrValue = info.maxHeartRate as Number;
} else {
mhrValue = 0;
@ -429,14 +454,36 @@ class RepaFieldView extends WatchUi.DataField {
fElevationLoss.setText(edrop.format("%d"));
}
// cadence
// TLF
if (fCadence != null) {
var cadenceColor = darken(calculateZoneColor(cadence, cadenceZones, cadenceZoneColors), 1.5);
fCadence.setColor(cadenceColor);
if (cadence != 0) {
fCadence.setText(cadence.format("%d"));
if (tlFieldData == TLF_GRADE) {
var currentGrade = grade.get() * 100;
var gradeColor = calculateZoneColor(currentGrade, gradeZones, gradeZoneColors);
fCadence.setColor(gradeColor);
if (currentGrade >= 10 || currentGrade <= -10) {
fCadence.setText(currentGrade.format("%.0f"));
} else {
fCadence.setText(currentGrade.format("%.1f"));
}
} else if (tlFieldData == TLF_GAP) {
fCadence.setColor(themeColor2);
if (pace != 0) {
var gap = adjustPaceForGrade(pace, grade.get());
// TODO color
var gapmin = gap.toNumber();
var gapsec = (gap - gapmin) * 60;
fCadence.setText(gapmin.format("%d") + ":" + gapsec.format("%02d"));
} else {
fCadence.setText("-");
}
} else {
fCadence.setText("-");
var cadenceColor = calculateZoneColor(cadence, cadenceZones, cadenceZoneColors);
fCadence.setColor(cadenceColor);
if (cadence != 0) {
fCadence.setText(cadence.format("%d"));
} else {
fCadence.setText("-");
}
}
}

33
source/RollingAverage.mc Normal file
View File

@ -0,0 +1,33 @@
import Toybox.Lang;
import Toybox.Time;
import Toybox.Math;
class RollingAverage {
hidden var _size as Number;
hidden var _values as Array<Numeric>;
hidden var _index as Number;
function initialize(size as Number) {
_size = size;
_values = new[size];
_index = 0;
}
function insert(value as Numeric) {
_values[_index] = value;
_index = (_index + 1) % _size;
}
function get() as Numeric {
var sum = 0.0;
var count = 0;
for (var i = 0; i < _size; i++) {
var value = _values[i];
if (value != null) {
sum += value;
count++;
}
}
return count == 0 ? 0.0 : sum / count;
}
}