/// <summary> /// Constructor to cloan a MatchingFeature /// </summary> /// <param name="inMatchingFeature">MatchingFeature to clone</param> public MatchingFeature(MatchingFeature inMatchingFeature) { this.totalDistanceMeters = inMatchingFeature.totalDistanceMeters; this.avgGrade = inMatchingFeature.avgGrade; this.totalTime = inMatchingFeature.totalTime; this.startPoint = inMatchingFeature.startPoint; this.endPoint = inMatchingFeature.endPoint; this.hillScoreClimbByBike = inMatchingFeature.hillScoreClimbByBike; this.hillScoreCycle2Max = inMatchingFeature.hillScoreCycle2Max; this.hillScoreFiets = inMatchingFeature.hillScoreFiets; this.hillScoreCourseScoreRunning = inMatchingFeature.hillScoreCourseScoreRunning; this.hillScoreCourseScoreCycling = inMatchingFeature.hillScoreCourseScoreCycling; this.id = inMatchingFeature.id; this.activityFact = inMatchingFeature.activityFact; this.featureStartTime = inMatchingFeature.featureStartTime; }
public static float DistanceMetersToPointGpsSimple(IGPSPoint point, IGPSPoint point2) { #if NO_SIMPLE_DISTANCE return(point.DistanceMetersToPoint(point2)); #else float _cosmean = (float)Math.Cos(point2.LatitudeDegrees * DegToRad); float dlat = point.LatitudeDegrees - point2.LatitudeDegrees; float dlon = point.LongitudeDegrees - point2.LongitudeDegrees; float result = dlat * dlat + dlon * dlon * _cosmean; #if SQUARE_DISTANCE return(result); #else return(EarthRadius * DegToRad * (float)Math.Sqrt(result)); #endif #endif }
static public TrailResultInfo ResultInfoFromSelection(IActivity activity, ZoneFiveSoftware.Common.Visuals.Fitness.IItemTrackSelectionInfo selInfo) { TrailResultInfo indexes = new TrailResultInfo(activity, false); DateTime time = selInfo.MarkedTimes[0].Lower; IGPSPoint p = Utils.TrackUtil.getGpsLoc(activity, time); if (p != null) { indexes.Points.Add(new TrailResultPoint(new TrailGPSLocation(p), time)); } time = selInfo.MarkedTimes[0].Upper; p = Utils.TrackUtil.getGpsLoc(activity, time); if (p != null) { indexes.Points.Add(new TrailResultPoint(new TrailGPSLocation(p), time)); } return(indexes); }
//Note: Faster to keep this static then public static float DistanceMetersToPointSimple(TrailGPSLocation trailp, IGPSPoint point) { #if NO_SIMPLE_DISTANCE return(trailp.DistanceMetersToPoint(point)); #else //Use the trailp lat instead of average lat if (trailp._cosmean == invalidLatLon) { trailp._cosmean = (float)Math.Cos(trailp.latitudeDegrees * DegToRad); } float dlat = point.LatitudeDegrees - trailp.latitudeDegrees; float dlon = point.LongitudeDegrees - trailp.longitudeDegrees; float result = dlat * dlat + dlon * dlon * trailp._cosmean; #if SQUARE_DISTANCE return(result); #else return(EarthRadius * DegToRad * (float)Math.Sqrt(result)); #endif #endif }
public void Draw(IMapDrawContext drawContext) { if (m_CaptureSelectedGPSLocations) { m_CaptureSelectedGPSLocations = false; if (m_SelectedGPSLocations.Count != MapControl.Selected.Count) { m_SelectedGPSLocations = getSelectedGPSLocations(drawContext); SelectedGPSLocationsChanged(this, new System.EventArgs()); } } if (_showPage) { //drawContext.Center IGPSLocation loc1 = drawContext.Projection.PixelToGPS(drawContext.Center, drawContext.ZoomLevel, new Point(0, 0)); IGPSLocation loc2 = drawContext.Projection.PixelToGPS(drawContext.Center, drawContext.ZoomLevel, new Point(0, 100)); IGPSPoint point1 = Utils.GPS.LocationToPoint(loc1); IGPSPoint point2 = Utils.GPS.LocationToPoint(loc2); float meters = point1.DistanceMetersToPoint(point2) / 100; float radiusInPixels = m_highlightRadius / meters; foreach (TrailGPSLocation gpsLocation in m_TrailPoints) { Point point = drawContext.Projection.GPSToPixel(drawContext.Center, drawContext.ZoomLevel, gpsLocation.GpsLocation); Pen pen = new Pen(Color.Red, 5.0F); drawContext.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; float X = point.X + (drawContext.DrawRectangle.Width / 2) - radiusInPixels; float Y = point.Y + (drawContext.DrawRectangle.Height / 2) - radiusInPixels; if (X > 0 && X < 1000 && Y > 0 && Y < 1000) { //Prevent crashes at large elipses drawContext.Graphics.DrawEllipse(pen, X, Y, radiusInPixels * 2, radiusInPixels * 2); } } } }
//Get the data in a generic Globalsat format, to separate ST public static IList <GhPacketBase.Train> ToGlobTrack(GlobalsatProtocol device, IList <IActivity> activities) { IList <GhPacketBase.Train> result = new List <GhPacketBase.Train>(); foreach (IActivity activity in activities) { IGPSRoute gpsRoute = activity.GPSRoute; GhPacketBase.Train train = new GhPacketBase.Train(); train.StartTime = activity.StartTime; train.TotalTime = TimeSpan.FromSeconds(gpsRoute.TotalElapsedSeconds); train.TotalDistanceMeters = (Int32)Math.Round(gpsRoute.TotalDistanceMeters); train.LapCount = 1; train.TotalCalories = (Int16)activity.TotalCalories; if (train.MaximumSpeed == 0 && train.TotalTime.TotalSeconds >= 1) { //Better than 0(?) - Info() could be used train.MaximumSpeed = train.TotalDistanceMeters / train.TotalTime.TotalSeconds; } train.MaximumHeartRate = (byte)activity.MaximumHeartRatePerMinuteEntered; train.AverageHeartRate = (byte)activity.AverageHeartRatePerMinuteEntered; train.TotalAscend = (Int16)activity.TotalAscendMetersEntered; train.TotalDescend = (Int16)activity.TotalDescendMetersEntered; train.AverageCadence = (Int16)activity.AverageCadencePerMinuteEntered; train.MaximumCadence = (Int16)activity.MaximumCadencePerMinuteEntered; train.AveragePower = (Int16)activity.AveragePowerWattsEntered; train.MaximumPower = (Int16)activity.MaximumPowerWattsEntered; //Some protocols send laps in separate header //Use common code instead of overiding this method //Laps are not implemented when sending, one lap hardcoded for (int j = 0; j < gpsRoute.Count; j++) { IGPSPoint point = gpsRoute[j].Value; GhPacketBase.TrackPoint trackpoint = new GhPacketBase.TrackPoint(); trackpoint.Latitude = point.LatitudeDegrees; trackpoint.Longitude = point.LongitudeDegrees; trackpoint.Altitude = (Int32)point.ElevationMeters; if (j == 0) { trackpoint.IntervalTime = 0; } else { #if ST_2_1 int intTime = gpsRoute[j].ElapsedSeconds - gpsRoute[j - 1].ElapsedSeconds; #else uint intTime = gpsRoute[j].ElapsedSeconds - gpsRoute[j - 1].ElapsedSeconds; #endif float dist = gpsRoute[j].Value.DistanceMetersToPoint(gpsRoute[j - 1].Value); if (intTime > 0) { trackpoint.IntervalTime = intTime; trackpoint.Speed = dist / intTime; } else { //Time is not really used - could probably be empty //The alternative would be to drop the points trackpoint.IntervalTime = 1; } } train.TrackPoints.Add(trackpoint); } train.TrackPointCount = (short)train.TrackPoints.Count; result.Add(train); } return(result); }
public static TrailResultInfo TrailResultInfoFromSplits(IActivity activity, bool onlyActiveLaps) { TrailResultInfo results = new TrailResultInfo(activity, false); if (activity == null) { //summary result return(results); } //Get around a problem with only Rest laps if (onlyActiveLaps) { onlyActiveLaps = false; for (int j = 0; j < activity.Laps.Count; j++) { if (!activity.Laps[j].Rest) { onlyActiveLaps = true; break; } } } bool lastIsRestlap = false; if (null == activity.Laps || 0 == activity.Laps.Count) { //Use MovingDistanceMetersTrack rather than ActualDistanceMetersTrack, assume similar activities have similar pauses/slow parts IDistanceDataTrack track = ActivityInfoCache.Instance.GetInfo(activity).MovingDistanceMetersTrack; if (track != null && track.Max > 0) { //Create some kind of points - could be dependent on length const float cDist = 1000; float dist = 0; while (dist < track.Max) { DateTime time = track.GetTimeAtDistanceMeters(dist); IGPSPoint p = Utils.TrackUtil.getGpsLoc(activity, time); if (p != null) { results.Points.Add(new TrailResultPoint(new TrailGPSLocation(p), time)); } else { if (activity.GPSRoute == null || activity.GPSRoute.Count == 0) { results.Points.Add(new TrailResultPoint(new TrailGPSLocation(activity.Name, true), time, track.TotalElapsedSeconds)); } else { System.Diagnostics.Debug.Assert(false, "out of GPS"); if (results.Points.Count > 0) { //end insert break; } } } dist = Math.Min(track.Max, dist + cDist); } } else { DateTime time = ActivityInfoCache.Instance.GetInfo(activity).ActualTrackStart; IGPSPoint p = Utils.TrackUtil.getGpsLoc(activity, time); if (p != null) { results.Points.Add(new TrailResultPoint(new TrailGPSLocation(p), time)); } } } else { int subresultIndex = 1; for (int j = 0; j < activity.Laps.Count; j++) { ILapInfo l = activity.Laps[j]; if (!onlyActiveLaps || !l.Rest || j > 0 && !activity.Laps[j - 1].Rest) { string name = l.Notes; if (string.IsNullOrEmpty(name)) { name = "#" + (results.Points.Count + 1); } DateTime d = l.StartTime; if (activity.GPSRoute == null || activity.GPSRoute.Count == 0) { results.Points.Add(new TrailResultPoint(new TrailGPSLocation(name, !l.Rest), d, l.TotalTime, l)); } else { IGPSPoint t = Utils.TrackUtil.getGpsLoc(activity, d); if (t != null) { results.Points.Add(new TrailResultPoint(new TrailGPSLocation(t, name, !l.Rest), d, l.TotalTime, l)); } } if (//All swim related have at least one PoolLength for each lap l.PoolLengths != null && (l.PoolLengths.Count > 0)) { TrailResultPoint tp = results.Points[results.Points.Count - 1]; foreach (IPoolLengthInfo p in l.PoolLengths) { DateTime d2 = p.StartTime; IPoolLengthInfo p1 = PoolLengthInfo.GetPoolLength(p); tp.SubPoints.Add(new TrailResultPoint(new TrailGPSLocation(null, !l.Rest), d2, p.TotalTime, p1, subresultIndex++)); } //Need (dummy) last point IPoolLengthInfo p2 = tp.SubPoints[tp.SubPoints.Count - 1].PoolLengthInfo; tp.SubPoints.Add(new TrailResultPoint(new TrailGPSLocation(null, !l.Rest), p2.StartTime + p2.TotalTime, TimeSpan.Zero, p2, subresultIndex)); } } } lastIsRestlap = activity.Laps[activity.Laps.Count - 1].Rest; } //Add end point, except if last is a rest lap (where last already is added) if (!onlyActiveLaps || !lastIsRestlap) { DateTime d = ActivityInfoCache.Instance.GetInfo(activity).ActualTrackEnd; if (activity.GPSRoute == null || activity.GPSRoute.Count == 0) { results.Points.Add(new TrailResultPoint(new TrailGPSLocation(activity.Name, !lastIsRestlap), d)); } else { IGPSPoint t = Utils.TrackUtil.getGpsLoc(activity, d); if (t != null) { results.Points.Add(new TrailResultPoint(new TrailGPSLocation(t, activity.Name, !lastIsRestlap), d)); } } } //Special for activities without any GPS info if (results.Count == 0 && activity.HasStartTime) { results.Points.Add(new TrailResultPoint(new TrailGPSLocation(activity.Name, true), activity.StartTime)); results.Points.Add(new TrailResultPoint(new TrailGPSLocation(activity.Name, true), activity.StartTime + activity.TotalTimeEntered)); } //A trail created from splits should not define elevation points foreach (TrailGPSLocation t in results.Points) { t.SetElevation(float.NaN); } return(results); }
//Get a close enough point, not necessarily the closest or best match public int getClosePoint(IGPSPoint point) { IList<int> result = new List<int>(); int x = (int)Math.Floor(point.LongitudeDegrees / m_lngWidth); int y = (int)Math.Floor(point.LatitudeDegrees / m_latWidth); foreach (int i in new int[] { x, x - 1, x + 1 }) { if (m_Grid.ContainsKey(i)) { foreach (int j in new int[] { y, y - 1, y + 1 }) { if (m_Grid[i].ContainsKey(j)) { foreach (int p in m_Grid[i][j]) { IGPSPoint pointInGrid = m_Route[p].Value; double diffDist = point.DistanceMetersToPoint(pointInGrid); if (diffDist < m_Distance) { return p; } } } } } } return -1; }
//Get all points close to the asking point, but merge in "stretches" to let caller find best match public IList<IndexDiffDist> getAllCloseStretch(IGPSPoint point) { const int boxsize = 2; IList<IndexDiffDist> result = new List<IndexDiffDist>(); int x = (int)Math.Floor(point.LongitudeDegrees / m_lngWidth); int y = (int)Math.Floor(point.LatitudeDegrees / m_latWidth); for (int i = x - boxsize; i <= x + boxsize; i++) { if (m_Grid.ContainsKey(i)) { for (int j = y - boxsize; j <= y + boxsize; j++) { if (m_Grid[i].ContainsKey(j)) { foreach (int p in m_Grid[i][j]) { IGPSPoint pointInGrid = m_Route[p].Value; double diffDist = point.DistanceMetersToPoint(pointInGrid); if (diffDist < m_Distance) { double totDist = double.MaxValue; if (null != m_Dist) { totDist = m_Dist[p].Value; } IndexDiffDist t = new IndexDiffDist(p, p, p, diffDist, totDist); result.Add(t); } } } } } } //Group the results with local minimums in "stretches" //Only directly adjacent stretches are merged now, so single points out of the box will split stretches if (result.Count > 0) { for (int i = 0; i < result.Count; i++) { for (int j = 0; j < result.Count; j++) { if (i != j && result[i].Index > -1 && result[j].Index > -1) { int k = Math.Min(i, j); int l = Math.Max(i, j); //Try merge with lower if ((result[k].high + 1 >= result[l].low && result[k].high <= result[l].high) || Math.Abs(result[k].Dist - result[l].Dist) < m_Distance) { int tmp; if (result[k].Diff < result[l].Diff) { //Use lower also at equal tmp = k; result[l].Index = -1; } else { tmp = l; result[k].Index = -1; } result[tmp].low = Math.Min(result[i].low, result[j].low); result[tmp].high = Math.Max(result[i].high, result[j].high); } } } } for (int i = result.Count-1; i >= 0; i--) { if (result[i].Index == -1) { result.RemoveAt(i); } } } return result; }
//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; }
protected void AddActivities(IImportResults importResults, IList <GlobalsatPacket.Train> trains, bool importSpeedTrackAsDistance, int detectPauses, int verbose) { foreach (GlobalsatPacket.Train train in trains) { DateTime pointTime = train.StartTime; IActivity activity = importResults.AddActivity(pointTime); activity.Metadata.Source = string.Format(CommonResources.Text.Devices.ImportJob_ActivityImportSource, sourceDescription); activity.TotalTimeEntered = train.TotalTime; activity.TotalDistanceMetersEntered = train.TotalDistanceMeters; activity.TotalCalories = train.TotalCalories; activity.MaximumHeartRatePerMinuteEntered = train.MaximumHeartRate; activity.AverageHeartRatePerMinuteEntered = train.AverageHeartRate; activity.MaximumCadencePerMinuteEntered = train.MaximumCadence; activity.AverageCadencePerMinuteEntered = train.AverageCadence; activity.MaximumPowerWattsEntered = train.MaximumPower; activity.AveragePowerWattsEntered = train.AveragePower; activity.TotalAscendMetersEntered = train.TotalAscend; activity.TotalDescendMetersEntered = -train.TotalDescend; activity.Notes += train.Comment; bool foundGPSPoint = false; bool foundCadencePoint = false; bool foundPowerPoint = false; activity.GPSRoute = new GPSRoute(); activity.HeartRatePerMinuteTrack = new NumericTimeDataSeries(); activity.CadencePerMinuteTrack = new NumericTimeDataSeries(); activity.PowerWattsTrack = new NumericTimeDataSeries(); activity.TemperatureCelsiusTrack = new NumericTimeDataSeries(); activity.ElevationMetersTrack = new NumericTimeDataSeries(); activity.DistanceMetersTrack = new DistanceDataTrack(); float pointDist = 0; double pointElapsed = 0; //As interval to first is not zero, add point activity.DistanceMetersTrack.Add(pointTime, pointDist); //Fix for (GB-580 only?) recording problem with interval 10 or larger (in fw before 2012-09) double?fixInterval = null; if (train.TrackPoints.Count > 1) { //All points except last has 0.1s interval double testIntervall = (train.TotalTime.TotalSeconds - train.TrackPoints[train.TrackPoints.Count - 1].IntervalTime) / (train.TrackPointCount - 1 - 1); if (testIntervall > 9.6) { fixInterval = testIntervall; } } DateTime pointTimePrev = System.DateTime.Now; DateTime pointTimeNext = System.DateTime.Now; bool insertPauseFirst = false; bool insertPauseLast = false; foreach (GhPacketBase.TrackPoint point in train.TrackPoints) { double time = point.IntervalTime; if (time < 0.11 && fixInterval != null) { time = (double)fixInterval; } pointElapsed += time; //Note: There is an intervall time to the first point, also if TGP says it is 0 pointTime = pointTime.AddSeconds(time); float dist = (float)(point.Speed * time); pointDist += dist; // TODO: How are GPS points indicated in indoor activities? //It seems like all are the same IGPSPoint gpsPoint = new GPSPoint((float)point.Latitude, (float)point.Longitude, point.Altitude); //There are no pause markers in the Globalsat protocol //Insert pauses when estimated/listed distance differs "too much" //Guess pauses - no info of real pause, but this can at least be marked in the track //Share setting with global split if (foundGPSPoint && activity.GPSRoute.Count > 0) { //estimated time for the pause double estimatedSec = 0; string info = ""; //speed & cadence method if (detectPauses == 2 && !float.IsNaN(point.Cadence)) { if (activity.GPSRoute.Count > 0) { float gpsDist = gpsPoint.DistanceMetersToPoint(activity.GPSRoute[activity.GPSRoute.Count - 1].Value); //Some limit on when to include pause //gpsDist must be higher than (all) GPS errors if (point.Cadence == 0 && point.Speed == 0 && gpsDist > 0 && insertPauseFirst == false) { insertPauseFirst = true; //We cannot know the true time for the expected pause, just set time between as pause to show on map estimatedSec = time; pointTimePrev = pointTime; } else if (point.Cadence != 0 && point.Speed != 0 && insertPauseFirst) { //last point insertPauseLast = true; pointTimeNext = pointTime; } } if (insertPauseLast) { insertPauseFirst = false; insertPauseLast = false; //Only add rounded pauses, ST only handles complete seconds activity.TimerPauses.Add(new ValueRange <DateTime>( pointTimePrev.AddMilliseconds(-pointTimePrev.Millisecond), pointTimeNext.AddMilliseconds(-pointTimeNext.Millisecond))); if (verbose >= 10) { //TODO: Remove remark when stable activity.Notes += string.Format("Added pause from {0} to {1} (dist:{2}, elapsedSec:{3} {4}) ", pointTimePrev.ToLocalTime(), pointTimeNext.ToLocalTime(), dist, pointTimeNext.Subtract(pointTimePrev).TotalSeconds, info) + System.Environment.NewLine; } } } else if (detectPauses == 1) { //distance method bool insertPause = false; //how far from the first point double perc = 0; //This code previously had code to detect pauses from HR too if (activity.GPSRoute.Count > 0) { float gpsDist = gpsPoint.DistanceMetersToPoint(activity.GPSRoute[activity.GPSRoute.Count - 1].Value); //Some limit on when to include pause //gpsDist must be higher than (all) GPS errors if (gpsDist > 100 && gpsDist > 3 * dist) { insertPause = true; //We cannot know the true time for the expected pause, just set time between as pause to show on map estimatedSec = time; if (gpsDist > 0) { perc = dist / gpsDist; } info += "gps: " + gpsDist; } } if (insertPause) { //Use complete seconds only - pause is estimated, ST handles sec internally and this must be synced to laps estimatedSec = Math.Round(estimatedSec); IGPSPoint newPoint = (new GPSPoint.ValueInterpolator()).Interpolate( activity.GPSRoute[activity.GPSRoute.Count - 1].Value, gpsPoint, perc); if (point == train.TrackPoints[train.TrackPoints.Count - 1]) { //Last point is incorrect, adjust (added normally) gpsPoint = newPoint; } else { if (estimatedSec <= time + 1) { pointTimePrev = pointTime.AddSeconds(-time); pointTimeNext = pointTime; } else { //Add extra point activity.DistanceMetersTrack.Add(pointTime, pointDist); if (point.Latitude != 0 || point.Longitude != 0) { activity.GPSRoute.Add(pointTime, newPoint); } else if (device.FitnessDevice.HasElevationTrack && !float.IsNaN(newPoint.ElevationMeters)) { activity.ElevationMetersTrack.Add(pointTime, newPoint.ElevationMeters); } pointTimePrev = pointTime; pointTimeNext = pointTime.AddSeconds((int)(estimatedSec - time)); pointTime = pointTimeNext; } //Only add rounded pauses, ST only handles complete seconds activity.TimerPauses.Add(new ValueRange <DateTime>( pointTimePrev.AddMilliseconds(-pointTimePrev.Millisecond + 1000), pointTimeNext.AddMilliseconds(-pointTimeNext.Millisecond - 9000))); if (verbose >= 10) { //TODO: Remove remark when stable activity.Notes += string.Format("Added pause from {0} to {1} (dist:{2}, elapsedSec:{3}, per:{4} {5}) ", pointTimePrev.ToLocalTime(), pointTimeNext.ToLocalTime(), dist, time, perc, info) + System.Environment.NewLine; } } } } } activity.DistanceMetersTrack.Add(pointTime, pointDist); //lat/lon is 0 if a device has never had a fix if (point.Latitude != 0 || point.Longitude != 0) { activity.GPSRoute.Add(pointTime, gpsPoint); //Check if lat/lon ever change (ignore altitude), GlobalSat reports last known location without a fix if (point.Latitude != train.TrackPoints[0].Latitude || point.Longitude != train.TrackPoints[0].Longitude) { foundGPSPoint = true; } } if (device.FitnessDevice.HasElevationTrack && !float.IsNaN(point.Altitude)) { activity.ElevationMetersTrack.Add(pointTime, point.Altitude); } //zero HR is invalid reading - drop if (point.HeartRate > 0) { activity.HeartRatePerMinuteTrack.Add(pointTime, point.HeartRate); } //Zero Cadence/Power may be valid values, if there are any values (no way to detect lost communication) activity.CadencePerMinuteTrack.Add(pointTime, point.Cadence); if (point.Cadence > 0) { foundCadencePoint = true; } activity.PowerWattsTrack.Add(pointTime, point.Power); if (point.Power > 0) { foundPowerPoint = true; } if (point.Temperature != 0x7fff) { activity.TemperatureCelsiusTrack.Add(pointTime, point.Temperature / 10.0F); } } TimeSpan lapElapsed = TimeSpan.Zero; int totalDistance = 0; foreach (GlobalsatPacket.Lap lapPacket in train.Laps) { DateTime lapTime = ZoneFiveSoftware.Common.Data.Algorithm.DateTimeRangeSeries.AddTimeAndPauses(activity.StartTime, lapElapsed, activity.TimerPauses); lapElapsed += lapPacket.LapTime; //Same as lapPacket.EndTime for unpaused ILapInfo lap = activity.Laps.Add(lapTime, lapPacket.LapTime); //Adding Distance would previously make ST fail to add new laps. The distance is needed only when there is no GPS (Markers added) if (activity.TotalDistanceMetersEntered > 0) { lap.TotalDistanceMeters = lapPacket.LapDistanceMeters; } if (lapPacket.LapCalories > 0) { lap.TotalCalories = lapPacket.LapCalories; } if (lapPacket.AverageHeartRate > 0) { lap.AverageHeartRatePerMinute = lapPacket.AverageHeartRate; } if (lapPacket.AverageCadence > 0) { lap.AverageCadencePerMinute = lapPacket.AverageCadence; } if (lapPacket.AveragePower > 0) { lap.AveragePowerWatts = lapPacket.AveragePower; } if (!foundGPSPoint && activity.ElevationMetersTrack != null && activity.ElevationMetersTrack.Count > 1) { //Limitation in ST: lap elevation not auto calc without GPS, lap preferred to elevation DateTime lapEnd = ZoneFiveSoftware.Common.Data.Algorithm.DateTimeRangeSeries.AddTimeAndPauses(activity.StartTime, lapElapsed, activity.TimerPauses); ITimeValueEntry <float> p1 = activity.ElevationMetersTrack.GetInterpolatedValue(lapTime); ITimeValueEntry <float> p2 = activity.ElevationMetersTrack.GetInterpolatedValue(lapEnd); if (p1 != null && p2 != null) { lap.ElevationChangeMeters = p2.Value - p1.Value; } } if (verbose >= 5) { //TODO: Localise outputs? lap.Notes = string.Format("MaxSpeed:{0:0.##}m/s MaxHr:{1} MinAlt:{2}m MaxAlt:{3}m", lapPacket.MaximumSpeed, lapPacket.MaximumHeartRate, lapPacket.MinimumAltitude, lapPacket.MaximumAltitude); //Not adding Power/Cadence - not available //lap.Notes = string.Format("MaxSpeed={0} MaxHr={1} MinAlt={2} MaxAlt={3} MaxCadence={4} MaxPower={5}", // lapPacket.MaximumSpeed, lapPacket.MaximumHeartRate, lapPacket.MinimumAltitude, lapPacket.MaximumAltitude, lapPacket.MaximumCadence, lapPacket.MaximumPower); } //Add distance markers from Globalsat. Will for sure be incorrect after pause insertion totalDistance += lapPacket.LapDistanceMeters; activity.DistanceMarkersMeters.Add(totalDistance); } if (!foundGPSPoint) { if (activity.GPSRoute.Count > 0) { activity.Notes += string.Format("No GPS. Last known latitude:{0}, longitude:{1}", activity.GPSRoute[0].Value.LatitudeDegrees, activity.GPSRoute[0].Value.LongitudeDegrees); } activity.GPSRoute = null; } //Keep elevation only if the device (may) record elevation separately from GPS //It may be used also if the user drops GPS if points have been recorded. //(ST may have partial use of elevation together with GPS on other parts in the future?) if (!device.FitnessDevice.HasElevationTrack || activity.ElevationMetersTrack.Count == 0) { activity.ElevationMetersTrack = null; } //Barometric devices occasionally have bad points last if (activity.ElevationMetersTrack != null && activity.ElevationMetersTrack.Count > 1 && Math.Abs(activity.ElevationMetersTrack[activity.ElevationMetersTrack.Count - 1].Value - activity.ElevationMetersTrack[activity.ElevationMetersTrack.Count - 2].Value) > 1) { if (activity.GPSRoute != null && activity.ElevationMetersTrack.StartTime.AddSeconds(activity.ElevationMetersTrack.TotalElapsedSeconds) == activity.GPSRoute.StartTime.AddSeconds(activity.GPSRoute.TotalElapsedSeconds)) { IGPSPoint g = activity.GPSRoute[activity.GPSRoute.Count - 1].Value; activity.GPSRoute.SetValueAt(activity.GPSRoute.Count - 1, new GPSPoint(g.LatitudeDegrees, g.LongitudeDegrees, float.NaN)); } activity.ElevationMetersTrack.RemoveAt(activity.ElevationMetersTrack.Count - 1); } if (activity.HeartRatePerMinuteTrack.Count == 0) { activity.HeartRatePerMinuteTrack = null; } if (!foundCadencePoint) { activity.CadencePerMinuteTrack = null; } if (!foundPowerPoint) { activity.PowerWattsTrack = null; } if (activity.TemperatureCelsiusTrack.Count == 0) { activity.TemperatureCelsiusTrack = null; } if (pointDist == 0 || !importSpeedTrackAsDistance && foundGPSPoint) { activity.DistanceMetersTrack = null; } #if DISTANCETRACK_FIX //attempt to fix import of distance track in 3.1.4515 - to be updated when rereleased //for now import distance track, to keep compatibility(?) try { activity.CalcSpeedFromDistanceTrack = importSpeedTrackAsDistance; } catch { //older than 3.1.4515, disable if (!importSpeedTrackAsDistance && !foundGPSPoint) { activity.DistanceMetersTrack = null; } } #endif } }
public static IGPSLocation PointToLocation(IGPSPoint point) { return new GPSLocation(point.LatitudeDegrees, point.LongitudeDegrees); }
public float DistanceMetersToPoint(IGPSPoint point) { GPSPoint thisPoint = new GPSPoint( this.LatitudeDegrees, this.LongitudeDegrees, 0); if (point == null) { return float.MaxValue; } return point.DistanceMetersToPoint(thisPoint); }
public TrailGPSLocation(IGPSPoint gpsLoc) : this(gpsLoc, "", true) { }
public TrailGPSLocation(IGPSPoint gpsLoc, string name, bool required) : this(gpsLoc.LatitudeDegrees, gpsLoc.LongitudeDegrees, gpsLoc.ElevationMeters, name, required, defaultRadius) { }
public static float checkPass(float radius, IGPSPoint r1, float dt1, IGPSPoint r2, float dt2, TrailGPSLocation trailp, out float d) { #if SQUARE_DISTANCE const int sqrt2 = 2; #else //Optimise, accuracy is down to percent const float sqrt2 = 1.4142135623730950488016887242097F; #endif d = float.MaxValue; float factor = -1; if (r1 == null || r2 == null || trailp == null) { return(factor); } //Check if the line goes via the "circle" if the sign changes //Also need to check close points that fit in a 45 deg tilted "square" where sign may not change //Optimise for all conditions tested - property access takes some time float tplat = trailp.latitudeDegrees; float tplon = trailp.longitudeDegrees; float r1lat = r1.LatitudeDegrees; float r1lon = r1.LongitudeDegrees; float r2lat = r2.LatitudeDegrees; float r2lon = r2.LongitudeDegrees; if (r1lat > tplat && r2lat < tplat || r1lat < tplat && r2lat > tplat || r1lon > tplon && r2lon < tplon || r1lon < tplon && r2lon > tplon || dt1 < radius * sqrt2 && dt2 < radius * sqrt2) { //Law of cosines - get a1, angle at r1, the first point float d12 = TrailGPSLocation.DistanceMetersToPointGpsSimple(r1, r2); #if SQUARE_DISTANCE float a1_0 = (float)((dt1 + d12 - dt2) / (2 * Math.Sqrt(dt1 * d12))); #else float a1_0 = (dt1 * dt1 + d12 * d12 - dt2 * dt2) / (2 * dt1 * d12); #endif //Point is in circle if closest point is between r1&r2 and it is in circle (neither r1 nor r2 is) //This means the angle a1 must be +/- 90 degrees : cos(a1)>=0 if (a1_0 > -0.001) { //Rounding errors w GPS measurements a1_0 = Math.Min(a1_0, 1); a1_0 = Math.Max(a1_0, -1); float a1 = (float)Math.Acos(a1_0); //x is closest point to t on r1-r2 //Dist from r1 to x float d1x = (float)Math.Abs(dt1 * a1_0); //a1_0 = (float)Math.Cos(a1); //Dist from t1 to x float dtx = dt1 * (float)Math.Sin(a1); if (d1x < d12 && dtx < radius) { d = dtx; factor = (float)(d1x / d12); //Return factor, to return best aproximation from r1 } } } return(factor); }