Exemplo n.º 1
0
        /// <summary>
        /// Gets a data track that represents the corresponding data value for each maximum effort.
        /// Example (single data point): 300 watts for 10 minutes:  What was the avg cadence over this max. effort?  HR, etc.
        /// This data series will return the track to answer this question for every point in the max effort.
        /// </summary>
        /// <param name="activity">Activity to analyze.</param>
        /// <param name="pointType">Cadence, power, hr, etc.</param>
        /// <param name="timeTrack">Coded data track describing when the max efforts occurred.
        /// Time track aligns with the max effort period (same as normal mean max time scale)
        /// Values represent max effort start time.
        /// Start time + Elapsed seconds = end time of max effort</param>
        /// <returns></returns>
        internal static INumericTimeDataSeries GetAvgTrack(IActivity activity, Common.TrackType pointType, INumericTimeDataSeries timeTrack)
        {
            if (activity == null || timeTrack == null || timeTrack.Count == 0)
            {
                return(null);
            }

            INumericTimeDataSeries track = new NumericTimeDataSeries();

            foreach (TimeValueEntry <float> point in timeTrack)
            {
                // Start time is relative to activity start time.
                //  Some data tracks don't start at same time as activity
                // point.Elapsed seconds is the period
                // point.Value is the start time relative to activity start
                float value = GetAvgValue(activity, pointType, activity.StartTime.AddSeconds(point.Value), point.ElapsedSeconds);
                if (!float.IsNaN(value))
                {
                    track.Add(timeTrack.EntryDateTime(point), value);
                }
                else if (point.ElapsedSeconds == 0)
                {
                    // This is to address the first point where the period = 0
                    value = GetAvgValue(activity, pointType, activity.StartTime, 1);
                    track.Add(timeTrack.StartTime, value);
                }
            }

            return(track);
        }
Exemplo n.º 2
0
        private void RefreshTree()
        {
            List <GearStats> rowData = new List <GearStats>();

            if (gearSelection.Count > 0)
            {
                string equipmentId             = Options.Instance.GetGearEquipmentId(activity);
                List <SprocketCombo> sprockets = Options.Instance.GetSprocketCombos(equipmentId);

                foreach (SprocketCombo combo in sprockets)
                {
                    INumericTimeDataSeries guessGears = new NumericTimeDataSeries(GearUtils.GuessGears(gearSelection, sprockets));
                    guessGears = GearUtils.RemovePausedTimesInTrack(guessGears, activity);
                    GearStats item = new GearStats(guessGears, combo.GearRatio, activity);
                    rowData.Add(item);
                }

                // Add 'Total' Line
                rowData.Add(new GearStats(rowData));
            }

            treeListStats.Columns.Clear();
            foreach (TreeList.Column column in GetTreeColumns())
            {
                treeListStats.Columns.Add(column);
            }

            treeListStats.RowDataRenderer = new MyRowDataRenderer(treeListStats);
            treeListStats.RowData         = rowData;
        }
