コード例 #1
0
ファイル: MeanMaxCache.cs プロジェクト: mechgt/meanmax
        /// <summary>
        /// Adds track to cache for later recall.  Replaces cache with new
        /// data track, if track already exists in cache.
        /// </summary>
        /// <param name="track">Data track to cache</param>
        /// <param name="refId">Unique Id of track</param>
        internal static void AddTrack(INumericTimeDataSeries track, string refId)
        {
            if (tracks.ContainsKey(refId))
            {
                tracks.Remove(refId);
            }

            tracks.Add(refId, track);
        }
コード例 #2
0
 //Add a point last in the track (no check), override if point already exists
 internal static void trackAdd(INumericTimeDataSeries track, DateTime time, float val)
 {
     track.Add(time, val);
     //Check if the entry already existed, overwrite if necessary
     if (track[track.Count - 1].Value != val)
     {
         //(uint)(this.EndTime - this.StartTime.AddSeconds(-this.StartTime.Second)).TotalSeconds == m_activityDistanceMetersTrack[i - 1].ElapsedSeconds)
         track.SetValueAt(track.Count - 1, val);
     }
 }
コード例 #3
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);
        }
コード例 #4
0
        public double GetRatioVsAvgValue(List <RangeInfoCacheWrapper> allRanges)
        {
            if (allRanges.Count > 0 &&
                allRanges[0].ActivityPauselessCadenceTrack.Count > 0)
            {
                INumericTimeDataSeries activityGears = ActivityGearTrackCache.Instance.CalculateGearTrack(allRanges[0].Activity);

                return(GetAvgRatioValue(allRanges) - activityGears.Avg);
            }

            return(0);
        }
コード例 #5
0
ファイル: Utilities.cs プロジェクト: mechgt/course-score
        /// <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);
        }
コード例 #6
0
ファイル: Utilities.cs プロジェクト: mechgt/course-score
        /// <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);
        }
コード例 #7
0
ファイル: Common.cs プロジェクト: mechgt/gear-chart
        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);
        }
コード例 #8
0
ファイル: DetailPaneChart.cs プロジェクト: mechgt/gear-chart
        private bool FillSingleDataSerie(LineChartTypes chartType, ChartDataSeries dataSerie)
        {
            INumericTimeDataSeries graphPoints = GetSmoothedActivityTrack(chartType);

            if (graphPoints.Count > 0)
            {
                TimeSpan trackStartDiffWithActivity = graphPoints.StartTime - m_ActivityInfoCache.ActualTrackStart;
                float    trackSecondDifference      = (float)trackStartDiffWithActivity.TotalSeconds;

                if (XAxisReferential == XAxisValue.Time)
                {
                    graphPoints = Utils.Utils.RemovePausedTimesInTrack(graphPoints, Activity);

                    foreach (ITimeValueEntry <float> entry in graphPoints)
                    {
                        float key = trackSecondDifference + entry.ElapsedSeconds;
                        if (!dataSerie.Points.ContainsKey(key))
                        {
                            dataSerie.Points.Add(key, new PointF(trackSecondDifference + entry.ElapsedSeconds, entry.Value));
                        }
                    }
                }
                else if (m_ActivityInfoCache.MovingDistanceMetersTrack != null)
                {
                    IDistanceDataTrack distanceTrack = m_ActivityInfoCache.MovingDistanceMetersTrack;
                    int pointCount = Math.Min(distanceTrack.Count, graphPoints.Count);
                    ITimeValueEntry <float> startEntry = distanceTrack.GetInterpolatedValue(graphPoints.StartTime);

                    foreach (ITimeValueEntry <float> entry in graphPoints)
                    {
                        ITimeValueEntry <float> interpolatedEntry = distanceTrack.GetInterpolatedValue(graphPoints.StartTime + new TimeSpan(0, 0, (int)entry.ElapsedSeconds));

                        if (interpolatedEntry != null)
                        {
                            float distanceAtTime = interpolatedEntry.Value;
                            float distanceValue  = (float)Length.Convert(distanceAtTime, Length.Units.Meter, Activity.Category.DistanceUnits);

                            float key = trackSecondDifference + entry.ElapsedSeconds;
                            if (!dataSerie.Points.ContainsKey(key))
                            {
                                dataSerie.Points.Add(key, new PointF(distanceValue, entry.Value));
                            }
                        }
                    }
                }

                return(true);
            }

            return(false);
        }
