Ejemplo n.º 1
0
        private void ProcessedRiderStateEventHandler(object sender, RiderStateEventArgs e)
        {
            if (!m_dispatcher.CheckAccess()) // are we currently on the UI thread?
            {
                // We're not in the UI thread, ask the dispatcher to call this same method in the UI thread, then exit
                m_dispatcher.BeginInvoke(new ProcessedRiderStateEventHandlerDelegate(ProcessedRiderStateEventHandler), new object[] { sender, e });
                return;
            }

            lblEventCount.Text = ZAMsettings.ZPMonitorService.EventsProcessed.ToString();

            string[] row = { e.Id.ToString(), e.Power.ToString(), e.Heartrate.ToString(), DateTime.Now.ToString() };

            lvTrace.Items.Insert(0, new ListViewItem(row));

            if (lvTrace.Items.Count > 10)
            {
                lvTrace.Items.RemoveAt(10);
            }
        }
Ejemplo n.º 2
0
            public Waypoint CheckWaypointCrossings(RiderStateEventArgs e)
            {
                Waypoint searchWp;

                //Logger.LogDebug($"{this.GetType()}::CheckWaypointCrossings - Waypoints: {WaypointList.Count}");

                if (e.IsForward) // RoadTime values are increasing
                {
                    searchWp = WaypointList.Find(item => item.Course == e.Course && item.IsForward == e.IsForward && item.RoadId == e.RoadId &&
                                                 item.LastRiderRoadTime < item.RoadTime && // Last check was behind Waypoint line (values going up)
                                                 e.RoadLocation >= item.RoadTime           // Current check is at or past Waypoint line
                                                 );
                }
                else // RoadTime values are decreasing
                {
                    searchWp = WaypointList.Find(item => item.Course == e.Course && item.IsForward == e.IsForward && item.RoadId == e.RoadId &&
                                                 item.LastRiderRoadTime > item.RoadTime && // Last check was past Waypoint line (values going down)
                                                 e.RoadLocation <= item.RoadTime           // Current check is at or behind Waypoint line
                                                 );
                }

                return(searchWp);
            }