Exemplo n.º 3
0
        /// <summary>
        /// Perform a smoothing operation using a moving average on the data series
        /// </summary>
        /// <param name="track">The data series to smooth</param>
        /// <param name="period">The range to smooth.  This is the total number of seconds to smooth across (slightly different than the ST method.)</param>
        /// <param name="min">An out parameter set to the minimum value of the smoothed data series</param>
        /// <param name="max">An out parameter set to the maximum value of the smoothed data series</param>
        /// <returns></returns>
        internal static INumericTimeDataSeries Smooth(INumericTimeDataSeries track, uint period, out float min, out float max)
        {
            min = float.NaN;
            max = float.NaN;
            INumericTimeDataSeries smooth = new NumericTimeDataSeries();

            if (track != null && track.Count > 0 && period > 1)
            {
                //min = float.NaN;
                //max = float.NaN;
                int   start = 0;
                int   index = 0;
                float value = 0;
                float delta;

                float per = period;

                // Iterate through track
                // For each point, create average starting with 'start' index and go forward averaging 'period' seconds.
                // Stop when last 'full' period can be created ([start].ElapsedSeconds + 'period' seconds >= TotalElapsedSeconds)
                while (track[start].ElapsedSeconds + period < track.TotalElapsedSeconds)
                {
                    while (track[index].ElapsedSeconds < track[start].ElapsedSeconds + period)
                    {
                        delta  = track[index + 1].ElapsedSeconds - track[index].ElapsedSeconds;
                        value += track[index].Value * delta;
                        index++;
                    }

                    // Finish value calculation
                    per   = track[index].ElapsedSeconds - track[start].ElapsedSeconds;
                    value = value / per;

                    // Add value to track
                    // TODO: I really don't need the smoothed track... really just need max.  Kill this for efficiency?
                    //smooth.Add(track.StartTime.AddSeconds(start), value);
                    smooth.Add(track.EntryDateTime(track[index]), value);

                    // Remove beginning point for next cycle
                    delta = track[start + 1].ElapsedSeconds - track[start].ElapsedSeconds;
                    value = (per * value - delta * track[start].Value);

                    // Next point
                    start++;
                }

                max = smooth.Max;
                min = smooth.Min;
            }
            else if (track != null && track.Count > 0 && period == 1)
            {
                min = track.Min;
                max = track.Max;
                return(track);
            }

            return(smooth);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Create a vam track from an activity's elevation track.
        /// vam is returned in Vm/h
        /// </summary>
        /// <param name="activity">Activity that needs an vam track</param>
        /// <returns>A vam track created from the elevation track</returns>
        public static INumericTimeDataSeries GetVAMTrack(IActivity activity)
        {
            INumericTimeDataSeries vamTrack       = new NumericTimeDataSeries();
            INumericTimeDataSeries elevationTrack = new NumericTimeDataSeries();

            elevationTrack = GetElevationTrack(activity);
            float vam              = 0;
            float lastElevation    = 0;
            float currentElevation = 0;

            DateTime startTime      = elevationTrack.StartTime;
            uint     currentSeconds = 0;
            uint     lastSeconds    = 0;

            if (vamTrack != null && elevationTrack != null)
            {
                /*for (int i = 0; i < elevationTrack.Count; i++)
                 * {
                 *  vam = ((elevationTrack[i].Value - startElevation) * 60f * 60f) / elevationTrack[i].ElapsedSeconds;
                 *  if (vam < 0 || float.IsNaN(vam) || float.IsNegativeInfinity(vam) || float.IsPositiveInfinity(vam))
                 *  {
                 *      vam = 0;
                 *  }
                 *  vamTrack.Add(startTime.AddSeconds(elevationTrack[i].ElapsedSeconds), vam);
                 * }*/

                for (int i = 0; i < elevationTrack.Count; i++)
                {
                    if (i == 0)
                    {
                        vamTrack.Add(startTime, vam);
                        lastElevation = elevationTrack[i].Value;
                        lastSeconds   = elevationTrack[i].ElapsedSeconds;
                    }
                    else
                    {
                        currentElevation = elevationTrack[i].Value;
                        currentSeconds   = elevationTrack[i].ElapsedSeconds;
                        vam = ((currentElevation - lastElevation) * 60f * 60f) / (currentSeconds - lastSeconds);
                        if (vam < 0 || float.IsNaN(vam) || float.IsNegativeInfinity(vam) || float.IsPositiveInfinity(vam))
                        {
                            vam = 0;
                        }
                        vamTrack.Add(startTime.AddSeconds(elevationTrack[i].ElapsedSeconds), vam);
                        lastSeconds   = currentSeconds;
                        lastElevation = currentElevation;
                    }
                }

                return(vamTrack);
            }
            else
            {
                return(null);
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Gets the cached track if available, or calculates from scratch if not available.
        /// </summary>
        /// <param name="activity">Activity to calculate</param>
        /// <param name="chartType">Which track to calculate</param>
        /// <param name="create">Cache the track if it doesn't exist.
        /// True will always return a track.
        /// False will return the cached track, or null if not already cached.</param>
        /// <returns>Cached track if available, or empty track if not found.</returns>
        private static INumericTimeDataSeries GetMeanMaxTrack(IActivity activity, Common.TrackType chartType, out INumericTimeDataSeries timeTrack, bool create)
        {
            if (activity == null)
            {
                timeTrack = new NumericTimeDataSeries();
                return(new NumericTimeDataSeries());
            }

            string id = activity.ReferenceId + chartType;

            if (tracks.ContainsKey(id))
            {
                // Returned cached value from memory :)
                timeTrack = tracks[id + "T"];
                return(tracks[id]);
            }
            else if (create)
            {
                // Not in cache, create a new mean-max track :(
                INumericTimeDataSeries track = new NumericTimeDataSeries();
                timeTrack = null;

                switch (chartType)
                {
                case Common.TrackType.HR:
                {
                    track = GetMeanMaxTrack(activity.HeartRatePerMinuteTrack, out timeTrack, activity.StartTime);
                    break;
                }

                case Common.TrackType.Power:
                {
                    track = GetMeanMaxTrack(activity.PowerWattsTrack, out timeTrack, activity.StartTime);
                    break;
                }

                case Common.TrackType.Cadence:
                {
                    track = GetMeanMaxTrack(activity.CadencePerMinuteTrack, out timeTrack, activity.StartTime);
                    break;
                }
                }

                // Add data track and related 'T'ime track to cache for next time
                MeanMaxCache.AddTrack(track, id);
                MeanMaxCache.AddTrack(timeTrack, id + "T");

                return(track);
            }
            else
            {
                // Not previously cached, AND requested not to create a new cached item.
                timeTrack = new NumericTimeDataSeries();
                return(null);
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Combine a list of tracks by averaging all of the values together.
        /// Start times are ignored, ElapsedSeconds is used for track alignment.  Each track is weighted equally.
        /// </summary>
        /// <param name="tracks">List of tracks to average together</param>
        /// <returns>A single track representing the average data series.</returns>
        private static INumericTimeDataSeries CombineAvgTracks(IList <INumericTimeDataSeries> tracks)
        {
            // Quick data analysis... return quickly if possible
            if (tracks.Count == 0)
            {
                // No data to analyze
                return(null);
            }
            else if (tracks.Count == 1)
            {
                // Only 1 track.  It is it's own average :)
                return(tracks[0]);
            }

            // Setup for analysis...
            INumericTimeDataSeries avgTrack  = new NumericTimeDataSeries();
            INumericTimeDataSeries longTrack = tracks[0];

            // Find longest track to be used as index later
            foreach (INumericTimeDataSeries track in tracks)
            {
                if (longTrack.TotalElapsedSeconds < track.TotalElapsedSeconds)
                {
                    longTrack = track;
                }
            }

            // Average the values:  Use longTrack as the reference because it'll cover all tracks
            //  (some may be shorter, which is OK)
            // Using this track as a reference will also be more efficient because it
            //  won't go through every second, it'll take advantage of the logarithmic algos elsewhere.
            foreach (ITimeValueEntry <float> index in longTrack)
            {
                // Sum & count used later to determine average
                float sum   = 0;
                int   count = 0;

                // Collect average point in each track
                foreach (INumericTimeDataSeries track in tracks)
                {
                    ITimeValueEntry <float> item = track.GetInterpolatedValue(track.StartTime.AddSeconds(index.ElapsedSeconds));
                    if (item != null)
                    {
                        sum += item.Value;
                        count++;
                    }
                }

                // Store average
                avgTrack.Add(longTrack.EntryDateTime(index), sum / count);
            }

            return(avgTrack);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Convert a track from speed to pace.  Length units are maintained.  Assumes input is in hours (mph, km/hr), and converts to seconds base (sec/mile, sec/km).
        /// </summary>
        /// <param name="track"></param>
        /// <returns></returns>
        public static INumericTimeDataSeries SpeedToPace(INumericTimeDataSeries track)
        {
            INumericTimeDataSeries result = new NumericTimeDataSeries(track);

            foreach (TimeValueEntry <float> item in result)
            {
                // Convert to pace if required
                item.Value = (float)Utilities.SpeedToPace(item.Value) * Common.SecondsPerMinute;
            }

            return(result);
        }
Exemplo n.º 8
0
        /// <summary>
        /// Convert a track data from meters to (toUnits)
        /// </summary>
        /// <param name="track">Input track</param>
        /// <param name="toUnits">Desired units</param>
        /// <returns>Converted track</returns>
        public static INumericTimeDataSeries ConvertDistanceUnits(INumericTimeDataSeries track, Length.Units toUnits)
        {
            INumericTimeDataSeries result = new NumericTimeDataSeries(track);

            foreach (TimeValueEntry <float> item in result)
            {
                // Convert to proper distance units from m/s
                item.Value = (float)Length.Convert(item.Value, Length.Units.Meter, toUnits) * Common.SecondsPerHour;
            }

            return(result);
        }
Exemplo n.º 9
0
        public INumericTimeDataSeries populateNumericDataTrack(float[,] activityData, DateTime activityStartTime)
        {
            INumericTimeDataSeries dataSeries = new NumericTimeDataSeries();

            for (int i = 0; i < activityData.GetLength(1); i++)
            {
                DateTime activityDataPointTime = activityStartTime.AddMilliseconds((double)activityData[0, i]);;
                dataSeries.Add(activityDataPointTime, activityData[1, i]);
                // logger.writeLog("populateNumericDataTrack - activity data point time" + activityDataPointTime.ToString() + i.ToString());
            }
            return(dataSeries);
        }
Exemplo n.º 10
0
        public static INumericTimeDataSeries GetRawGearTrack(INumericTimeDataSeries cadenceTrack, INumericTimeDataSeries distanceTrack)
        {
            if (cadenceTrack == null || cadenceTrack.Count == 0 ||
                distanceTrack == null || distanceTrack.Count == 0)
            {
                return(new NumericTimeDataSeries());
            }

            // Set min & max ratio for filtering data
            float    minRatio = 0;
            float    maxRatio = 12;
            double   speed, deltaDist, deltaSeconds, cadence;
            float    mPerRev;
            DateTime currTime, prevTime;
            ITimeValueEntry <float> currPoint, prevPoint;
            INumericTimeDataSeries  gearSelection = new NumericTimeDataSeries();

            // This loops through the cadence track, and interpolates the distance track
            // This is in case the distance track and cadence track don't match for whatever reason.
            // Cadence was chosen becase it will change erratically, while distance track should be comparitavely smooth.
            for (int i = 1; i < cadenceTrack.Count; i++)
            {
                // Store current point time
                prevTime = cadenceTrack.EntryDateTime(cadenceTrack[i - 1]);
                currTime = cadenceTrack.EntryDateTime(cadenceTrack[i]);

                prevPoint = distanceTrack.GetInterpolatedValue(prevTime);
                currPoint = distanceTrack.GetInterpolatedValue(currTime);

                if (prevPoint != null && currPoint != null)
                {
                    deltaDist    = currPoint.Value - prevPoint.Value;
                    deltaSeconds = (currTime - prevTime).TotalSeconds;
                    speed        = deltaDist / deltaSeconds;

                    cadence = cadenceTrack[i].Value;
                    mPerRev = (float)(speed / cadence * 60);

                    /* Filter bad values
                     *  1) Not pedaling: cad == 0
                     *  2) Bad GPS (unlikely): deltaDist == 0
                     *  3) Out of bounds: minRatio < gearSelection < maxRatio
                     */
                    if (cadence != 0 && deltaDist != 0 && mPerRev > minRatio && mPerRev < maxRatio)
                    {
                        gearSelection.Add(currTime, mPerRev);
                    }
                }
            }

            return(gearSelection);
        }
Exemplo n.º 11
0
        /// <summary>
        /// Multiple each value in a data series by 100.
        /// </summary>
        /// <param name="track"></param>
        /// <returns></returns>
        public static INumericTimeDataSeries GetPercentTrack(INumericTimeDataSeries track)
        {
            if (track == null || track.Count == 0)
            {
                return(track);
            }

            NumericTimeDataSeries percent = new NumericTimeDataSeries(track);

            foreach (TimeValueEntry <float> point in percent)
            {
                point.Value = point.Value * 100f;
            }

            return(percent);
        }
Exemplo n.º 12
0
        public static INumericTimeDataSeries SetTrackStartValueToZero(INumericTimeDataSeries inTrack)
        {
            if (inTrack == null || inTrack.Count == 0)
            {
                return(inTrack);
            }

            NumericTimeDataSeries newTrack = new NumericTimeDataSeries(inTrack);
            float firstPoint = inTrack[0].Value;

            foreach (TimeValueEntry <float> point in newTrack)
            {
                point.Value = point.Value - firstPoint;
            }

            return(newTrack);
        }
Exemplo n.º 13
0
 public static INumericTimeDataSeries RemoveBadTrackData(INumericTimeDataSeries track, double lowValue, double highValue)
 {
     if (track != null && track.Count > 0)
     {
         INumericTimeDataSeries newSeries = new NumericTimeDataSeries();
         for (int i = 0; i < track.Count; i++)
         {
             // If the track data is in the high/low bounds, add it to the new series
             if (track[i].Value <highValue && track[i].Value> lowValue)
             {
                 newSeries.Add(track.StartTime.AddSeconds(track[i].ElapsedSeconds), track[i].Value);
             }
         }
         return(newSeries);
     }
     else
     {
         return(null);
     }
 }
Exemplo n.º 14
0
        /// <summary>
        /// Create gear guess selection chart based on gear ratios
        /// </summary>
        /// <param name="input">Pre-calculated raw data to estimate from.  This should already be smoothed or filtered if desired.</param>
        /// <returns>Gear guess NumericTimeDataSeries</returns>
        public static NumericTimeDataSeries GuessGears(INumericTimeDataSeries input, List <SprocketCombo> sprockets)
        {
            ITimeDataSeries <SprocketCombo> guessSprockets = GuessSprockets(input, sprockets);
            NumericTimeDataSeries           guessSeries    = new NumericTimeDataSeries();
            DateTime lastTime = input.StartTime;

            // Iterate through entire data series, recreating the result
            foreach (ITimeValueEntry <SprocketCombo> item in guessSprockets)
            {
                float ratio = 0;

                if (item.Value != null)
                {
                    ratio = item.Value.GearRatio;
                }

                guessSeries.Add(guessSprockets.EntryDateTime(item), ratio);
            }

            return(guessSeries);
        }
Exemplo n.º 15
0
 /// <summary>
 /// Create an elevation track if one doesn't exists.
 /// </summary>
 /// <param name="activity">Activity that needs an elevation track</param>
 /// <returns>If an elevation track exists, return it.  Otherwise, create it from GPS</returns>
 public static INumericTimeDataSeries GetElevationTrack(IActivity activity)
 {
     if (activity.GPSRoute != null && activity.GPSRoute.Count > 0)
     {
         // Get the track from GPS data
         INumericTimeDataSeries newSeries = new NumericTimeDataSeries();
         for (int i = 0; i < activity.GPSRoute.Count; i++)
         {
             //DateTime time = activity.StartTime.AddSeconds(activity.GPSRoute[i].ElapsedSeconds);
             // Changed from activity.StartTime to GPSRoute.EntryDateTime to catch start time differences (old line above)
             DateTime time = activity.GPSRoute.EntryDateTime(activity.GPSRoute[i]);
             newSeries.Add(time, activity.GPSRoute[i].Value.ElevationMeters);
         }
         return(newSeries);
     }
     else
     {
         // Return the elevation track
         return(activity.ElevationMetersTrack);
     }
 }
        internal void RefreshPage()
        {
            if (!ActivityComparerDetail.Instance.Visible || activities == null)
            {
                return;
            }

            INumericTimeDataSeries meanMax     = new NumericTimeDataSeries();
            INumericTimeDataSeries activityMax = new NumericTimeDataSeries();

            meanMax.AllowMultipleAtSameTime = true;

            DateTime start = DateTime.Now.Date;

            foreach (IActivity mmact in activities)
            {
                // Try to pull from memory if available
                //track = MeanMaxCache.GetTrack(activity, ChartType);
                activityMax = ActivityComparerCache.GetTrack(mmact, ChartType);

                // Compile all points together
                foreach (TimeValueEntry <float> item in activityMax)
                {
                    meanMax.Add(start.AddSeconds(item.ElapsedSeconds), item.Value);
                }

                // Remove low points
                for (int i = meanMax.Count - 2; i >= 0; i--)
                {
                    if (meanMax[i].Value < meanMax[i + 1].Value)
                    {
                        meanMax.RemoveAt(i);
                    }
                }
            }

            updateZedGraph(meanMax, zedChart, ChartType);
        }
Exemplo n.º 17
0
        // Commented out to use GetDistanceMovingTrack for features.  This code is still valid, I just don't want to confuse myself
        /// <summary>
        /// Create an distance track if one doesn't exists.
        /// </summary>
        /// <param name="activity">Activity that needs an distance track</param>
        /// <returns>If an distance track exists, return it.  Otherwise, create it from GPS</returns>
        //public static INumericTimeDataSeries GetDistanceTrack(IActivity activity)
        //{
        //    // Get the track from GPS data
        //    if (activity.GPSRoute != null && activity.GPSRoute.Count > 0)
        //    {
        //        INumericTimeDataSeries newSeries = new NumericTimeDataSeries();
        //        IGPSPoint lastPoint = null;
        //        float distanceTotal = 0;
        //        for (int i = 0; i < activity.GPSRoute.Count; i++)
        //        {
        //            DateTime time = activity.StartTime.AddSeconds(activity.GPSRoute[i].ElapsedSeconds);

        //            if (i == 0)
        //            {
        //                newSeries.Add(time, distanceTotal);
        //            }
        //            else
        //            {
        //                distanceTotal += activity.GPSRoute[i].Value.DistanceMetersToPoint(lastPoint);
        //                newSeries.Add(time, distanceTotal);
        //            }

        //            lastPoint = activity.GPSRoute[i].Value;
        //        }
        //        return newSeries;
        //    }
        //    else if (activity.DistanceMetersTrack != null)
        //    {
        //        // If this track was joined, there's a chance the distance data is screwy.  I'll fix it as not to worry about it later.
        //        INumericTimeDataSeries newSeries = new NumericTimeDataSeries();
        //        float lastDistance = 0;
        //        float totalDistance = 0;
        //        float deltaDistance = 0;

        //        DateTime time = activity.DistanceMetersTrack.StartTime;

        //        for (int i = 0; i < activity.DistanceMetersTrack.Count; i++)
        //        {
        //            if (i == 0)
        //            {
        //                totalDistance += activity.DistanceMetersTrack[i].Value;
        //            }
        //            else
        //            {
        //                deltaDistance = activity.DistanceMetersTrack[i].Value - lastDistance;
        //                if (deltaDistance < 0)
        //                {
        //                    // Should i set it to 0 here or to the value of the current track?
        //                    // I lean towards 0 since the track could really have anything in it.
        //                    // I don't want to take the value and it be huge due to joined activities
        //                    // that were parts of other activities (not starting at distance=0)
        //                    deltaDistance = 0;
        //                }
        //                totalDistance += deltaDistance;
        //            }

        //            newSeries.Add(time.AddSeconds(activity.DistanceMetersTrack[i].ElapsedSeconds), totalDistance);
        //            lastDistance = activity.DistanceMetersTrack[i].Value;
        //        }

        //        // Return the distance track
        //        return newSeries;
        //    }
        //    else
        //    {
        //        return null;
        //    }
        //}

        /// <summary>
        /// Create a speed track from an activity's distance track.
        /// Speed is returned in m/s
        /// </summary>
        /// <param name="activity">Activity that needs an speed track</param>
        /// <returns>A speed track created from the distance track</returns>
        public static INumericTimeDataSeries GetSpeedTrack(IActivity activity)
        {
            INumericTimeDataSeries distanceTrack = new NumericTimeDataSeries();

            distanceTrack = GetDistanceMovingTrack(activity);

            INumericTimeDataSeries speedTrack = new NumericTimeDataSeries();

            float    speed           = 0;
            float    lastTime        = 0;
            float    lastDistance    = 0;
            float    currentTime     = 0;
            float    currentDistance = 0;
            DateTime startTime       = activity.StartTime;

            for (int i = 0; i < distanceTrack.Count; i++)
            {
                if (i == 0)
                {
                    speedTrack.Add(startTime, speed);
                    lastTime     = distanceTrack[i].ElapsedSeconds;
                    lastDistance = distanceTrack[i].Value;
                }
                else
                {
                    currentTime     = distanceTrack[i].ElapsedSeconds;
                    currentDistance = distanceTrack[i].Value;

                    speed = (currentDistance - lastDistance) / (currentTime - lastTime);
                    speedTrack.Add(startTime.AddSeconds(distanceTrack[i].ElapsedSeconds), speed);
                    lastTime     = currentTime;
                    lastDistance = currentDistance;
                }
            }

            return(speedTrack);
        }
Exemplo n.º 18
0
 public static INumericTimeDataSeries GetSectionOfTrack(INumericTimeDataSeries track, DateTime start, DateTime end)
 {
     if (track != null && track.Count > 0)
     {
         INumericTimeDataSeries newSeries = new NumericTimeDataSeries();
         for (int i = 0; i < track.Count; i++)
         {
             if (track.StartTime.AddSeconds(track[i].ElapsedSeconds) >= start &&
                 track.StartTime.AddSeconds(track[i].ElapsedSeconds) <= end)
             {
                 newSeries.Add(track.StartTime.AddSeconds(track[i].ElapsedSeconds), track[i].Value);
             }
             else if (track.StartTime.AddSeconds(track[i].ElapsedSeconds) >= end)
             {
                 break;
             }
         }
         return(newSeries);
     }
     else
     {
         return(null);
     }
 }
Exemplo n.º 19
0
        public ITimeDataSeries <SprocketCombo> GetRangeSprocketsTrack(RangeInfoCacheWrapper range)
        {
            ITimeDataSeries <SprocketCombo> result = null;

            if (range.ContainsStoredInfo(m_SprocketTrackStoredInfoId) &&
                range.RetrieveStoredInfo(m_SprocketTrackStoredInfoId) != null)
            {
                result = range.RetrieveStoredInfo(m_SprocketTrackStoredInfoId) as ITimeDataSeries <SprocketCombo>;
            }
            else
            {
                INumericTimeDataSeries gears = ActivityGearTrackCache.Instance.CalculateGearTrack(range.Activity);

                gears = Utils.Utils.RemovePausedTimesInTrack(gears, range.Activity);

                NumericTimeDataSeries tempResult = new NumericTimeDataSeries();
                Utils.Utils.ExtractRangeFromDataTrack(gears, range.PauselessRange, tempResult);
                result = GearChart.UI.GearUtils.GuessSprockets(tempResult, Common.Data.GetSprocketCombos(range.Activity));

                range.SaveStoredInfo(m_SprocketTrackStoredInfoId, result);
            }

            return(result);
        }
Exemplo n.º 20
0
        internal void RefreshPage()
        {
            if (activities == null)
            {
                return;
            }

            INumericTimeDataSeries meanMax     = new NumericTimeDataSeries();
            INumericTimeDataSeries timeTrack   = new NumericTimeDataSeries();
            INumericTimeDataSeries activityMax = new NumericTimeDataSeries();
            Dictionary <string, INumericTimeDataSeries> assocTracks = new Dictionary <string, INumericTimeDataSeries>();
            SortedList <float, float> mmTempList = new SortedList <float, float>();

            meanMax.AllowMultipleAtSameTime = true;

            DateTime start = DateTime.Now.Date;

            foreach (IActivity mmact in activities)
            {
                // Try to pull from memory if available
                activityMax = MeanMaxCache.GetMeanMaxTrack(mmact, ChartType, out timeTrack);
#if DebugOFF
                Utilities.ExportTrack(activityMax, "C:\\STexports\\" + "MeanMax" + ".csv");
                Utilities.ExportTrack(mmact.PowerWattsTrack, "C:\\STexports\\" + "RawPowerTrack" + ".csv");
                Utilities.ExportTrack(mmact.HeartRatePerMinuteTrack, "C:\\STexports\\" + "RawHeartRate" + ".csv");
                Utilities.ExportTrack(mmact.CadencePerMinuteTrack, "C:\\STexports\\" + "RawCadence" + ".csv");
#endif
                int numActivities = (activities as List <IActivity>).Count;
                if (numActivities == 1)
                {
                    // Add timetrack - Note this this only available for single activity analysis
                    assocTracks.Add(timeId, timeTrack);

                    // Add associated chart lines (matching HR, Cadence, etc.)
                    foreach (string id in GlobalSettings.Instance.ChartLines)
                    {
                        Common.TrackType lineType = ColumnDefinition.GetTrackType(id);
                        if (lineType != ChartType)
                        {
                            // Get associated track (HR, Cad, etc.)
                            // This line is similar to the Mean-max chart where the time track is the 'period'
                            //  but the data is the HR, Cad., etc. that occurred at the -same time- as the Max effort occurred
                            //  rather than being a max value in itself.
                            //  The value is the average value during the range of time as opposed to a single entry
                            INumericTimeDataSeries track = MeanMaxCache.GetAvgTrack(mmact, lineType, timeTrack);
#if DebugOFF
                            Utilities.ExportTrack(track, "C:\\STexports\\MM" + ColumnDefinition.GetText(id) + ".csv");
#endif
                            if (!assocTracks.ContainsKey(id) && track != null)
                            {
                                // Store associated tracks for charting
                                assocTracks.Add(id, track);
                            }
                            else
                            {
                                // TODO: Combine multiple tracks for averaging.  Currently it'll only display the first activity track :(
                            }
                        }
                    }
                }

                // Compile all points together (used for multiple activities)
                foreach (TimeValueEntry <float> item in activityMax)
                {
                    /* If(entry not exist in meanMax || current value > existing value)
                     *  {
                     *   Add new MM entry
                     *   Update assoc track values
                     *  }
                     *  else
                     *  {
                     *
                     *  }
                     */

                    // Add to temporary sortedlist
                    if (!mmTempList.ContainsKey(item.ElapsedSeconds))
                    {
                        mmTempList.Add(item.ElapsedSeconds, item.Value);
                    }
                    else if (mmTempList.ContainsKey(item.ElapsedSeconds) && mmTempList[item.ElapsedSeconds] < item.Value)
                    {
                        mmTempList[item.ElapsedSeconds] = item.Value;
                    }
                    //meanMax.Add(start.AddSeconds(item.ElapsedSeconds), item.Value);
                }

                // Copy sorted temporary list to proper numeric time series
                foreach (float seconds in mmTempList.Keys)
                {
                    meanMax.Add(start.AddSeconds(seconds), mmTempList[seconds]);
                }

                // Remove low points (used for multiple activities)
                for (int i = meanMax.Count - 2; i >= 0; i--)
                {
                    if (meanMax[i].Value < meanMax[i + 1].Value)
                    {
                        meanMax.RemoveAt(i);
                    }
                }
            }

            updateZedGraph(meanMax, assocTracks, zedChart, ChartType);
        }
Exemplo n.º 21
0
        private INumericTimeDataSeries GetSmoothedActivityTrack(LineChartTypes chartType)
        {
            // Fail safe
            INumericTimeDataSeries result = new NumericTimeDataSeries();

            if (Activity != null)
            {
                switch (chartType)
                {
                case LineChartTypes.Cadence:
                {
                    result = m_ActivityInfoCache.SmoothedCadenceTrack;
                    break;
                }

                case LineChartTypes.Elevation:
                {
                    INumericTimeDataSeries tempResult = m_ActivityInfoCache.SmoothedElevationTrack;

                    // Value is in meters so convert to the right unit
                    result = new NumericTimeDataSeries();
                    foreach (ITimeValueEntry <float> entry in tempResult)
                    {
                        double temp = Length.Convert(entry.Value, Length.Units.Meter, Activity.Category.ElevationUnits);

                        result.Add(tempResult.EntryDateTime(entry), (float)temp);
                    }
                    break;
                }

                case LineChartTypes.Grade:
                {
                    result = new NumericTimeDataSeries();

                    INumericTimeDataSeries tempResult = m_ActivityInfoCache.SmoothedGradeTrack;

                    foreach (ITimeValueEntry <float> entry in tempResult)
                    {
                        result.Add(tempResult.EntryDateTime(entry), entry.Value * 100.0f);
                    }
                    break;
                }

                case LineChartTypes.HeartRateBPM:
                {
                    result = m_ActivityInfoCache.SmoothedHeartRateTrack;
                    break;
                }

                case LineChartTypes.HeartRatePercentMax:
                {
                    result = new NumericTimeDataSeries();

                    IAthleteInfoEntry lastAthleteEntry = PluginMain.GetApplication().Logbook.Athlete.InfoEntries.LastEntryAsOfDate(m_ActivityInfoCache.ActualTrackStart);

                    // Value is in BPM so convert to the % max HR if we have the info
                    if (!float.IsNaN(lastAthleteEntry.MaximumHeartRatePerMinute))
                    {
                        INumericTimeDataSeries tempResult = m_ActivityInfoCache.SmoothedHeartRateTrack;

                        foreach (ITimeValueEntry <float> entry in tempResult)
                        {
                            double temp = (entry.Value / lastAthleteEntry.MaximumHeartRatePerMinute) * 100;

                            result.Add(tempResult.EntryDateTime(entry), (float)temp);
                        }
                    }
                    break;
                }

                case LineChartTypes.Power:
                {
                    result = m_ActivityInfoCache.SmoothedPowerTrack;
                    break;
                }

                case LineChartTypes.Speed:
                {
                    INumericTimeDataSeries tempResult = m_ActivityInfoCache.SmoothedSpeedTrack;

                    // Value is in m/sec so convert to the right unit and to
                    //  pace if necessary
                    result = new NumericTimeDataSeries();
                    foreach (ITimeValueEntry <float> entry in tempResult)
                    {
                        double temp = Length.Convert(entry.Value, Length.Units.Meter, Utils.Utils.MajorLengthUnit(Activity.Category.DistanceUnits)) * Utils.Constants.SecondsPerHour;

                        if (Activity.Category.SpeedUnits == Speed.Units.Pace)
                        {
                            // Convert to pace and then in second
                            temp = Utils.Utils.SpeedToPace(temp) * Utils.Constants.SecondsPerMinute;
                        }

                        result.Add(tempResult.EntryDateTime(entry), (float)temp);
                    }
                    break;
                }

                default:
                {
                    Debug.Assert(false);
                    break;
                }
                }
            }

            return(result);
        }
Exemplo n.º 22
0
        private static Record GetDistancePaceRecord(IActivity activity, RecordCategory category)
        {
            float fastestSpeed = 0;
            float currentSpeed;
            float currentDistance = 0;

            if (activity.GPSRoute != null && activity.GPSRoute.TotalElapsedSeconds > 0 && ActivityInfoCache.Instance.GetInfo(activity).DistanceMetersMoving >= category.Meters)
            {
                int           recordStart = 0, recordEnd = 0, startIndex, endIndex = 0;
                List <double> p2pDistance = new List <double>();

                // Go through each starting point
                for (startIndex = 0; startIndex < activity.GPSRoute.Count; startIndex++)
                {
                    // Find end GPS point that's the proper distance away
                    while (currentDistance <= category.Meters)
                    {
                        endIndex += 1;

                        // Typical return point.  End has exceeded route.  Construct and return record for this activity/category.
                        if (endIndex >= activity.GPSRoute.Count)
                        {
                            // Construct record GPS route
                            GPSRoute recordGPS = new GPSRoute();
                            NumericTimeDataSeries recordHRTrack = new NumericTimeDataSeries();
                            NumericTimeDataSeries pwrTrack      = new NumericTimeDataSeries();
                            NumericTimeDataSeries elevTrack     = new NumericTimeDataSeries();
                            NumericTimeDataSeries cadTrack      = new NumericTimeDataSeries();
                            DistanceDataTrack     distTrack     = new DistanceDataTrack();

                            for (int i = recordStart; i <= recordEnd; i++)
                            {
                                // Record information/statistics
                                DateTime time = activity.GPSRoute.EntryDateTime(activity.GPSRoute[i]);

                                recordGPS.Add(time, activity.GPSRoute[i].Value);
                                if (activity.HeartRatePerMinuteTrack != null &&
                                    activity.HeartRatePerMinuteTrack.StartTime <= time &&
                                    activity.HeartRatePerMinuteTrack.StartTime.AddSeconds(activity.HeartRatePerMinuteTrack.TotalElapsedSeconds) >= time)
                                {
                                    recordHRTrack.Add(time, activity.HeartRatePerMinuteTrack.GetInterpolatedValue(time).Value);
                                }

                                if (activity.PowerWattsTrack != null &&
                                    activity.PowerWattsTrack.StartTime <= time &&
                                    activity.PowerWattsTrack.StartTime.AddSeconds(activity.PowerWattsTrack.TotalElapsedSeconds) >= time)
                                {
                                    pwrTrack.Add(time, activity.PowerWattsTrack.GetInterpolatedValue(time).Value);
                                }

                                if (activity.CadencePerMinuteTrack != null &&
                                    activity.CadencePerMinuteTrack.StartTime <= time &&
                                    activity.CadencePerMinuteTrack.StartTime.AddSeconds(activity.CadencePerMinuteTrack.TotalElapsedSeconds) >= time)
                                {
                                    cadTrack.Add(time, activity.CadencePerMinuteTrack.GetInterpolatedValue(time).Value);
                                }

                                if (activity.DistanceMetersTrack != null &&
                                    activity.DistanceMetersTrack.StartTime <= time &&
                                    activity.DistanceMetersTrack.StartTime.AddSeconds(activity.DistanceMetersTrack.TotalElapsedSeconds) >= time)
                                {
                                    distTrack.Add(time, activity.DistanceMetersTrack.GetInterpolatedValue(time).Value);
                                }

                                if (activity.ElevationMetersTrack != null &&
                                    activity.ElevationMetersTrack.StartTime <= time &&
                                    activity.ElevationMetersTrack.StartTime.AddSeconds(activity.ElevationMetersTrack.TotalElapsedSeconds) >= time)
                                {
                                    elevTrack.Add(time, activity.ElevationMetersTrack.GetInterpolatedValue(time).Value);
                                }
                            }

                            // Return record
                            Record record = new Record(activity, category, recordGPS, recordHRTrack, pwrTrack, cadTrack, distTrack, elevTrack);
                            return(record);
                        }
                        else
                        {
                            // Add to end of route until category distance is found
                            currentDistance += activity.GPSRoute[endIndex].Value.DistanceMetersToPoint(activity.GPSRoute[endIndex - 1].Value);
                        }
                    }

                    // In meters / second
                    currentSpeed = currentDistance / (activity.GPSRoute[endIndex].ElapsedSeconds - activity.GPSRoute[startIndex].ElapsedSeconds);

                    // Store fastest info (in meters / second)
                    if (fastestSpeed < currentSpeed)
                    {
                        fastestSpeed = currentSpeed;
                        recordStart  = startIndex;
                        recordEnd    = endIndex;
                    }

                    // Remove first point from routeDistance, and go to next starting point.
                    currentDistance -= activity.GPSRoute[startIndex].Value.DistanceMetersToPoint(activity.GPSRoute[startIndex + 1].Value);
                }
            }

            // Activity does not contain GPS data or not long enough
            return(null);
        }
Exemplo n.º 23
0
        /// <summary>
        /// Get critical value track.  This is the full track of critical values for various activities over a date range until 'now'.
        /// /// </summary>
        /// <param name="activities">The list of activities to consider</param>
        /// <param name="trackType">Which data track to evaluate</param>
        /// <param name="seconds">The critical period, measured in seconds.  Example, for 5 minute power this would be 300.</param>
        /// <returns>Returns a data track of values over a date range.  1 value per day.</returns>
        internal static INumericTimeDataSeries GetCriticalTrack(IEnumerable <IActivity> activities, Common.TrackType trackType, float seconds)
        {
            float criticalValue = 0;
            SortedList <DateTime, float> criticalData = new SortedList <DateTime, float>();
            INumericTimeDataSeries       criticalTrack = new NumericTimeDataSeries();
            float i = 0, count = (activities as List <IActivity>).Count;


            // Populate activity data
            foreach (IActivity activity in activities)
            {
                Progress = (int)(i++ / count * 100f);
                Application.DoEvents();

                // Filter bad data
                if (activity.StartTime.Year != 1)
                {
                    criticalValue = MeanMaxCache.GetCriticalValue(activity, trackType, seconds);
                    if (!float.IsNaN(criticalValue))
                    {
                        DateTime activityDate = activity.StartTime.Add(activity.TimeZoneUtcOffset).Date;

                        if (criticalData.ContainsKey(activityDate))
                        {
                            // Handle days with multiple activities
                            // Update with higher value
                            criticalData[activityDate] = Math.Max(criticalValue, criticalData[activityDate]);
                        }
                        else if (criticalValue != 0)
                        {
                            // Add critical value to data set
                            criticalData.Add(activityDate, criticalValue);
                        }
                    }
                }
            }

            // Construct critical data track
            DateTime firstDate;

            if (criticalData.Count > 0)
            {
                firstDate     = criticalData.Keys[0];
                criticalValue = criticalData[firstDate];
            }
            else
            {
                firstDate = DateTime.Now;
            }
            float value;

            for (DateTime day = firstDate; day < DateTime.Now;)
            {
                // Decay the critical value first, value for 'today'
                //criticalValue = criticalValue - criticalValue / GlobalSettings.Instance.TCc;

                if (criticalData.TryGetValue(day.Date, out value))
                {
                    // TODO: Determine best way to incorporate decay values
                    // Value recorded for today or previous (decayed) value
                    //criticalValue = Math.Max(value, criticalValue);
                    //criticalTrack.Add(day, criticalValue);
                    criticalTrack.Add(day, value);
                }
                else
                {
                    // Decayed value
                    //criticalTrack.Add(day, criticalValue);
                }
                // Next day
                day = day.AddDays(1);
            }

            Progress = -1;

            return(criticalTrack);
        }
Exemplo n.º 24
0
        /// <summary>
        /// Create a grade track from an activity's elevation track
        /// </summary>
        /// <param name="activity">Activity that needs an grade track</param>
        /// <returns>A grade track created from the elevation track</returns>
        public static INumericTimeDataSeries GetGradeTrack(IActivity activity)
        {
            INumericTimeDataSeries gradeTrack     = new NumericTimeDataSeries();
            INumericTimeDataSeries elevationTrack = new NumericTimeDataSeries();
            INumericTimeDataSeries distanceTrack  = new NumericTimeDataSeries();

            elevationTrack = GetElevationTrack(activity);
            distanceTrack  = GetDistanceMovingTrack(activity);
            float    grade            = 0;
            float    lastElevation    = 0;
            float    lastDistance     = 0;
            float    currentElevation = 0;
            float    currentDistance  = 0;
            DateTime startTime        = activity.StartTime;

            if (gradeTrack != null && elevationTrack != null && distanceTrack != null)
            {
                for (int i = 0; i < elevationTrack.Count; i++)
                {
                    if (i == 0)
                    {
                        gradeTrack.Add(startTime, grade);
                        lastElevation = elevationTrack[i].Value;
                        ITimeValueEntry <float> point = distanceTrack.GetInterpolatedValue(startTime.AddSeconds(elevationTrack[i].ElapsedSeconds));
                        if (point != null)
                        {
                            lastDistance = point.Value;
                        }
                    }
                    else
                    {
                        ITimeValueEntry <float> point = distanceTrack.GetInterpolatedValue(startTime.AddSeconds(elevationTrack[i].ElapsedSeconds));
                        if (point != null)
                        {
                            currentDistance = point.Value;
                        }
                        else
                        {
                            currentDistance = lastDistance;
                        }

                        currentElevation = elevationTrack[i].Value;
                        grade            = (currentElevation - lastElevation) / (currentDistance - lastDistance);
                        if (float.IsNaN(grade) || float.IsInfinity(grade))
                        {
                            grade = 0;
                        }
                        gradeTrack.Add(startTime.AddSeconds(elevationTrack[i].ElapsedSeconds), grade);

                        lastDistance  = currentDistance;
                        lastElevation = currentElevation;
                    }
                }

                return(gradeTrack);
            }
            else
            {
                return(null);
            }
        }
Exemplo n.º 25
0
        /// <summary>
        /// Gets an average value over a specific timeframe in an activity track.
        /// Used to calculate associated values, for example.
        /// </summary>
        /// <param name="activity">Activity to analyze</param>
        /// <param name="pointType">Which data track is data requested from</param>
        /// <param name="start">Start time of requested average period (in UTC time)</param>
        /// <param name="period">Duration of time (in seconds) to evaluate with respect to start time</param>
        /// <returns>The average value of X track, from 'start' to 'period' seconds later.</returns>
        internal static float GetAvgValue(IActivity activity, Common.TrackType pointType, DateTime start, uint period)
        {
            // Check for bad data
            if (activity == null || period < 0)
            {
                return(float.NaN);
            }

            INumericTimeDataSeries track = null;

            switch (pointType)
            {
            case Common.TrackType.Cadence:
                track = activity.CadencePerMinuteTrack;
                break;

            case Common.TrackType.HR:
                track = activity.HeartRatePerMinuteTrack;
                break;

            case Common.TrackType.Power:
                track = activity.PowerWattsTrack;
                break;

            case Common.TrackType.Grade:
                track = new NumericTimeDataSeries(ActivityInfoCache.Instance.GetInfo(activity).SmoothedGradeTrack);
                for (int i = 0; i < track.Count; i++)
                {
                    track.SetValueAt(i, track[i].Value * 100f);
                }
                break;
            }

            // No track data
            if (track == null)
            {
                return(float.NaN);
            }

            // Find average value:  Sum entries every 1 second, then divide it out at the end
            float    sum  = 0;
            DateTime time = start;
            TimeSpan span = TimeSpan.Zero;

            while (time < start.AddSeconds(period))
            {
                // Sum all values (we'll divide it out at the end)
                ITimeValueEntry <float> item = track.GetInterpolatedValue(time);

                // Ignore bad values... they're simply excluded from calculation
                //  and the average is taken from a smaller subset of values
                if (item != null)
                {
                    sum += item.Value;
                    span = span.Add(TimeSpan.FromSeconds(1));
                }

                time = time.AddSeconds(1);
            }

            // Divide sum by period to get the average value
            if (span.TotalSeconds > 0)
            {
                return(sum / (int)span.TotalSeconds);
            }
            else
            {
                // Bad data.  Oops, requested time range didn't exist for the requested track.
                return(float.NaN);
            }
        }
Exemplo n.º 26
0
        /// <summary>
        /// Calculate Mean-Max track from scratch.  Elapsed seconds = amount of time maintained at point.Value.
        /// </summary>
        /// <param name="source">Data track to calculate</param>
        /// <param name="timeTrack">Out parameter describing when each 'mean-max' instance was found.
        /// Time track is the period (matching the primary mean-max track).
        /// Value of each point is the .ElapsedSeconds (relative to activity start) for each Mean-Max occurrance.
        /// This provides a key to locate associated data in other parts of the activity that occurred at the
        /// same time (such as HR, cadence, etc.)  Elapsed Seconds (relative to activity start, not track start)
        /// is the start of the occurance.  Elapsed + period would define the full range of the occurance.</param>
        /// <param name="activityStart">Activity start time.  This is used as a reference point
        /// to align the data tracks.  See 'timeTrack' parameter.</param>
        /// <returns>Primary Mean-max data track, & 'timeTrack' as an out parameter.</returns>
        private static INumericTimeDataSeries GetMeanMaxTrack(INumericTimeDataSeries source, out INumericTimeDataSeries timeTrack, DateTime activityStart)
        {
            INumericTimeDataSeries meanMaxTrack = new NumericTimeDataSeries(); // Stores the mean-max data

            timeTrack = new NumericTimeDataSeries();                           // Stores correlation data to link when max efforts occurred

            float previous = 0;

            if (source != null && source.Count > 0)
            {
                meanMaxTrack.AllowMultipleAtSameTime = true;

                float min, max, seconds = 0;
                uint  maxCalc    = source.TotalElapsedSeconds - 1;
                float resolution = 1.07f; // <-- This number will change how closely the calculated points are to one another.  Big number is grainy, small number is precise.
                maxCalc = maxCalc / 2;

                // Store offset (difference between activity start time and track start).
                // Sometimes data doesn't start at same time as activity and
                // the activity start time is used as a common reference to relate other tracks together.
                int trackOffset = (int)(source.StartTime - activityStart).TotalSeconds;

                // Add the 'first' mean max point.  This is necessary to align all other points with start.
                // Without this, mean-max data would slide to the left.
                meanMaxTrack.Add(activityStart, source.Max);

                // Assign first timeTrack point as well.
                foreach (ITimeValueEntry <float> point in source)
                {
                    if (point.Value == source.Max)
                    {
                        // Set to elapsed time (in seconds) relative to activity start date.
                        timeTrack.Add(activityStart, point.ElapsedSeconds + trackOffset);
                        break;
                    }
                }

                for (uint period = maxCalc; period >= 1;)
                {
                    // Progress status
                    float percent = (((float)period / (float)maxCalc) - 1F) * -100F;

                    try
                    {
                        Application.DoEvents();
                    }
                    catch { }

                    // NOTE: Smoothing is # seconds on each side FOR ST ALGO... not total seconds (i.e. '30' would be smoothing over 60 total seconds... 30 on each side)
                    //INumericTimeDataSeries smooth = ZoneFiveSoftware.Common.Data.Algorithm.NumericTimeDataSeries.Smooth(source, (int)period, out min, out max);
                    //INumericTimeDataSeries smooth = Utilities.STSmooth(source, period, out min, out max);
                    //Utilities.ExportTrack(smooth, "C:\\Smooth\\" + period + "_ST.csv");
                    INumericTimeDataSeries smooth = Utilities.Smooth(source, period * 2, out min, out max);

                    // Add current point to data track
                    if (max >= previous)
                    {
                        // Find where max occurred
                        foreach (TimeValueEntry <float> item in smooth)
                        {
                            if (item.Value == max)
                            {
                                // Store exactly this occurs
                                seconds = item.ElapsedSeconds + trackOffset - period;
                                break;
                            }
                        }
                    }
                    else
                    {
                        // Keep values from previous iteration.  'seconds' will remain untouched.
                        max = previous;
                    }

                    // DateTime portion (first entry) is irrelevant (can be anything) to tracks below.
                    meanMaxTrack.Add(activityStart.AddSeconds(period * 2), max);
                    timeTrack.Add(activityStart.AddSeconds(period * 2), seconds);

                    period   = (uint)Math.Min(period - 1, (float)period / resolution);
                    previous = max;
                }
            }

            // Return populated data track, or new (empty) data track if appropriate
            return(meanMaxTrack);
        }
Exemplo n.º 27
0
        /// <summary>
        /// Perform a smoothing operation using a moving average on the data series
        /// </summary>
        /// <param name="track">The data series to smooth</param>
        /// <param name="period">The range to smooth.  This is the total number of seconds to smooth across (slightly different than the ST method.)</param>
        /// <param name="min">An out parameter set to the minimum value of the smoothed data series</param>
        /// <param name="max">An out parameter set to the maximum value of the smoothed data series</param>
        /// <returns></returns>
        internal static INumericTimeDataSeries Smooth(INumericTimeDataSeries track, uint period, out float min, out float max)
        {
            min = float.NaN;
            max = float.NaN;
            INumericTimeDataSeries smooth = new NumericTimeDataSeries();

            if (track != null && track.Count > 0 && period > 1)
            {
                //min = float.NaN;
                //max = float.NaN;
                int   start = 0;
                int   index = 0;
                float value = 0, firstAvg = 0;
                float delta;

                float per    = period;
                int   offset = (int)period / 2;

                // TODO: (LOW) Handle smoothing beginning and end of track.  Remove debug line below and replace with real code.  Debug establishes the first point.
                smooth.Add(track.StartTime, track[0].Value);
                //  Idea: Fill unavailable points with first (and last) fully averaged values to weight everything appropriately.

                //// Find first average set
                //while (track[index].ElapsedSeconds < track[start].ElapsedSeconds + period)
                //{
                //    delta = track[index + 1].ElapsedSeconds - track[index].ElapsedSeconds;
                //    firstAvg += track[index].Value * delta;
                //    index++;
                //}

                //// Finish value calculation
                //per = track[index].ElapsedSeconds - track[start].ElapsedSeconds;
                //firstAvg = firstAvg / per;

                // Iterate through track
                // For each point, create average starting with 'start' index and go forward averaging 'period' seconds.
                // Stop when last 'full' period can be created ([start].ElapsedSeconds + 'period' seconds >= TotalElapsedSeconds)
                index = 0;
                while (track[start].ElapsedSeconds + period < track.TotalElapsedSeconds)
                {
                    while (track[index].ElapsedSeconds < track[start].ElapsedSeconds + period)
                    {
                        delta  = track[index + 1].ElapsedSeconds - track[index].ElapsedSeconds;
                        value += track[index].Value * delta;
                        index++;
                    }

                    // Finish value calculation
                    per   = track[index].ElapsedSeconds - track[start].ElapsedSeconds;
                    value = value / per;

                    // Add value to track - Offset will align it so that the value is centered within the period
                    smooth.Add(track.EntryDateTime(track[index]).AddSeconds(-offset), value);

                    // Remove beginning point for next cycle
                    delta = track[start + 1].ElapsedSeconds - track[start].ElapsedSeconds;
                    value = (per * value - delta * track[start].Value);

                    // Next point
                    start++;
                }

                max = smooth.Max;
                min = smooth.Min;
            }
            else if (track != null && track.Count > 0 && period == 1)
            {
                min = track.Min;
                max = track.Max;
                return(track);
            }

            return(smooth);
        }
Exemplo n.º 28
0
        /// <summary>
        /// Removes the track pauses within a data track.
        /// </summary>
        /// <param name="sourceTrack">Data track</param>
        /// <param name="activity">Activity containing pause definitions</param>
        /// <returns>Returns the source track with the track pause times removed</returns>
        public static INumericTimeDataSeries RemovePausedTimesInTrack(INumericTimeDataSeries sourceTrack, IActivity activity)
        {
            ActivityInfo activityInfo = ActivityInfoCache.Instance.GetInfo(activity);

            if (activityInfo != null && sourceTrack != null)
            {
                if (activityInfo.NonMovingTimes.Count == 0)
                {
                    return(sourceTrack);
                }
                else
                {
                    INumericTimeDataSeries result = new NumericTimeDataSeries();
                    DateTime currentTime          = sourceTrack.StartTime;
                    IEnumerator <ITimeValueEntry <float> > sourceEnumerator = sourceTrack.GetEnumerator();
                    IEnumerator <IValueRange <DateTime> >  pauseEnumerator  = activityInfo.NonMovingTimes.GetEnumerator();
                    double totalPausedTimeToDate = 0;
                    bool   sourceEnumeratorIsValid;
                    bool   pauseEnumeratorIsValid;

                    pauseEnumeratorIsValid  = pauseEnumerator.MoveNext();
                    sourceEnumeratorIsValid = sourceEnumerator.MoveNext();

                    while (sourceEnumeratorIsValid)
                    {
                        bool addCurrentSourceEntry     = true;
                        bool advanceCurrentSourceEntry = true;

                        // Loop to handle all pauses up to this current track point
                        if (pauseEnumeratorIsValid)
                        {
                            if (currentTime >= pauseEnumerator.Current.Lower &&
                                currentTime <= pauseEnumerator.Current.Upper)
                            {
                                addCurrentSourceEntry = false;
                            }
                            else if (currentTime > pauseEnumerator.Current.Upper)
                            {
                                // Advance pause enumerator
                                totalPausedTimeToDate += (pauseEnumerator.Current.Upper - pauseEnumerator.Current.Lower).TotalSeconds;
                                pauseEnumeratorIsValid = pauseEnumerator.MoveNext();

                                // Make sure we retry with the next pause
                                addCurrentSourceEntry     = false;
                                advanceCurrentSourceEntry = false;
                            }
                        }

                        if (addCurrentSourceEntry)
                        {
                            result.Add(currentTime - new TimeSpan(0, 0, (int)totalPausedTimeToDate), sourceEnumerator.Current.Value);
                        }

                        if (advanceCurrentSourceEntry)
                        {
                            sourceEnumeratorIsValid = sourceEnumerator.MoveNext();
                            currentTime             = sourceTrack.StartTime + new TimeSpan(0, 0, (int)sourceEnumerator.Current.ElapsedSeconds);
                        }
                    }

                    return(result);
                }
            }

            return(null);
        }
Exemplo n.º 29
0
        //public IDistanceDataTrack GetSmoothedDistanceTrack(int seconds, out float min, out float max)
        //{
        //    IDistanceDataTrack distanceTrack;
        //    if (record.Activity.DistanceMetersTrack != null)
        //    {
        //        // #1 Use Distance track from activity
        //        distanceTrack = record.Activity.DistanceMetersTrack;
        //    }
        //    else
        //    {
        //        if (record.Activity.GPSRoute != null)
        //        {
        //            // #2 Otherwise create a distance track from GPS
        //            distanceTrack = Utilities.CreateDistanceDataTrack(record.Activity);
        //            return Utilities.STSmooth(distanceTrack, seconds, min, max);
        //        }
        //        else
        //        {
        //            // Else, no distance track, and cannot create one.
        //            distanceTrack = new DistanceDataTrack();
        //        }
        //    }
        //}

        //public INumericTimeDataSeries GetSmoothedGradeTrack(int seconds, out float min, out float max)
        //{
        //    NumericTimeDataSeries gradeTrack = new NumericTimeDataSeries();
        //    for (int i = 0; i < record.Activity.ElevationMetersTrack.Count; i++)
        //    {
        //        if (i == 0)
        //        {
        //            gradeTrack.Add(record.Activity.ElevationMetersTrack[i].ElapsedSeconds, 0);
        //        }
        //    }
        //}

        #region Constructors

        public Feature(IActivity activity, feature_type type, DateTime inStartTime, DateTime inEndTime)
        {
            startTime        = inStartTime;
            endTime          = inEndTime;
            added            = false;
            hillNumber       = 0;
            _feature_type    = type;
            masterActivityID = activity.ReferenceId;

            // Default fill and line color
            fillColor     = Color.FromArgb(125, 146, 94, 9);
            lineColor     = Color.FromArgb(255, 146, 94, 9);
            lineWidth     = 1;
            selectedColor = Color.Empty;
            routeWidth    = PluginMain.GetApplication().SystemPreferences.RouteSettings.RouteWidth;

            IGPSRoute recordGPS = new GPSRoute();
            INumericTimeDataSeries recordHRTrack = new NumericTimeDataSeries();
            INumericTimeDataSeries pwrTrack      = new NumericTimeDataSeries();
            INumericTimeDataSeries elevTrack     = new NumericTimeDataSeries();
            INumericTimeDataSeries cadTrack      = new NumericTimeDataSeries();
            IDistanceDataTrack     distTrack     = new DistanceDataTrack();
            RecordCategory         category      = new RecordCategory();

            ActivityInfo ai    = ActivityInfoCache.Instance.GetInfo(activity);
            DateTime     start = activity.StartTime;

            if (activity.GPSRoute != null)
            {
                // Check and make sure the route has points
                if (activity.GPSRoute.Count > 0)
                {
                    // If the time passed in is before the start of the gps track, get the first value
                    if (activity.GPSRoute.StartTime > inStartTime)
                    {
                        startPoint = activity.GPSRoute[0].Value;
                    }
                    else
                    {
                        // Set the start point
                        ITimeValueEntry <IGPSPoint> sPoint = activity.GPSRoute.GetInterpolatedValue(inStartTime);
                        if (sPoint != null)
                        {
                            startPoint = sPoint.Value;
                        }
                    }

                    // If the time passed in is after the end of the gps track, get the last value
                    if (activity.GPSRoute.StartTime.AddSeconds(activity.GPSRoute[activity.GPSRoute.Count - 1].ElapsedSeconds) < inEndTime)
                    {
                        endPoint = activity.GPSRoute[activity.GPSRoute.Count - 1].Value;
                    }
                    else
                    {
                        // Set the end point
                        ITimeValueEntry <IGPSPoint> ePoint = activity.GPSRoute.GetInterpolatedValue(inEndTime);
                        if (ePoint != null)
                        {
                            endPoint = ePoint.Value;
                        }
                    }
                }


                // Create the GPSRoute
                for (int i = 0; i < activity.GPSRoute.Count; i++)
                {
                    if (activity.GPSRoute.StartTime.AddSeconds(activity.GPSRoute[i].ElapsedSeconds) >= inStartTime &&
                        activity.GPSRoute.StartTime.AddSeconds(activity.GPSRoute[i].ElapsedSeconds) <= inEndTime)
                    {
                        recordGPS.Add(activity.GPSRoute.StartTime.AddSeconds(activity.GPSRoute[i].ElapsedSeconds), activity.GPSRoute[i].Value);
                    }
                }
            }

            // Create the Distance Track
            INumericTimeDataSeries allDistanceTrack  = ai.MovingDistanceMetersTrack; // Utilities.GetDistanceTrack(activity);
            INumericTimeDataSeries allElevationTrack = ai.SmoothedElevationTrack;    // Utilities.GetElevationTrack(activity);

            // Work your way through the moving meters track to create all others
            if (allDistanceTrack != null)
            {
                for (int i = 0; i < allDistanceTrack.Count; i++)
                {
                    DateTime time = allDistanceTrack.StartTime.AddSeconds(allDistanceTrack[i].ElapsedSeconds);
                    if (time >= inStartTime &&
                        time <= inEndTime)
                    {
                        // Add the distance point
                        distTrack.Add(time, allDistanceTrack[i].Value);
                        ITimeValueEntry <float> point = null;

                        // Find the elevation point at this time and add it
                        if (allElevationTrack != null && allElevationTrack.Count > 0)
                        {
                            point = allElevationTrack.GetInterpolatedValue(time);
                            if (point != null)
                            {
                                elevTrack.Add(time, point.Value);
                            }
                        }

                        // Find the HR point at this time and add it
                        if (activity.HeartRatePerMinuteTrack != null && activity.HeartRatePerMinuteTrack.Count > 0)
                        {
                            point = activity.HeartRatePerMinuteTrack.GetInterpolatedValue(time);
                            if (point != null)
                            {
                                recordHRTrack.Add(time, point.Value);
                            }
                        }

                        // Find the power point at this time and add it
                        if (activity.PowerWattsTrack != null && activity.PowerWattsTrack.Count > 0)
                        {
                            point = activity.PowerWattsTrack.GetInterpolatedValue(time);
                            if (point != null)
                            {
                                pwrTrack.Add(time, point.Value);
                            }
                        }

                        // Find the cadence point at this time and add it
                        if (activity.CadencePerMinuteTrack != null && activity.CadencePerMinuteTrack.Count > 0)
                        {
                            point = activity.CadencePerMinuteTrack.GetInterpolatedValue(time);
                            if (point != null)
                            {
                                cadTrack.Add(time, point.Value);
                            }
                        }
                    }
                    else if (allDistanceTrack.StartTime.AddSeconds(allDistanceTrack[i].ElapsedSeconds) > inEndTime)
                    {
                        break;
                    }
                }
            }

            // Get the start/end distance
            if (distTrack != null && distTrack.Count > 0)
            {
                startDistance = distTrack[0].Value;
                endDistance   = distTrack[distTrack.Count - 1].Value;
            }
            else
            {
                startDistance = 0;
                endDistance   = 0;
            }

            // Get the start/end elevation
            if (elevTrack != null && elevTrack.Count > 0)
            {
                startElevation = elevTrack[0].Value;
                endElevation   = elevTrack[elevTrack.Count - 1].Value;
            }
            else
            {
                startElevation = 0;
                endElevation   = 0;
            }

            // Build the record
            record = new Record(activity, category, recordGPS, recordHRTrack, pwrTrack, cadTrack, distTrack, elevTrack, inStartTime);

            // Create a reference id for this hill
            refId = Guid.NewGuid().ToString("D");

            double distanceX = endDistance - startDistance;

            distance = distanceX;

            double elev = endElevation - startElevation;

            elevGain = elev;

            // Find the start percents from the distance track
            if (allDistanceTrack != null && allDistanceTrack.Count > 0)
            {
                startPercentDistance = startDistance / allDistanceTrack[allDistanceTrack.Count - 1].Value;
                endPercentDistance   = endDistance / allDistanceTrack[allDistanceTrack.Count - 1].Value;

                startPercentTime = ((inStartTime - allDistanceTrack.StartTime).TotalSeconds / allDistanceTrack[allDistanceTrack.Count - 1].ElapsedSeconds);
                endPercentTime   = ((inEndTime - allDistanceTrack.StartTime).TotalSeconds / allDistanceTrack[allDistanceTrack.Count - 1].ElapsedSeconds);
            }

            // Calculate the VAM (Velocity Ascended, Meters per hour)
            // Calculate the W/kg (Relative power)
            vam = 0;
            wKg = 0;
            if (elevGain > 0)
            {
                vam = (elevGain * 60f * 60f) / record.TotalTime.TotalSeconds;
                wKg = vam / ((2 + (avgGrade * 10f)) * 100f);
            }

            ActivityInfo aiRec = ActivityInfoCache.Instance.GetInfo(record.Activity);

            stoppedTime = aiRec.TimeNotMoving;
        }
Exemplo n.º 30
0
        /// <summary>
        /// Create an distance track if one doesn't exists.
        /// </summary>
        /// <param name="activity">Activity that needs an distance track</param>
        /// <returns>If an distance track exists, return it.  Otherwise, create it from GPS</returns>
        public static INumericTimeDataSeries GetDistanceMovingTrack(IActivity activity)
        {
            ActivityInfo ai = ActivityInfoCache.Instance.GetInfo(activity);

            // Get the track from GPS data
            if (ai.MovingDistanceMetersTrack != null && ai.MovingDistanceMetersTrack.Count > 0)
            {
                // If this track was joined, there's a chance the distance data is screwy.  I'll fix it as not to worry about it later.
                INumericTimeDataSeries newSeries = new NumericTimeDataSeries();
                float lastDistance  = 0;
                float totalDistance = 0;
                float deltaDistance = 0;

                DateTime time = ai.MovingDistanceMetersTrack.StartTime;

                for (int i = 0; i < ai.MovingDistanceMetersTrack.Count; i++)
                {
                    if (i == 0)
                    {
                        totalDistance += ai.MovingDistanceMetersTrack[i].Value;
                    }
                    else
                    {
                        deltaDistance = ai.MovingDistanceMetersTrack[i].Value - lastDistance;
                        if (deltaDistance < 0)
                        {
                            // Should i set it to 0 here or to the value of the current track?
                            // I lean towards 0 since the track could really have anything in it.
                            // I don't want to take the value and it be huge due to joined activities
                            // that were parts of other activities (not starting at distance=0)
                            deltaDistance = 0;
                        }
                        totalDistance += deltaDistance;
                    }

                    newSeries.Add(time.AddSeconds(ai.MovingDistanceMetersTrack[i].ElapsedSeconds), totalDistance);
                    lastDistance = ai.MovingDistanceMetersTrack[i].Value;
                }

                // Return the distance track
                return(newSeries);
            }
            else if (activity.DistanceMetersTrack != null)
            {
                // If this track was joined, there's a chance the distance data is screwy.  I'll fix it as not to worry about it later.
                INumericTimeDataSeries newSeries = new NumericTimeDataSeries();
                float lastDistance  = 0;
                float totalDistance = 0;
                float deltaDistance = 0;

                DateTime time = activity.DistanceMetersTrack.StartTime;

                for (int i = 0; i < activity.DistanceMetersTrack.Count; i++)
                {
                    if (i == 0)
                    {
                        totalDistance += activity.DistanceMetersTrack[i].Value;
                    }
                    else
                    {
                        deltaDistance = activity.DistanceMetersTrack[i].Value - lastDistance;
                        if (deltaDistance < 0)
                        {
                            // Should i set it to 0 here or to the value of the current track?
                            // I lean towards 0 since the track could really have anything in it.
                            // I don't want to take the value and it be huge due to joined activities
                            // that were parts of other activities (not starting at distance=0)
                            deltaDistance = 0;
                        }
                        totalDistance += deltaDistance;
                    }

                    newSeries.Add(time.AddSeconds(activity.DistanceMetersTrack[i].ElapsedSeconds), totalDistance);
                    lastDistance = activity.DistanceMetersTrack[i].Value;
                }

                // Return the distance track
                return(newSeries);
            }
            else
            {
                return(null);
            }
        }