コード例 #9
0
        private void UpdateCachedGearTrack(IActivity activity, INumericTimeDataSeries gearTrack)
        {
            ActivityGearTrackCacheItem newCacheItem = new ActivityGearTrackCacheItem(activity);

            if (!m_InfoCache.ContainsKey(activity))
            {
                m_InfoCache.Add(activity, null);

                activity.PropertyChanged += new PropertyChangedEventHandler(OnActivityDataChanged);
            }

            m_InfoCache[activity]             = newCacheItem;
            m_InfoCache[activity].m_GearTrack = gearTrack;
        }
コード例 #10
0
ファイル: Common.cs プロジェクト: mechgt/gear-chart
 /// <summary>
 /// Determines whether or not a series contains a particular time entry
 /// </summary>
 /// <param name="series"></param>
 /// <param name="time"></param>
 /// <returns></returns>
 public static bool ContainsTime(INumericTimeDataSeries series, DateTime time)
 {
     if (series != null &&
         series.Count > 0 &&
         series.StartTime <= time &&
         series.StartTime.AddSeconds(series.TotalElapsedSeconds) >= time)
     {
         return(true);
     }
     else
     {
         return(false);
     }
 }
コード例 #11
0
        /// <summary>
        /// Add 'track' to 'graph' and apply labels based on 'chartType'
        /// </summary>
        /// <param name="track">Data track</param>
        /// <param name="graph">Which graph to stick the data on</param>
        /// <param name="chartType">This determines the labeling, coloring, etc. (all appearance related)</param>
        internal static void updateZedGraph(INumericTimeDataSeries track, ZedGraphControl graph, Common.ChartBasis chartType)
        {
            GraphPane myPane = graph.GraphPane;

            myPane.XAxis.Title.Text = CommonResources.Text.LabelTime;
            myPane.XAxis.Type       = AxisType.Log;

            Color mainCurveColor = Color.FromArgb(204, 0, 0);

            switch (chartType)
            {
            case Common.ChartBasis.Cadence:
                mainCurveColor          = Common.ColorCadence;
                myPane.YAxis.Title.Text = CommonResources.Text.LabelCadence;
                break;

            case Common.ChartBasis.HR:
                mainCurveColor          = Common.ColorHR;
                myPane.YAxis.Title.Text = CommonResources.Text.LabelHeartRate + " " + CommonResources.Text.LabelBPM;
                break;

            case Common.ChartBasis.Power:
                mainCurveColor          = Common.ColorPower;
                myPane.YAxis.Title.Text = CommonResources.Text.LabelPower;
                break;
            }

            myPane.XAxis.MinorTic.IsOutside = true;

            PointPairList zedTrack = new PointPairList();

            foreach (ITimeValueEntry <float> item in track)
            {
                zedTrack.Add(item.ElapsedSeconds, item.Value);
            }

            myPane.CurveList.Clear();
            LineItem curve = myPane.AddCurve("Curve Label", zedTrack, mainCurveColor, SymbolType.None);

            curve.Line.Width      = 2f;
            curve.Line.Fill.Type  = FillType.Solid;
            curve.Line.Fill.Color = Color.FromArgb(50, mainCurveColor);
            myPane.YAxis.Title.FontSpec.FontColor = mainCurveColor;
            myPane.YAxis.Scale.FontSpec.FontColor = mainCurveColor;

            graph.AxisChange();
            graph.Refresh();
        }
コード例 #12
0
ファイル: Utilities.cs プロジェクト: mechgt/course-score
        /// <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);
        }
コード例 #13
0
        public static float GetSyncGraphOffset(INumericTimeDataSeries graphPoints, INumericTimeDataSeries refGraphPoints, SyncGraphMode syncGraph)
        {
            float syncGraphOffset = 0;

            if (graphPoints != refGraphPoints &&
                refGraphPoints != null && refGraphPoints.Count > 1 &&
                graphPoints != null && graphPoints.Count > 1)
            {
                switch (syncGraph)
                {
                case SyncGraphMode.None:
                    break;

                case SyncGraphMode.Start:
                    syncGraphOffset = refGraphPoints[0].Value - graphPoints[0].Value;
                    break;

                case SyncGraphMode.End:
                    syncGraphOffset = refGraphPoints[refGraphPoints.Count - 1].Value - graphPoints[graphPoints.Count - 1].Value;
                    break;

                case SyncGraphMode.Average:
                    syncGraphOffset = refGraphPoints.Avg - graphPoints.Avg;
                    break;

                case SyncGraphMode.Min:
                    syncGraphOffset = refGraphPoints.Min - graphPoints.Min;
                    break;

                case SyncGraphMode.Max:
                    syncGraphOffset = refGraphPoints.Max - graphPoints.Max;
                    break;

                default:
                {
                    Debug.Assert(false, string.Format("Unexpecteded SyncGraphMode {0}", syncGraph));
                    break;
                }
                }
                if (float.IsNaN(syncGraphOffset) || float.IsInfinity(syncGraphOffset))
                {
                    syncGraphOffset = 0;
                }
            }
            return(syncGraphOffset);
        }