Ejemplo n.º 3
0
        /// <summary>
        /// Handle player state changes.  Calculates moving averages.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RiderStateEventHandler(object sender, RiderStateEventArgs e)
        {
            DateTime now          = DateTime.Now; // fixed current time
            bool     calculateMax = false;
            bool     triggerMax   = false;

            // AdjustedCollectionTime will be null if Monitoring but not Collecting
            if (!this.mStarted || e.AdjustedCollectionTime == null || e.IsPaused || this.mWaitingOnPauseResume)
            {
                //if (this.mDuration == 60)
                //{
                //    Debug.WriteLine($"MovingAverage not collecting - Duration: {this.mDuration}");
                //}
                return;
            }
            else
            {
                //Debug.WriteLine($"MovingAverage collecting - Duration: {this.mDuration}");
            }

            // the Statistics class captures the values we want to measure
            var stats = new Statistics(e.Power, e.Heartrate);

            if (mSampleCountAll == 0)
            {
                // Capture the current distance traveled value to use as an offset to each successive distance value.
                mDistanceSeedValue = e.Distance;
            }

            // To keep track of overall average power.  Performing here as zeros count.
            mSampleWattsSumAll += (long)stats.Power;
            mSampleCountAll    += 1;

            if (MetricsCalculatedEvent != null)
            {
                double apSampleWatts      = mSampleWattsSumAll / (double)mSampleCountAll;
                double?apSampleWattsPerKg = this.CalculateUserWattsPerKg(apSampleWatts);

                // Calculate average speed, distance is given in meters.
                double distanceKm = (e.Distance - mDistanceSeedValue) / 1000.0;
                double distanceMi = distanceKm / 1.609;
                double speedKph   = Math.Round((distanceKm / e.AdjustedCollectionTime.Value.TotalSeconds) * 3600, 1);
                double speedMph   = Math.Round((distanceMi / e.AdjustedCollectionTime.Value.TotalSeconds) * 3600, 1);
                distanceKm = Math.Round(distanceKm, 1);
                distanceMi = Math.Round(distanceMi, 1);

                OnMetricsCalculatedEvent(new MetricsCalculatedEventArgs((int)Math.Round(apSampleWatts, 0), apSampleWattsPerKg, speedKph, speedMph, e.AdjustedCollectionTime.Value, distanceKm, distanceMi));
            }

            // If power is zero and excluding values, exit
            if (mExcludeZeroPowerValues && stats.Power == 0)
            {
                return;
            }

            // Remove any queue items which are older than the set time duration
            lock (this)
            {
                while (mStatsQueue.Count > 0)
                {
                    // look at front of queue
                    var peekStats = mStatsQueue.Peek();

                    // determine time difference between the newest item and this oldest item
                    TimeSpan oldest = stats.Timestamp - peekStats.Timestamp;

                    // if queue isn't at capacity yet, exit loop
                    if (oldest.TotalSeconds <= mDuration)
                    {
                        break;
                    }

                    // subtract oldest entry from values and dequeue
                    mWattsSum -= (long)peekStats.Power;
                    mHRbpmSum -= (long)peekStats.HeartRate;
                    mStatsQueue.Dequeue();

                    calculateMax = true;  // we have a full sample, calculate maximums
                }

                // add this new item to the queue
                mStatsQueue.Enqueue(stats);
                mWattsSum += (long)stats.Power;
                mHRbpmSum += (long)stats.HeartRate;
            }

            // calculate averages
            double curAvgPower = mWattsSum / (double)mStatsQueue.Count;
            double?curAvgWkg   = this.CalculateUserWattsPerKg(curAvgPower);

            int curAvgHR = (int)Math.Round(mHRbpmSum / (double)mStatsQueue.Count, 0);

            // if queue was full, check to see if we have any new max values
            if (calculateMax)
            {
                if (curAvgPower > mAPwattsMax)
                {
                    mAPwattsMax      = curAvgPower;
                    mAPwattsPerKgMax = curAvgWkg;
                    triggerMax       = true;
                }
                if (curAvgHR > mHRbpmMax)
                {
                    mHRbpmMax  = curAvgHR;
                    triggerMax = true;
                }

                // Since buffer was full trigger an event so consumer can use this new average (without waiting for a change).  Used by NormalizedPower
                OnMovingAverageCalculatedEvent(new MovingAverageCalculatedEventArgs((int)Math.Round(curAvgPower, 0), mDurationType, e.AdjustedCollectionTime.Value));
            }

            // if either average power or average HR changed, trigger event
            if (curAvgPower != mAPwatts || curAvgHR != mHRbpm)
            {
                mAPwatts = curAvgPower;
                mHRbpm   = curAvgHR;

                // The FTP column will track the current average power until the time duration is satisfied (mAPwattsMax set).
                // This enables the rider to see what his FTP would be real-time.

                // calculate FTP watts and w/kg based on current average power
                double ftpWatts      = curAvgPower * 0.95;
                double?ftpWattsPerKg = this.CalculateUserWattsPerKg(ftpWatts);
                ftpWatts = Math.Round(ftpWatts, 0);

                bool ignoreFTP = mAPwattsMax > 0;

                OnMovingAverageChangedEvent(new MovingAverageChangedEventArgs((int)Math.Round(curAvgPower, 0), curAvgWkg, curAvgHR, mDurationType, (int)ftpWatts, ftpWattsPerKg, ignoreFTP));
            }

            // if either max average power or max HR changed, trigger event
            if (triggerMax)
            {
                // Once the time duration is satisfied, FTP will no longer use current average power, it will use the maximum average power.

                // calculate FTP watts and w/kg based on max average power
                double ftpWattsMax      = mAPwattsMax * 0.95;
                double?ftpWattsPerKgMax = this.CalculateUserWattsPerKg(ftpWattsMax);
                ftpWattsMax = Math.Round(ftpWattsMax, 0);

                OnMovingAverageMaxChangedEvent(new MovingAverageMaxChangedEventArgs((int)Math.Round(mAPwattsMax, 0), mAPwattsPerKgMax, mHRbpmMax, mDurationType, (int)ftpWattsMax, ftpWattsPerKgMax));
            }

            //Logger.LogDebug($"id: {e.PlayerState.Id} watch: {e.PlayerState.WatchingRiderId} power: {stats.Power} HR: {stats.HeartRate} Count: {m_statsQueue.Count} Sum: {m_sumTotal} Avg: {PowerAvg} Oldest: {oldest.TotalSeconds} TTP: {(DateTime.Now - start).TotalMilliseconds} WorldTime: {e.PlayerState.WorldTime} ");
            //Logger.LogDebug($"id: {e.PlayerState.Id} power: {stats.Power} HR: {stats.HeartRate} Count: {m_statsQueue.Count} PowerAvg: {curAvgPower} HRAvg: {curAvgHR} PowerMax: {m_maxAvgPower} HRMax: {m_maxAvgHR} Oldest: {oldest.TotalSeconds} TTP: {(DateTime.Now - start).TotalMilliseconds} WorldTime: {e.PlayerState.WorldTime} ");
        }
Ejemplo n.º 4
0
            public void Add(RiderStateEventArgs e)
            {
                //Logger.LogDebug($"{this.GetType()}::Add - Waypoint RoadId: {e.RoadId}, IsForward: {e.IsForward}, Course: {e.Course}, RoadLocation: {e.RoadLocation}");

                this.Add(new Waypoint(e.RoadId, e.IsForward, e.Course, e.RoadLocation));
            }
