public SessionSummaryDto CalculateWorkoutSummary(SessionDto session, List<DetectedInterval> detectedIntervals, int unit) { var sessionSummaryDtoList = new List<SessionSummaryDto>(); for (var w = 0; w < detectedIntervals.Count; w++) { var sessionDataSubsetDto = new SessionDataSubsetDto() { MinimumSecond = (double)detectedIntervals[w].StartTime, MaximumSecond = (double)detectedIntervals[w].FinishTime, SessionId = session.Id, Unit = unit }; var sessionSummaryDto = GetSessionDataSubset(sessionDataSubsetDto); sessionSummaryDtoList.Add(sessionSummaryDto); } var totalCount = sessionSummaryDtoList.Count(); // calculate speed var averageSpeed = Math.Round(sessionSummaryDtoList.Sum(x => x.AverageSpeed) / totalCount, 2, MidpointRounding.AwayFromZero); var maximumSpeed = sessionSummaryDtoList.MaxBy(s => s.MaximumSpeed).MaximumSpeed; // calculate distance var totalDistance = Math.Round(sessionSummaryDtoList.Sum(x => x.TotalDistance), 2, MidpointRounding.AwayFromZero); // calculate altitiude var averageAltitude = Math.Round(sessionSummaryDtoList.Sum(x => x.AverageAltitude) / totalCount, 2, MidpointRounding.AwayFromZero); var maximumAltitude = sessionSummaryDtoList.MaxBy(s => s.MaximumAltitude).MaximumAltitude; // calculate heart rate var averageHeartRate = Math.Round(sessionSummaryDtoList.Sum(x => x.AverageHeartRate / totalCount), 2, MidpointRounding.AwayFromZero); var minimumHeartRate = sessionSummaryDtoList.MinBy(s => s.MinimumHeartRate).MinimumHeartRate; var maximumHeartRate = sessionSummaryDtoList.MinBy(s => s.MaximumHeartRate).MaximumHeartRate; // calculate power var averagePower = Math.Round(sessionSummaryDtoList.Sum(x => x.AveragePower) / totalCount, 2, MidpointRounding.AwayFromZero); var maximumPower = sessionSummaryDtoList.MaxBy(s => s.MaximumPower).MaximumPower; // calculate cadence var averageCadence = Math.Round(sessionSummaryDtoList.Sum(x => x.AverageCadence) / totalCount, 2, MidpointRounding.AwayFromZero); var maximumCadence = sessionSummaryDtoList.MaxBy(s => s.MaximumCadence).MaximumCadence; var workoutSummary = new SessionSummaryDto() { AverageAltitude = averageAltitude, MaximumAltitude = maximumAltitude, AverageHeartRate = averageHeartRate, MinimumHeartRate = minimumHeartRate, MaximumHeartRate = maximumHeartRate, AveragePower = averagePower, MaximumPower = maximumPower, AverageCadence = averageCadence, MaximumCadence = maximumCadence, AverageSpeed = averageSpeed, MaximumSpeed = maximumSpeed, TotalDistance = totalDistance, Date = session.Date, SessionId = session.Id }; return workoutSummary; }
public SessionSummaryDto GetSessionDataSubset(SessionDataSubsetDto sessionDataSubsetDto) { var minimumSeconds = sessionDataSubsetDto.MinimumSecond; var maximumSeconds = sessionDataSubsetDto.MaximumSecond; var totalTimeInHours = (maximumSeconds - minimumSeconds) / 3600; var requestedUnitIsMetric = sessionDataSubsetDto.Unit == 0; var session = _context.Sessions.Single(x => x.Id == sessionDataSubsetDto.SessionId); var sModeIsMetric = session.SMode.ToString("D9").IsMetric(); // pad int to 9 decimals if zero var sessionData = _context.SessionData.Where(x => x.SessionId == sessionDataSubsetDto.SessionId).ToList().OrderBy(x => x.Row) .Select(sd => new SessionDataDto() { Id = sd.Id, HeartRate = sd.HeartRate, Speed = sd.Speed, Cadence = sd.Cadence, Altitude = sd.Altitude, Power = sd.Power, SessionId = sd.SessionId, Date = DateFormat.CalculateSessionDataRowDate(session.Date, session.Interval, sd.Row) }).ToList(); var filteredSessionData = sessionData.FilterListDates(session.Date, minimumSeconds, maximumSeconds); // filter sessions by min and max seconds var totalCount = filteredSessionData.Count(); double maximumSpeed; double averageSpeed; double totalSpeed; double totalDistance; double averageAltitude; double maximumAltitude; double totalAltitude; if (requestedUnitIsMetric) // return metric values { if (sModeIsMetric) { // calculate speed - divided speed by 10 as speed is *10 in file totalSpeed = filteredSessionData.Sum(s => s.Speed); averageSpeed = Math.Round((totalSpeed / 10) / totalCount, 2, MidpointRounding.AwayFromZero); maximumSpeed = Math.Round(filteredSessionData.MaxBy(s => s.Speed).Speed / 10, 2, MidpointRounding.AwayFromZero); // calculate distance totalDistance = Math.Round(averageSpeed * totalTimeInHours, 2, MidpointRounding.AwayFromZero); // calculate altitiude totalAltitude = filteredSessionData.Sum(s => s.Altitude / 10); averageAltitude = Math.Round(totalAltitude / totalCount, 2, MidpointRounding.AwayFromZero); maximumAltitude = Math.Round(filteredSessionData.MaxBy(s => s.Altitude).Altitude / 10, 2, MidpointRounding.AwayFromZero); } else { // calculate speed - divided speed by 10 as speed is *10 in file - converted kilometres to miles totalSpeed = filteredSessionData.Sum(s => s.Speed); averageSpeed = ((totalSpeed / 10) / totalCount).ConvertToKilometres(); maximumSpeed = (filteredSessionData.MaxBy(s => s.Speed).Speed / 10).ConvertToKilometres(); // calculate distance totalDistance = ((totalSpeed / 10) / totalCount * totalTimeInHours).ConvertToKilometres(); // calculate altitiude - convert metres to feet totalAltitude = filteredSessionData.Sum(s => s.Altitude / 10); averageAltitude = (totalAltitude / totalCount).ConvertToMetres(); maximumAltitude = (filteredSessionData.MaxBy(s => s.Altitude).Altitude / 10).ConvertToMetres(); } } else // return imperial values { if (sModeIsMetric) { // calculate speed - divided speed by 10 as speed is *10 in file - converted kilometres to miles totalSpeed = filteredSessionData.Sum(s => s.Speed); averageSpeed = ((totalSpeed / 10) / totalCount).ConvertToMiles(); maximumSpeed = (filteredSessionData.MaxBy(s => s.Speed).Speed / 10).ConvertToMiles(); // calculate distance totalDistance = ((totalSpeed / 10) / totalCount * totalTimeInHours).ConvertToMiles(); // calculate altitiude - convert metres to feet totalAltitude = filteredSessionData.Sum(s => s.Altitude / 10); averageAltitude = (totalAltitude / totalCount).ConvertToFeet(); maximumAltitude = (filteredSessionData.MaxBy(s => s.Altitude).Altitude / 10).ConvertToFeet(); } else { // calculate speed - divided speed by 10 as speed is *10 in file totalSpeed = filteredSessionData.Sum(s => s.Speed); averageSpeed = Math.Round((totalSpeed / 10) / totalCount, 2, MidpointRounding.AwayFromZero); maximumSpeed = Math.Round( filteredSessionData.MaxBy(s => s.Speed).Speed / 10, 2, MidpointRounding.AwayFromZero); // calculate distance totalDistance = Math.Round(averageSpeed * totalTimeInHours, 2, MidpointRounding.AwayFromZero); // calculate altitiude totalAltitude = filteredSessionData.Sum(s => s.Altitude / 10); averageAltitude = Math.Round(totalAltitude / totalCount, 2, MidpointRounding.AwayFromZero); maximumAltitude = Math.Round(filteredSessionData.MaxBy(s => s.Altitude).Altitude / 10, 2, MidpointRounding.AwayFromZero); } } // calculate heart rate var totalHeartRate = filteredSessionData.Sum(s => s.HeartRate); var averageHeartRate = Math.Round(totalHeartRate / totalCount, 2, MidpointRounding.AwayFromZero); var minimumHeartRate = filteredSessionData.MinBy(s => s.HeartRate).HeartRate; var maximumHeartRate = filteredSessionData.MaxBy(s => s.HeartRate).HeartRate; // calculate power var totalPower = filteredSessionData.Sum(s => s.Power); var averagePower = Math.Round(totalPower / totalCount, 2, MidpointRounding.AwayFromZero); var maximumPower = Math.Round(filteredSessionData.MaxBy(s => s.Power).Power, 2, MidpointRounding.AwayFromZero); // calculate cadence var totalCadence = filteredSessionData.Sum(s => s.Cadence); var averageCadence = Math.Round(totalCadence / totalCount, 2, MidpointRounding.AwayFromZero); var maximumCadence = Math.Round(filteredSessionData.MaxBy(s => s.Cadence).Cadence, 2, MidpointRounding.AwayFromZero); // calculate normalized power var filteredSessionDto = new SessionDto() { Interval = session.Interval, SessionData = filteredSessionData }; var normalizedPower = filteredSessionDto.CalculateNormalizedPower(); // calculate intensity factor var functionalThresholdPower = _context.Athletes.Single(a => a.Id == session.AthleteId).FunctionalThresholdPower; var intensityFactor = normalizedPower.CalculateIntensityFactor(functionalThresholdPower); // calculate training stress score var sessionTimeInSeconds = filteredSessionData.Count * filteredSessionDto.Interval; //session.Length.TimeOfDay.TotalSeconds; double trainingStressScore = 0; string trainingStressScoreStatus = ""; if (normalizedPower != 0) { trainingStressScore = Metrics.CalculateTrainingStressScore(sessionTimeInSeconds, normalizedPower, intensityFactor, functionalThresholdPower); trainingStressScoreStatus = Metrics.TrainingStressScoreStatus(trainingStressScore); } var sessionSummaryDto = new SessionSummaryDto() { AverageAltitude = averageAltitude, MaximumAltitude = maximumAltitude, AverageHeartRate = averageHeartRate, MinimumHeartRate = minimumHeartRate, MaximumHeartRate = maximumHeartRate, AveragePower = averagePower, MaximumPower = maximumPower, AverageCadence = averageCadence, MaximumCadence = maximumCadence, AverageSpeed = averageSpeed, MaximumSpeed = maximumSpeed, TotalDistance = totalDistance, NormalizedPower = normalizedPower, IntensityFactor = intensityFactor, TrainingStressScore = trainingStressScore, TrainingStressScoreStatus = trainingStressScoreStatus, Date = session.Date, SessionId = session.Id }; return sessionSummaryDto; }
public List<DetectedInterval> DetectIntervals(SessionDto session) { var sessionData = session.SessionData; var interval = session.Interval; var potentialIntervalStart = new SessionDataDto(); var detectedIntervalEnd = new SessionDataDto(); var detectedIntervals = new List<DetectedInterval>(); for (var x = 0; x < sessionData.Count; x++) { bool potentialIntervalDetected = false; bool intervalDetected = false; for (var p = x; p < sessionData.Count; p++) { // get average of proceeding 14 seconds of powers - time taken for rider to reach maximum power var currentPowers = new List<double>(); var proceedingPowers = new List<double>(); for (var i = 0; i < 14; i++) { if (p + (i + 1) < sessionData.Count) { if (sessionData[p + i].Power == 0) // rider must be applying power for next 14 seconds { break; } currentPowers.Add(sessionData[p + i].Power); // get power for the next 14 seconds proceedingPowers.Add(sessionData[(p + 1) + i].Power); // get power for the next 14 seconds starting at current power +1 } } if (currentPowers.Count == 0) // no powers added to the last - last detected power was 0 { break; } var currentPowersAverage = currentPowers.Average(); var proceedingPowersAverage = proceedingPowers.Average(); // check for potential interval if (currentPowersAverage < proceedingPowersAverage) { if (!potentialIntervalDetected) { potentialIntervalStart = sessionData[p]; potentialIntervalDetected = true; } } else // possible that cyclist built up speed and reached interval speed to maintain { var potentialIntervalPowerToMaintain = sessionData[p].Power; var percentage = ((potentialIntervalPowerToMaintain * 40) / 100); var minimumPowerRange = potentialIntervalPowerToMaintain - percentage; var maximumPowerRange = potentialIntervalPowerToMaintain + percentage; var minimumIntervalDuration = 10; // interval power must be maintained for atleast 10 seconds var timer = 0; var counter = 1; for (var q = p; q < sessionData.Count; q++) { if (sessionData[q].Power > minimumPowerRange && sessionData[q].Power < maximumPowerRange) { timer += interval * counter; } else { if (timer > minimumIntervalDuration) { intervalDetected = true; detectedIntervalEnd = sessionData[q]; var startTime = potentialIntervalStart.Row * interval; var finishTime = detectedIntervalEnd.Row * interval; var intervalData = GetSessionDataSubset(new SessionDataSubsetDto() // get interval summary { MinimumSecond = startTime, MaximumSecond = finishTime, Unit = 0, SessionId = session.Id }); detectedIntervals.Add(new DetectedInterval(startTime, finishTime, intervalData.AveragePower, true)); } break; } } } if (intervalDetected) { x = detectedIntervalEnd.Row * interval; // start detecting new interval at the end of the detected interval potentialIntervalStart = sessionData[detectedIntervalEnd.Row]; break; } } } // detect workout and rest periods var totalCount = sessionData.Count(); var totalPower = sessionData.Sum(s => s.Power); var averagePower = Math.Round(totalPower / totalCount, 2, MidpointRounding.AwayFromZero); var minimumIntervalPower = ((averagePower * 80) / 100); // workout periods must be greater than 30% of average session power for (var f = 0; f < detectedIntervals.Count; f ++) { detectedIntervals[f].IsRest = detectedIntervals[f].AveragePower < minimumIntervalPower; } return detectedIntervals; }
public SessionDto GetSingle(int sessionId) { var session = _context.Sessions.Single(x => x.Id == sessionId); var sessionData = _context.SessionData.Where(x => x.SessionId == sessionId).ToList().OrderBy(x =>x.Row) .Select(sd => new SessionDataDto() { Id = sd.Id, HeartRate = sd.HeartRate, Speed = sd.Speed, Cadence = sd.Cadence, Altitude = sd.Altitude, Power = sd.Power, SessionId = sd.SessionId, Row = sd.Row, Date = DateFormat.CalculateSessionDataRowDate(session.Date, session.Interval, sd.Row) }).ToList(); var sessionDto = new SessionDto { Id = session.Id, Title = session.Title, SoftwareVersion = session.SoftwareVersion, MonitorVersion = session.MonitorVersion, SMode = session.SMode, StartTime = session.StartTime, Length = session.Length, Date = session.Date, Interval = session.Interval, Upper1 = session.Upper1, Lower1 = session.Lower1, AthleteId = session.AthleteId, SessionData = sessionData }; return sessionDto; }