コード例 #14
0
ファイル: Utilities.cs プロジェクト: mechgt/course-score
        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);
        }
コード例 #15
0
        /// <summary>
        /// Populate a dataseries with data from a NumericTimeDataSeries
        /// </summary>
        /// <param name="timeDataSeries">NumericTimeDataSeries containing data to add to chart data series</param>
        /// <param name="chartDataSeries">Dataseries to add data to</param>
        /// <param name="chartType">Chart basis (time or distance)</param>
        /// <returns>Populated dataseries</returns>
        private static ChartDataSeries PopulateDataSeries(INumericTimeDataSeries timeDataSeries, ChartDataSeries chartDataSeries, ChartBasis chartType, IActivity activity)
        {
            switch (chartType)
            {
            case ChartBasis.Gear_Distance:
                ActivityInfo       info  = ActivityInfoCache.Instance.GetInfo(activity);
                IDistanceDataTrack track = GearUtils.GetDistanceTrack(activity);
                float test = 0;

                foreach (ITimeValueEntry <float> entry in timeDataSeries)
                {
                    DateTime time     = track.EntryDateTime(entry);
                    float    distance = track.GetInterpolatedValue(time).Value;
                    distance = (float)Length.Convert(distance, Length.Units.Meter, activity.Category.DistanceUnits);
                    PointF point = new PointF(distance, entry.Value);
                    if (!chartDataSeries.Points.ContainsKey(point.X))
                    {
                        chartDataSeries.Points.Add(point.X, point);
                    }
                    else if (test != point.X)
                    {
                        test = point.X;
                    }
                }

                break;

            case ChartBasis.Gear_Time:
                foreach (ITimeValueEntry <float> entry in timeDataSeries)
                {
                    PointF point = new PointF(entry.ElapsedSeconds, entry.Value);
                    chartDataSeries.Points.Add(point.X, point);
                }

                break;

            default:

                break;
            }

            return(chartDataSeries);
        }
コード例 #16
0
ファイル: Data.cs プロジェクト: mechgt/gear-chart
        /// <summary>
        /// Gets the Sprocket Track for a given activity.  Returns an empty track if unable to calculate (for instance if no GPS or Distance track).
        /// </summary>
        /// <param name="activity"></param>
        /// <returns>Returns an ITimeDataSeries<SprocketCombo> of the sprocket combo used, or an empty track if nothing can be calculated.</returns>
        public static ITimeDataSeries <SprocketCombo> GetSprocketTrack(IActivity activity)
        {
            // Raw data
            INumericTimeDataSeries          gearTrack     = ActivityGearTrackCache.Instance.CalculateRawTrack(activity);
            ITimeDataSeries <SprocketCombo> sprocketTrack = new TimeDataSeries <SprocketCombo>();

            if (gearTrack.Count > 0)
            {
                // Smooth data track
                float min, max;
                gearTrack = ZoneFiveSoftware.Common.Data.Algorithm.NumericTimeDataSeries.Smooth(gearTrack, Constants.GearTrackSmoothing, out max, out min);

                // Estimate/round gear track
                string id = Options.Instance.GetGearEquipmentId(activity);
                List <SprocketCombo> sprockets = Options.Instance.GetSprocketCombos(id);
                sprocketTrack = GearUtils.GuessSprockets(gearTrack, sprockets);
            }

            return(sprocketTrack);
        }
コード例 #17
0
ファイル: Utilities.cs プロジェクト: mechgt/course-score
 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);
     }
 }