Ejemplo n.º 5
0
        /// <summary>
        /// Handle player state changes.
        /// Event distance is given in meters.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RiderStateEventHandler(object sender, RiderStateEventArgs e)
        {
            if (!m_started || !ConfiguredSplits.ShowSplits || e.CollectionTime == null)
            {
                return;
            }

            SplitV2 split = null;

            if (ConfiguredSplits.CalculateGoal)
            {
                if (m_splitCount >= ConfiguredSplits.Splits.Count)
                {
                    return;
                }

                // get the in-progress split
                split = ConfiguredSplits.Splits[m_splitCount];
            }

            DateTime now = DateTime.Now;

            TimeSpan runningTime = (now - m_startTime);
            TimeSpan splitTime   = (now - m_splitStartTime);

            if (m_eventCount++ == 0)
            {
                // Capture the current distance traveled value to use as an offset to each successive distance value.
                m_distanceSeedValue = e.Distance;
            }

            // Calculate total distance travelled
            int totalMeters = e.Distance - m_distanceSeedValue;

            double totalKmTravelled = totalMeters / 1000.0;
            double totalMiTravelled = totalKmTravelled / 1.609;

            //double totalDistance = Math.Round(m_splits.SplitsInKm ? kmsTravelled : milesTravelled, 1);

            // Calculate how deep into the current split the rider is.
            int splitMetersTravelled = totalMeters - m_lastSplitMeters;

            // How long is current split?
            int splitLengthMeters = split == null ? ConfiguredSplits.SplitDistanceAsMeters : split.SplitDistanceAsMeters;

            // How much of the split is completed (expressed as percentage)
            double splitCompletedPct = splitMetersTravelled / (double)splitLengthMeters;

            // Compute distance, leave unrounded
            double splitKmTravelled = splitMetersTravelled / 1000.0;
            double splitMiTravelled = splitKmTravelled / 1.609;

            //double splitDistance = m_splits.SplitsInKm ? splitKmTravelled : splitMiTravelled;
            //double splitSpeed = Math.Round((splitDistance / splitTime.TotalSeconds) * 3600, 1);

            double splitSpeedMph = Math.Round((splitMiTravelled / splitTime.TotalSeconds) * 3600, 1);
            double splitSpeedKph = Math.Round((splitKmTravelled / splitTime.TotalSeconds) * 3600, 1);

            // Now round the distance
            splitMiTravelled = Math.Round(splitMiTravelled, 1);
            splitKmTravelled = Math.Round(splitKmTravelled, 1);

            //splitDistance = Math.Round(splitDistance, 1);

            if (split != null)
            {
                if (splitMetersTravelled >= splitLengthMeters)
                {
                    // Calculate the deltaTime, positive number is bad, negative good.
                    TimeSpan deltaTime = new TimeSpan(0, 0, (int)Math.Round(runningTime.Subtract(split.TotalTime).TotalSeconds, 0));

                    // This completes the split.  TotalDistance travelled and Delta is included.
                    SplitEventArgs args = new SplitEventArgs(m_splitCount + 1, splitTime, splitSpeedMph, splitSpeedKph, totalMiTravelled, totalKmTravelled, runningTime, ConfiguredSplits.SplitsInKm, deltaTime);
                    OnSplitGoalCompletedEvent(args);

                    // Reset time and begin next split
                    m_splitStartTime = now;
                    m_splitCount++;

                    m_lastSplitMeters = split.TotalDistanceAsMeters;
                }
                else
                {
                    // Goal time of split start
                    TimeSpan splitStartTime = split.TotalTime.Subtract(split.SplitTime);

                    // Goal time to get to this point in the split
                    TimeSpan splitWaypointTime = splitStartTime.Add(split.SplitTime.Multiply(splitCompletedPct));

                    // Calculate the deltaTime, positive number is bad, negative good.
                    TimeSpan deltaTime = new TimeSpan(0, 0, (int)Math.Round(runningTime.Subtract(splitWaypointTime).TotalSeconds, 0));

                    // This is an update to the split in-progress.  SplitDistance travelled is included.
                    SplitEventArgs args = new SplitEventArgs(m_splitCount + 1, splitTime, splitSpeedMph, splitSpeedKph, splitMiTravelled, splitKmTravelled, runningTime, ConfiguredSplits.SplitsInKm, deltaTime);
                    OnSplitUpdatedEvent(args);

                    Logger.LogDebug($"%Complete: {splitCompletedPct} Start: {splitStartTime.ToString("m'm 's's'")} Waypoint: {splitWaypointTime.ToString("m'm 's's'")} Delta: {deltaTime.ToString("m'm 's's'")}");
                }
            }
            else
            {
                if (splitMetersTravelled >= splitLengthMeters)
                {
                    // This completes the split.  TotalDistance traveled is included.
                    SplitEventArgs args = new SplitEventArgs(m_splitCount + 1, splitTime, splitSpeedMph, splitSpeedKph, totalMiTravelled, totalKmTravelled, runningTime, ConfiguredSplits.SplitsInKm);
                    OnSplitCompletedEvent(args);

                    // Reset time and begin next split
                    m_splitStartTime = now;
                    m_splitCount++;

                    m_lastSplitMeters = totalMeters;
                }
                else
                {
                    // This is an update to the split in-progress.  SplitDistance traveled is included.
                    SplitEventArgs args = new SplitEventArgs(m_splitCount + 1, splitTime, splitSpeedMph, splitSpeedKph, splitMiTravelled, splitKmTravelled, runningTime, ConfiguredSplits.SplitsInKm);
                    OnSplitUpdatedEvent(args);
                }
            }
        }