diff --git a/source/GarminApp.mc b/source/GarminApp.mc index 8523f29..f8ea2d8 100644 --- a/source/GarminApp.mc +++ b/source/GarminApp.mc @@ -7,7 +7,8 @@ import Toybox.System; class GarminApp extends Application.AppBase { - const MAX_BARS = 60; + const MAX_BARS = 280; + //const MAX_BARS_DISPLAY = 0; const BASELINE_AVG_CADENCE = 160; const MAX_CADENCE = 190; const MIN_CQ_SAMPLES = 30; @@ -16,6 +17,20 @@ class GarminApp extends Application.AppBase { var globalTimer; var isRecording as Boolean = false; + enum { //each chart corresponds to a difference bar duration average (in seconds) + FifteenminChart = 3, + ThirtyminChart = 6, + OneHourChart = 13, + TwoHourChart = 26 + } + + const CHART_ENUM_NAMES = { + FifteenminChart => "15 Minutes", + ThirtyminChart => "30 Minutes", + OneHourChart => "1 Hour", + TwoHourChart => "2 Hours" + }; + enum { Beginner = 1.06, Intermediate = 1.04, @@ -28,31 +43,31 @@ class GarminApp extends Application.AppBase { Other } + //default value (can change in settings) + private var _userHeight = 170;//>>cm + private var _userSpeed = 3.8;//>>m/s + private var _experienceLvl = Beginner; + private var _userGender = Male; + private var _chartDuration = ThirtyminChart; + private var _idealMinCadence = 80; private var _idealMaxCadence = 100; + + private var _cadenceHistory as Array = new [MAX_BARS]; // Store session's cadence private var _cadenceIndex = 0; private var _cadenceCount = 0; - private var _cadenceHistory as Array = new [MAX_BARS]; + + private var _cadenceBarAvg as Array = new [_chartDuration]; // Store data points for display + private var _cadenceAvgIndex = 0; + private var _cadenceAvgCount = 0; + private var _finalCQ = null; private var _missingCadenceCount = 0; private var _finalCQConfidence = null; private var _finalCQTrend = null; - - private var _userHeight = null;//>>cm - private var _userSpeed = null;//>>m/s - private var _experienceLvl = null; - private var _userGender = null; - private var _cqHistory as Array = []; - function dummyValueTesting() as Void { - _userHeight = 170; - _userSpeed = 3.8; - _experienceLvl = Beginner; - _userGender = Female; - } - function initialize() { AppBase.initialize(); System.println("[INFO] App initialized"); @@ -65,11 +80,7 @@ class GarminApp extends Application.AppBase { Logger.logMemoryStats("Startup"); globalTimer = new Timer.Timer(); - globalTimer.start(method(:updateCadence), 1000, true); - dummyValueTesting(); - /* - remember to remove after testing - */ + globalTimer.start(method(:updateCadenceBarAvg),1000,true); idealCadenceCalculator(); } @@ -139,26 +150,42 @@ class GarminApp extends Application.AppBase { return isRecording; } - function updateCadence() as Void { - if (!isRecording) { + function updateCadenceBarAvg() as Void { + if (!isRecording) { return; // ignore samples when not actively monitoring - } - - var info = Activity.getActivityInfo(); + } + + var info = Activity.getActivityInfo(); + + //var zoneState = null; + if (info != null && info.currentCadence != null) { + var newCadence = info.currentCadence; + _cadenceBarAvg[_cadenceAvgIndex] = newCadence.toFloat(); + // Add to circular buffer + _cadenceAvgIndex = (_cadenceAvgIndex + 1) % _chartDuration; + if (_cadenceAvgCount < _chartDuration) { + _cadenceAvgCount++; + } + else //calculate avg + { + var barAvg = 0.0; + for(var i = 0; i < _chartDuration; i++){ + barAvg += _cadenceBarAvg[i]; + } + updateCadenceHistory(barAvg / _chartDuration); + _cadenceAvgCount = 0; + } + } - // ----- Cadence sample handling ----- - if (info != null && info.currentCadence != null) { - var newCadence = info.currentCadence; + } - // Store cadence sample - _cadenceHistory[_cadenceIndex] = newCadence.toFloat(); + function updateCadenceHistory(newCadence as Float) as Void { + _cadenceHistory[_cadenceIndex] = newCadence; + // Add to circular buffer _cadenceIndex = (_cadenceIndex + 1) % MAX_BARS; - - if (_cadenceCount < MAX_BARS) { - _cadenceCount++; - } - - if (DEBUG_MODE) { + if (_cadenceCount < MAX_BARS) { _cadenceCount++; } + + if (DEBUG_MODE) { System.println("[CADENCE] " + newCadence); } } else { @@ -193,10 +220,10 @@ class GarminApp extends Application.AppBase { if (_cadenceIndex % 60 == 0 && _cadenceIndex > 0) { Logger.logMemoryStats("Runtime"); } + +} -} - // Cadence Quality function computeTimeInZoneScore() as Number { @@ -410,6 +437,14 @@ function writeDiagnosticLog() as Void { function getMaxCadence() as Number { return _idealMaxCadence; } + + function setMinCadence(value as Number) as Void { + _idealMinCadence = value; + } + + function setMaxCadence(value as Number) as Void { + _idealMaxCadence = value; + } function getCadenceHistory() as Array { return _cadenceHistory; @@ -423,14 +458,14 @@ function writeDiagnosticLog() as Void { return _cadenceCount; } - function setMinCadence(value as Number) as Void { - _idealMinCadence = value; + function getChartDuration() as String{ + return CHART_ENUM_NAMES[_chartDuration]; } - function setMaxCadence(value as Number) as Void { - _idealMaxCadence = value; + function setChartDuration(value as String) as Void { + _chartDuration = value; } - + function getUserGender() as String { return _userGender; } @@ -459,7 +494,6 @@ function writeDiagnosticLog() as Void { return _experienceLvl; } - //double check ltr function setExperienceLvl(value as Number) as Void { _experienceLvl = value; } diff --git a/source/Views/AdvancedView.mc b/source/Views/AdvancedView.mc index 616e93b..cb5e03b 100644 --- a/source/Views/AdvancedView.mc +++ b/source/Views/AdvancedView.mc @@ -6,7 +6,7 @@ import Toybox.Timer; import Toybox.System; class AdvancedView extends WatchUi.View { - const MAX_BARS = 60; + const MAX_BARS = 280; const MAX_CADENCE_DISPLAY = 200; private var _simulationTimer; @@ -50,15 +50,15 @@ class AdvancedView extends WatchUi.View { var seconds = info.timerTime / 1000; var hours = seconds / 3600; var minutes = (seconds % 3600) / 60; - var secs = seconds % 60; - var timeStr = hours.format("%01d") + ":" + minutes.format("%02d") + "." + secs.format("%02d"); + //var secs = seconds % 60; + var timeStr = hours.format("%01d") + ":" + minutes.format("%02d"); //+ "." + secs.format("%02d"); dc.setColor(0xFFF813, Graphics.COLOR_TRANSPARENT); dc.drawText(width / 2, 3, Graphics.FONT_LARGE, timeStr, Graphics.TEXT_JUSTIFY_CENTER); } // Draw heart rate circle (left, dark red RGB: 211,19,2519 var hrX = width / 4; - var hrY = (height * 2) / 5; + var hrY = (height * 2) / 7; var circleRadius = 42; dc.setColor(0x9D0000, Graphics.COLOR_TRANSPARENT); @@ -85,30 +85,36 @@ class AdvancedView extends WatchUi.View { } //draw ideal cadence range + var idealMinCadence = app.getMinCadence(); var idealMaxCadence = app.getMaxCadence(); - var idealCadenceY = height * 0.45; + /* + var idealCadenceY = height * 0.37; if(idealMinCadence != null && idealMaxCadence != null){ var displayString = (idealMinCadence + " - " + idealMaxCadence).toString(); dc.setColor(0xAAAAAA, Graphics.COLOR_TRANSPARENT); dc.drawText(width / 2,idealCadenceY , Graphics.FONT_XTINY, displayString, Graphics.TEXT_JUSTIFY_CENTER); - } + }*/ - var cadenceY = height * 0.8; + var cadenceY = height * 0.37; + var chartDurationDisplay = null; + var chartDurationY = height * 0.85; if (info != null && info.currentCadence != null) { - // Draw "CADENCE" label (light gray RGB: 170,170,170 = 0xAAAAAA) - dc.setColor(0xAAAAAA, Graphics.COLOR_TRANSPARENT); - dc.drawText(width / 2, cadenceY, Graphics.FONT_XTINY, "CADENCE", Graphics.TEXT_JUSTIFY_CENTER); - // Draw cadence value in green (RGB: 0,255,0 = 0x00FF00) correctColor(info.currentCadence, idealMinCadence, idealMaxCadence, dc); - dc.drawText(width / 2, cadenceY + 20, Graphics.FONT_XTINY, info.currentCadence.toString() + " spm", Graphics.TEXT_JUSTIFY_CENTER); + dc.drawText(width / 2, cadenceY + 20, Graphics.FONT_XTINY, info.currentCadence.toString() + " spm", Graphics.TEXT_JUSTIFY_CENTER); } drawChart(dc); + + var string = app.getChartDuration(); + + dc.setColor(0x969696, Graphics.COLOR_TRANSPARENT); + dc.drawText(width / 2, chartDurationY, Graphics.FONT_XTINY, "Last " + string, Graphics.TEXT_JUSTIFY_CENTER); + } @@ -126,21 +132,43 @@ class AdvancedView extends WatchUi.View { //margins value var margin = width * 0.1; - var marginLeftRightMultiplier = 1.2; - var marginTopMultiplier = 0.5; - var marginBottomMultiplier = 2; + var marginLeftRightMultiplier = 1.38; + //var marginTopMultiplier = 0.5; + var marginBottomMultiplier = 1.6; //chart position var chartLeft = margin * marginLeftRightMultiplier; var chartRight = width - chartLeft; - var chartTop = height * 0.5 + margin * marginTopMultiplier; + var chartTop = height * 0.5; var chartBottom = height - margin*marginBottomMultiplier; var chartWidth = chartRight - chartLeft; var chartHeight = chartBottom - chartTop; - + var quarterChartHeight = chartHeight / 4; + + //bar zone + var barZoneLeft = chartLeft + 1; + var barZoneRight = chartRight - 1; + var barZoneWidth = barZoneRight - barZoneLeft; + var barZoneBottom = chartBottom - 1; + + //additional line indicator + var nLine = 3; + var lineLength = 6; + var line1x1 = chartLeft - lineLength; + var line1x2 = chartLeft; + var line2x1 = chartRight - 1; + var line2x2 = chartRight + lineLength; + var lineY = chartTop + quarterChartHeight; + + // Draw white border around chart (RGB: 255,255,255 = 0xFFFFFF) - dc.setColor(0xFFFFFF, Graphics.COLOR_TRANSPARENT); + dc.setColor(0x969696, Graphics.COLOR_TRANSPARENT); dc.drawRectangle(chartLeft, chartTop, chartWidth, chartHeight); + for(var i = 0; i < nLine; i++){ + dc.drawLine(line1x1,lineY,line1x2,lineY); + dc.drawLine(line2x1,lineY,line2x2,lineY); + lineY += quarterChartHeight; + } // Get data from app var app = getApp(); @@ -151,44 +179,48 @@ class AdvancedView extends WatchUi.View { var cadenceCount = app.getCadenceCount(); //check array ?null if(cadenceCount == 0) {return;} - - // Calculate bar width + var numBars = cadenceCount; - if(numBars == 0) { return; } - var barWidth = chartWidth / MAX_BARS; + var barWidth = (barZoneWidth / MAX_BARS).toNumber(); var startIndex = (cadenceIndex - numBars + MAX_BARS) % MAX_BARS; - + // Draw bars for (var i = 0; i < numBars; i++) { var index = (startIndex + i) % MAX_BARS; // Start from oldest data var cadence = cadenceHistory[index]; if(cadence == null) {cadence = 0;} - + //calculate bar height and position - var barHeight = (cadence / MAX_CADENCE_DISPLAY) * chartHeight; - var x = chartLeft + i * barWidth; - var y = chartBottom - barHeight; + var barHeight = ((cadence / MAX_CADENCE_DISPLAY) * chartHeight).toNumber(); + var x = barZoneLeft + i * barWidth; + var y = barZoneBottom - barHeight; - //seperation between each bar - var barOffset = 1; correctColor(cadence, idealMinCadence, idealMaxCadence, dc); - dc.fillRectangle(x, y, barWidth-barOffset, barHeight); + dc.fillRectangle(x, y, barWidth, barHeight); } - } -} -function correctColor(cadence as Number, idealMinCadence as Number, idealMaxCadence as Number, dc as Dc) as Void{ - if(cadence <= idealMinCadence) - { - dc.setColor(0x0000FF, Graphics.COLOR_TRANSPARENT);//blue - } - else if (cadence >= idealMaxCadence) - { - dc.setColor(0xFF0000, Graphics.COLOR_TRANSPARENT);//red + } - else - { - dc.setColor(0x00FF00, Graphics.COLOR_TRANSPARENT);//green + + function correctColor(cadence as Number, idealMinCadence as Number, idealMaxCadence as Number, dc as Dc) as Void{ + var yellowThreshold = idealMaxCadence + 20; + + if(cadence <= idealMinCadence) + { + dc.setColor(0x38b6ff, Graphics.COLOR_TRANSPARENT);//blue + } + else if (cadence >= idealMaxCadence && cadence < yellowThreshold) + { + dc.setColor(0xff751f, Graphics.COLOR_TRANSPARENT);//orange + } + else if (cadence >= yellowThreshold) + { + dc.setColor(0xFF0000, Graphics.COLOR_TRANSPARENT);//red + } + else + { + dc.setColor(0x00FF00, Graphics.COLOR_TRANSPARENT);//green + } } }