コード例 #18
0
        private void ExportButton_Click(object sender, EventArgs e)
        {
            // TODO: How to export multiple activities...?
            IActivity activity = ZoneFiveSoftware.Common.Visuals.Util.CollectionUtils.GetFirstItemOfType <IActivity>(activities);

            // Nothing to export if activity is empty
            if (activity == null)
            {
                return;
            }

            // Open File Save dialog to create new CSV Document
            SaveFileDialog saveFile = new SaveFileDialog();

            saveFile.FileName        = "MeanMax " + activity.StartTime.ToLocalTime().ToString("yyyy-MM-dd");
            saveFile.Filter          = "All Files (*.*)|*.*|Comma Separated Values (*.csv)|*.csv";
            saveFile.FilterIndex     = 2;
            saveFile.DefaultExt      = "csv";
            saveFile.OverwritePrompt = true;

            string comma = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator;

            // Cancel if user doesn't select a file
            if (saveFile.ShowDialog() != DialogResult.OK)
            {
                return;
            }

            // Export mean-max data
            // TODO: Export related data as well as MM data
            INumericTimeDataSeries timeTrack;
            INumericTimeDataSeries track = MeanMaxCache.GetMeanMaxTrack(activity, ChartType, out timeTrack);

            //Dictionary<INumericTimeDataSeries, string> tracks = new Dictionary<INumericTimeDataSeries, string>();

            //tracks.Add(track, GlobalSettings.Instance.PrimaryChart.ToString());
            //tracks.Add(timeTrack, CommonResources.Text.LabelTime);

            //Utilities.ExportTrack(tracks, saveFile.FileName);
            Utilities.ExportTrack(track, saveFile.FileName);
        }
コード例 #19
0
ファイル: Common.cs プロジェクト: mechgt/gear-chart
        /// <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);
        }
コード例 #20
0
        internal static float getValFromDateTime(INumericTimeDataSeries track, DateTime t, out int status)
        {
            //Ignore malformed activities and selection outside the result
            float res = 0;

            status = 1;
            ITimeValueEntry <float> entry = track.GetInterpolatedValue(t);

            if (entry != null)
            {
                res    = entry.Value;
                status = 0;
            }
            else if (track.Count > 0 && t >= track.StartTime.AddSeconds(2))
            {
                //Seem to be out of bounds
                //Any time after start is handled as end, as pauses may complicate calcs (default is 0, i.e. before)
                res = track[track.Count - 1].Value;
            }
            return(res);
        }
コード例 #21
0
ファイル: Utilities.cs プロジェクト: mechgt/course-score
        /// <summary>
        /// Temporary output routine.  Eventually should be deleted.
        /// </summary>
        /// <param name="track"></param>
        internal static void ExportTrack(INumericTimeDataSeries track, string name)
        {
            try
            {
                System.IO.StreamWriter writer = new System.IO.StreamWriter(name);

                // Write Header
                writer.WriteLine("Seconds, Value");

                foreach (ITimeValueEntry <float> item in track)
                {
                    // Write data
                    writer.WriteLine(item.ElapsedSeconds + ", " + item.Value);
                }

                writer.Close();
            }
            catch
            {
            }
        }
コード例 #22
0
        public void RefreshPage()
        {
            if (Activities == null)
            {
                // Nothing to do.  Exit.
                return;
            }

            // Update display
            bnrReport.Text = MeanMax.Resources.Strings.Label_MeanMax + ": " + Common.GetText(GlobalSettings.Instance.ReportChart);

            // Track list: One track per selected line.
            Dictionary <CriticalLineDefinition, INumericTimeDataSeries> trackList = new Dictionary <CriticalLineDefinition, INumericTimeDataSeries>();
            List <Color> colors = Utilities.Rainbow(GlobalSettings.Instance.SelectedCriticalLines.Count, 255);

            progressLine = 0;

            foreach (CriticalLineDefinition line in GlobalSettings.Instance.CriticalPowerLines)
            {
                if (line.Selected)
                {
                    // Assign rainbowed line color
                    line.LineColor = colors[progressLine++];

                    // Get critical track described by this line from all Activities
                    INumericTimeDataSeries criticalTrack = MeanMaxCache.GetCriticalTrack(Activities,
                                                                                         GlobalSettings.Instance.ReportChart,
                                                                                         line.Seconds);
                    // Add track to list
                    trackList.Add(line, criticalTrack);
                }
            }

            // Display tracks on graph
            updateZedGraph(trackList, zedChart, GlobalSettings.Instance.ReportChart);

            ZoomChartButton_Click(null, null);
        }
コード例 #23
0
ファイル: Data.cs プロジェクト: mechgt/gear-chart
        public static INumericTimeDataSeries GetGearTrack(IActivity activity, INumericTimeDataSeries cadenceTrack, INumericTimeDataSeries distanceTrack)
        {
            // Raw data
            INumericTimeDataSeries gearTrack = GearUtils.GetRawGearTrack(cadenceTrack, distanceTrack);

            if (gearTrack.Count > 0)
            {
                // Smooth data track
                float min, max;
                gearTrack = ZoneFiveSoftware.Common.Data.Algorithm.NumericTimeDataSeries.Smooth(gearTrack, Constants.GearTrackSmoothing, out max, out min);

                // Estimate/round gear track
                string id = Options.Instance.GetGearEquipmentId(activity);
                List <SprocketCombo> sprockets = Options.Instance.GetSprocketCombos(id);
                gearTrack = GearUtils.GuessGears(gearTrack, sprockets);

                return(gearTrack);
            }
            else
            {
                return(new NumericTimeDataSeries());
            }
        }
コード例 #24
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);
        }
コード例 #25
0
ファイル: Utilities.cs プロジェクト: mechgt/course-score
 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);
     }
 }
コード例 #26
0
ファイル: Common.cs プロジェクト: mechgt/gear-chart
        /// <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);
        }
コード例 #27
0
ファイル: DetailPaneChart.cs プロジェクト: mechgt/gear-chart
        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);
        }
コード例 #28
0
        public static void ExtractRangeFromDataTrack(INumericTimeDataSeries sourceTrack, ValueRange <DateTime> timeToKeep, INumericTimeDataSeries resultTrack)
        {
            resultTrack.Clear();

            if (sourceTrack != null && sourceTrack.Count > 0)
            {
                TimeSpan endElapsedTime      = timeToKeep.Upper - sourceTrack.StartTime;
                TimeSpan totalElapsedTime    = timeToKeep.Upper - timeToKeep.Lower;
                int      endElapsedSeconds   = (int)endElapsedTime.TotalSeconds;
                int      totalElapsedSeconds = (int)totalElapsedTime.TotalSeconds;
                DateTime timeStart           = timeToKeep.Lower;
                DateTime timeEnd             = timeToKeep.Upper;
                DateTime sourceDataEndTime   = sourceTrack.StartTime + new TimeSpan(0, 0, (int)sourceTrack.TotalElapsedSeconds);

                // Readjust to match the track since it can start after the activity start time
                //  or end before the end time
                if (timeStart < sourceTrack.StartTime)
                {
                    timeStart = sourceTrack.StartTime;
                }
                else if (timeStart > sourceDataEndTime)
                {
                    timeStart = sourceDataEndTime;
                }

                if (timeEnd < sourceTrack.StartTime)
                {
                    timeEnd = sourceTrack.StartTime;
                }
                else if (timeEnd > sourceDataEndTime)
                {
                    timeEnd = sourceDataEndTime;
                }

                ITimeValueEntry <float> startDistance = sourceTrack.GetInterpolatedValue(timeStart);
                int currentItemIndex = sourceTrack.IndexOf(startDistance);

                // Since we use an interpolated value, it is possible that there is no matching
                //  index, in which case we add it so we can have a start point in the array
                if (currentItemIndex == -1)
                {
                    sourceTrack.Add(timeStart, startDistance.Value);
                    startDistance    = sourceTrack.GetInterpolatedValue(timeStart);
                    currentItemIndex = sourceTrack.IndexOf(startDistance);
                }

                // Just make sure we have a start index.  Can be == -1 if there is no track?
                if (currentItemIndex != -1)
                {
                    // Now go through all indexes until we hit the end
                    while (currentItemIndex < sourceTrack.Count && sourceTrack[currentItemIndex].ElapsedSeconds <= endElapsedSeconds)
                    {
                        ITimeValueEntry <float> currentDistance = sourceTrack[currentItemIndex];
                        DateTime currentTime = timeStart + new TimeSpan(0, 0, (int)(currentDistance.ElapsedSeconds - startDistance.ElapsedSeconds));

                        resultTrack.Add(currentTime, currentDistance.Value);

                        ++currentItemIndex;
                    }
                }

                // Since we check the elapsed seconds, it is possible that our end point needs to
                //  be interpolated.  Let's make sure we have our end point in our cached data
                ITimeValueEntry <float> lastEntry = sourceTrack.GetInterpolatedValue(timeEnd);

                // Should never be empty, we at least added the start above...
                if (resultTrack.Count != 0 && lastEntry != null)
                {
                    if (resultTrack[resultTrack.Count - 1].ElapsedSeconds != totalElapsedSeconds)
                    {
                        // Add our final interpolated point
                        resultTrack.Add(timeEnd, lastEntry.Value);
                    }
                }
            }
        }
コード例 #29
0
        /**********************************/

        internal static float getValFromDateTime(INumericTimeDataSeries track, DateTime t)
        {
            int status;

            return(getValFromDateTime(track, t, out status));
        }
コード例 #30
0
ファイル: MeanMaxCache.cs プロジェクト: mechgt/meanmax
        /// <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);
            }
        }