Beispiel #1
0
        public void StartTiming(bool startOnMovement = true)
        {
            if (geolocator == null)
            {
                throw new InvalidOperationException("LapTimer must be initialized before calling StartTiming.");
            }

            if (isTiming)
            {
                StopTiming();
            }

            lapNumber            = 1;
            lapStartElapsedTime  = TimeSpan.Zero;
            headingNormalisation = new List <double>();
            lapCoordinates       = new List <LapDataPointViewModel>();
            currentSector        = generateSectors ? null : sectorCoordinates.MinBy(s => s.SectorNumber);
            accelerometer        = Accelerometer.GetDefault();
            if (accelerometer != null)
            {
                accelerometer.ReadingChanged += accelerometer_ReadingChanged;
            }

            geolocator.PositionChanged += geolocator_PositionChanged;
            if (!startOnMovement)
            {
                stopwatch.Start();
                isTiming = true;
                if (TimingStarted != null)
                {
                    TimingStarted(this, null);
                }
            }
            isFirstReading = true;
        }
        private void mapCircuit_Hold(object sender, System.Windows.Input.GestureEventArgs e)
        {
            if (!App.ViewModel.Track.IsLocal)
            {
                return;
            }
            var mapPosition = e.GetPosition(mapCircuit);
            var mapLayer    = new MapLayer();

            var geoCoordinate = mapCircuit.ConvertViewportPointToGeoCoordinate(mapPosition);

            lastSectorNumber++;
            double heading         = (double)App.ViewModel.DegreesData.SelectedItem;
            var    geographyPoint  = GeographyPoint.Create(geoCoordinate.Latitude, geoCoordinate.Longitude, null, heading);
            var    lineCoordinates = geographyPoint.ConvertToLine(0.02);
            var    newSector       = new TrackSectorViewModel
            {
                StartLatitude  = lineCoordinates[0].Latitude,
                StartLongitude = lineCoordinates[0].Longitude,
                EndLatitude    = lineCoordinates[0].Latitude,
                EndLongitude   = lineCoordinates[0].Longitude,
                Heading        = heading,
                IsFinishLine   = false,
                SectorNumber   = lastSectorNumber
            };

            App.ViewModel.Track.Sectors.Add(newSector);
            App.ViewModel.Track.SelectedSector = newSector;
            NavigationService.Navigate(new Uri("/EditSectorPage.xaml", System.UriKind.Relative));
        }
        public static bool IsCompletedByLapDataPoints(this TrackSectorViewModel sector, LapDataPointViewModel startPoint, LapDataPointViewModel endPoint)
        {
            var sectorLineStartPoint = GeoUtmConverter.ToUTM(sector.StartLatitude, sector.StartLongitude);
            var sectorLineEndPoint   = GeoUtmConverter.ToUTM(sector.EndLatitude, sector.EndLongitude);
            var dataLineStartPoint   = GeoUtmConverter.ToUTM(startPoint.Latitude.Value, startPoint.Longitude.Value);
            var dataLineEndPoint     = GeoUtmConverter.ToUTM(endPoint.Latitude.Value, endPoint.Longitude.Value);

            return(IsIntersect(sectorLineStartPoint, sectorLineEndPoint, dataLineStartPoint, dataLineEndPoint));
        }
 public static TrackSector AsModel(this TrackSectorViewModel sectorCoordinateViewModel)
 {
     return(new TrackSector
     {
         id = sectorCoordinateViewModel.Id,
         EndLatitude = sectorCoordinateViewModel.EndLatitude,
         EndLongitude = sectorCoordinateViewModel.EndLongitude,
         Heading = sectorCoordinateViewModel.Heading,
         IsFinishLine = sectorCoordinateViewModel.IsFinishLine,
         Number = sectorCoordinateViewModel.SectorNumber,
         StartLatitude = sectorCoordinateViewModel.StartLatitude,
         StartLongitude = sectorCoordinateViewModel.StartLongitude
     });
 }
        private void mapCircuit_Tap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            if (!App.ViewModel.Track.IsLocal)
            {
                return;
            }
            double smallestDistance             = double.MaxValue;
            TrackSectorViewModel selectedSector = null;
            var mapPosition    = e.GetPosition(mapCircuit);
            var geoCoordinate  = mapCircuit.ConvertViewportPointToGeoCoordinate(mapPosition);
            var goegraphyPoint = GeographyPoint.Create(geoCoordinate.Latitude, geoCoordinate.Longitude, null, geoCoordinate.Course);

            foreach (var sector in App.ViewModel.Track.Sectors)
            {
                if (double.IsNaN(sector.StartLatitude) ||
                    double.IsNaN(sector.StartLongitude) ||
                    double.IsNaN(sector.EndLatitude) ||
                    double.IsNaN(sector.EndLongitude))
                {
                    continue;
                }

                var    sectorLineStartPoint = GeographyPoint.Create(sector.StartLatitude, sector.StartLongitude);
                var    sectorLineEndPoint   = GeographyPoint.Create(sector.EndLatitude, sector.EndLongitude);
                var    midpoint             = GeoUtmConverter.Midpoint(sectorLineStartPoint, sectorLineEndPoint, sector.Heading);
                double distance             = midpoint.Distance(goegraphyPoint);
                if (distance < 0.5 && distance < smallestDistance)
                {
                    smallestDistance = distance;
                    selectedSector   = sector;
                }
            }
            if (selectedSector == null)
            {
                return;
            }
            App.ViewModel.Track.SelectedSector = selectedSector;
            NavigationService.Navigate(new Uri("/EditSectorPage.xaml", System.UriKind.Relative));
        }
Beispiel #6
0
        private bool PositionCompletesSector(LapDataPointViewModel currentPosition, LapDataPointViewModel previousPosition, TrackSectorViewModel sector)
        {
            if (previousPosition == null)
            {
                return(false);
            }
            if (currentPosition.Heading.HasValue && !HeadingIsWithinRange(sector.Heading, currentPosition.Heading.Value, 170d))
            {
                return(false);
            }

            return(sector.IsCompletedByLapDataPoints(previousPosition, currentPosition));
        }
Beispiel #7
0
        private void EndLap(LapDataPointViewModel lapEndDataPoint, int lapEndDataPointIndex, int currentSectorNumber)
        {
            if (!lapCoordinates.Any(lc => lc.Latitude.HasValue && lc.Longitude.HasValue && lc.Heading.HasValue))
            {
                return;
            }

            var lapStartTime = lapStartElapsedTime;
            var lapTime      = lapStartElapsedTime = lapEndDataPoint.ElapsedTime;

            lapEndDataPoint.IsEndOfLap = true;
            int currentLapEndCoordinate = lapEndDataPointIndex;
            var allCoordinates          = lapCoordinates.ToList();
            var currentLapCoordinates   = allCoordinates.Skip(previousLapEndCoordinateIndex + 1).Take(currentLapEndCoordinate - previousLapEndCoordinateIndex).ToList();

            if (!currentLapCoordinates.Any())
            {
                return;
            }
            var lap = new LapViewModel
            {
                StartElapsedTime = lapStartTime,
                EndElapsedTime   = lapTime,
                MaximumSpeed     = currentLapCoordinates.Max(c => c.Speed) ?? 0,
                Timestamp        = currentLapCoordinates.First().Timestamp,
                SectorNumber     = currentSectorNumber,
                LapNumber        = lapNumber,
                IsComplete       = true
            };

            previousLapEndCoordinateIndex = currentLapEndCoordinate;

            if (generateSectors)
            {
                if (currentLapCoordinates.Count() < 3)
                {
                    return;
                }
                lap.DataPoints = new ObservableCollection <LapDataPointViewModel>(currentLapCoordinates);

                var sectors = currentLapCoordinates.ToTrackSectors();
                if (sectors.Count < 3)
                {
                    return;
                }

                currentSector     = sectors[1];
                sectorCoordinates = sectors.Select(s => s.Value);
                lastSectorNumber  = 3;
            }
            else
            {
                // Some accelerometer readings in this lap but received before currentSector changed will have the last SectorNumber
                // so change them to be the first sector
                foreach (var coordinate in currentLapCoordinates)
                {
                    if (coordinate.SectorNumber == 1)
                    {
                        break;
                    }

                    coordinate.SectorNumber = 1;
                }
                lap.DataPoints = new ObservableCollection <LapDataPointViewModel>(currentLapCoordinates);
            }

            lapNumber++;
            if (LapComplete != null)
            {
                LapComplete(this, new LapEventArgs(lap));
            }
        }
Beispiel #8
0
        private void geolocator_PositionChanged(IGeolocator sender, PositionChangedEventArgs args)
        {
            if (!isTiming && isFirstReading)
            {
                // Speed threshold for starting timing - 4m/s
#if DEBUG
                if (!args.Position.Speed.HasValue || double.IsNaN(args.Position.Speed.Value) || args.Position.Speed.Value < 0.5d)
                {
                    return;
                }
#else
                if (!args.Position.Speed.HasValue || double.IsNaN(args.Position.Speed.Value) || args.Position.Speed.Value < 4d)
                {
                    return;
                }
#endif
                isFirstReading = false;
                stopwatch.Start();
                isTiming = true;
                if (TimingStarted != null)
                {
                    TimingStarted(this, null);
                }
            }

            var    elapsedTime      = stopwatch.Elapsed;
            double?speedInUserUnits = args.Position.Speed.HasValue && !double.IsNaN(args.Position.Speed.Value)
                                        ? isMetricUnits
                                            ? args.Position.Speed * Constants.KMH_TO_METRES_PER_SECOND
                                            : args.Position.Speed * Constants.KMH_TO_METRES_PER_SECOND / Constants.MILES_TO_KILOMETRES
                                        : null;

            var newGeographicLapDataPoint = new LapDataPointViewModel
            {
                ElapsedTime  = elapsedTime,
                Altitude     = args.Position.Altitude.HasValue && !double.IsNaN(args.Position.Altitude.Value) ?  args.Position.Altitude : null,
                Heading      = args.Position.Heading.HasValue && !double.IsNaN(args.Position.Heading.Value) ?  args.Position.Heading : null,
                Latitude     = args.Position.Latitude,
                Longitude    = args.Position.Longitude,
                Speed        = speedInUserUnits,
                SectorNumber = currentSector != null ? currentSector.SectorNumber : 1,
                Timestamp    = args.Position.Timestamp.ToLocalTime()
            };
            lapCoordinates.Add(newGeographicLapDataPoint);
            // Assuming this adding to the end of the list for performance
            int possibleLapEndGeographicLapDataPointIndex = lastGeographicLapDataPointIndex;
            lastGeographicLapDataPointIndex = lapCoordinates.Count - 1;

            bool skipCurrentCoordinate = false;
            if (generateSectors && currentSector == null)
            {
                skipCurrentCoordinate = true;
                if (newGeographicLapDataPoint.Heading.HasValue)
                {
                    double?lastHeading = headingNormalisation.Any() ? headingNormalisation.Last() : (double?)null;
                    headingNormalisation.Add(newGeographicLapDataPoint.Heading.Value);
                    if (lastHeading.HasValue && HeadingIsWithinRange(lastHeading.Value, newGeographicLapDataPoint.Heading.Value, 30d))
                    {
                        currentSector = newGeographicLapDataPoint.ToTrackSector(1, true);
                    }
                }
            }

            if (skipCurrentCoordinate)
            {
                return;
            }

            int  currentSectorNumber         = currentSector.SectorNumber;
            bool sectorCompleted             = PositionCompletesSector(newGeographicLapDataPoint, lastGeographicLapDataPoint, currentSector);
            var  possibleSectorEndCoordinate = lastGeographicLapDataPoint;
            lastGeographicLapDataPoint = newGeographicLapDataPoint;
            bool lapComplete = false;
            if (sectorCompleted)
            {
                lapComplete = currentSector.IsFinishLine;
                int nextSectorNumber = currentSectorNumber + 1;
                if (nextSectorNumber > lastSectorNumber)
                {
                    nextSectorNumber = firstSectorNumber;
                }

                // Fix the sector number for the current point, i.e. Move it over into the next sector and possibleSectorEndCoordinate is the end of the last sector
                newGeographicLapDataPoint.SectorNumber = nextSectorNumber;

                if (sectorCoordinates.Any())
                {
                    currentSector = sectorCoordinates.Single(s => s.SectorNumber == nextSectorNumber);
                }
                else
                {
                    lapComplete = true;
                }

                if (!lapComplete)
                {
                    EndSector(possibleSectorEndCoordinate, currentSectorNumber);
                }
            }
            if (lapComplete)
            {
                EndLap(possibleSectorEndCoordinate, possibleLapEndGeographicLapDataPointIndex, currentSectorNumber);
            }
        }
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            string navigationParameter;

            if (!NavigationContext.QueryString.TryGetValue(AppConstants.NAVIGATIONPARAMETER_TYPE, out navigationParameter))
            {
                navigationParameter = null;
            }

            if (App.ViewModel.Settings.IsTrial)
            {
                adRotatorControl.Visibility = System.Windows.Visibility.Visible;
                adDefaultControl.Visibility = System.Windows.Visibility.Visible;
            }
            else
            {
                adRotatorControl.Visibility = System.Windows.Visibility.Collapsed;
                adDefaultControl.Visibility = System.Windows.Visibility.Collapsed;
            }

            var    lapViewModel          = App.ViewModel.Track.History.SelectedSession.SelectedLap;
            string lapMinimumLengthBlurb = string.Format("* {0} {1} {2}",
                                                         AppResources.Text_Blurb_OfficialLength,
                                                         Math.Round(App.ViewModel.Track.Length * Constants.APP_MINIMUMLAPLENGTH_FACTOR, 3),
                                                         App.ViewModel.Settings.IsMetricUnits
                                                         ? AppResources.Text_Unit_Kilometres
                                                         : AppResources.Text_Unit_Miles);

            var lapDetailViewModel = new LapDetailViewModel
            {
                Lap                     = lapViewModel,
                SpeedUnitText           = App.ViewModel.SpeedUnitText,
                LengthUnitText          = App.ViewModel.LengthUnitText,
                ProjectedLength         = lapViewModel.ProjectedLength(),
                SessionWeather          = App.ViewModel.Track.History.SelectedSession.Weather,
                GpsDeviceName           = App.ViewModel.Track.History.SelectedSession.GpsDeviceName,
                LapMinimumLengthBlurb   = lapMinimumLengthBlurb,
                IsOfficial              = null,
                LapBelongsToCurrentUser = true,
                IsLeaderboardTime       = string.Equals(navigationParameter, AppConstants.NAVIGATIONPARAMETER_VALUE_LEADERBOARD)
            };

            var bestLapViewModel = lapViewModel as BestLapViewModel;

            if (bestLapViewModel != null)
            {
                lapDetailViewModel.UserDisplayName         = bestLapViewModel.UserDisplayName;
                lapDetailViewModel.GpsDeviceName           = bestLapViewModel.GpsDeviceName;
                lapDetailViewModel.IsOfficial              = !bestLapViewModel.IsUnofficial;
                lapDetailViewModel.LapBelongsToCurrentUser = App.ViewModel.IsAuthenticated && bestLapViewModel.UserName == App.MobileService.CurrentUser.UserId;
            }

            DataContext = lapDetailViewModel;
            var sectorSplitsAhead = new Dictionary <int, bool>();

            foreach (var sector in App.ViewModel.Track.Sectors)
            {
                var sectorSplitDataPoint = lapDetailViewModel.Lap.DataPoints.Where(dp => dp.Latitude.HasValue && dp.Longitude.HasValue).SkipWhile(dp => dp.SectorNumber != sector.SectorNumber).TakeWhile(dp => dp.SectorNumber == sector.SectorNumber).LastOrDefault();
                if (!sector.IsFinishLine && sectorSplitDataPoint == null)
                {
                    sectorSplitsAhead.Add(sector.SectorNumber, false);
                    continue;
                }

                var sectorSplit = sector.IsFinishLine
                                    ? lapDetailViewModel.Lap.LapTime
                                    : sectorSplitDataPoint.ElapsedTime - lapDetailViewModel.Lap.StartElapsedTime;

                TimeSpan bestSplit;
                if (sector.IsFinishLine)
                {
                    bestSplit = App.ViewModel.Track.BestLap != null && App.ViewModel.Track.BestLap.DataPoints != null
                                ? App.ViewModel.Track.BestLap.LapTime
                                : sectorSplit;
                }
                else
                {
                    var bestSplitDataPoint = App.ViewModel.Track.BestLap != null && App.ViewModel.Track.BestLap.DataPoints != null
                                                ? App.ViewModel.Track.BestLap.DataPoints.Where(dp => dp.Latitude.HasValue && dp.Longitude.HasValue).SkipWhile(dp => dp.SectorNumber != sector.SectorNumber).TakeWhile(dp => dp.SectorNumber == sector.SectorNumber).LastOrDefault()
                                                : null;

                    bestSplit = bestSplitDataPoint != null
                                ? bestSplitDataPoint.ElapsedTime - App.ViewModel.Track.BestLap.StartElapsedTime
                                : sectorSplit;
                }

                var splitStatus = sectorSplit - bestSplit;
                lapDetailViewModel.SectorSplits.Add(Tuple.Create(sector.SectorNumber,
                                                                 sectorSplit,
                                                                 splitStatus));
                sectorSplitsAhead.Add(sector.SectorNumber, splitStatus <= TimeSpan.Zero);
            }

            mapCircuit.Center = new GeoCoordinate(App.ViewModel.Track.Latitude, App.ViewModel.Track.Longitude);
            mapCircuit.SetZoomLevelForTrack(App.ViewModel.Track.LengthInMetres());

            TrackSectorViewModel finishLineSector = null;

            foreach (var sector in App.ViewModel.Track.Sectors)
            {
                if (sector.IsFinishLine)
                {
                    finishLineSector = sector;
                }

                var sectorLine = new MapPolyline();
                sectorLine.StrokeColor = sector.IsFinishLine ? Colors.Orange : Colors.Black;
                sectorLine.Path.Add(new GeoCoordinate(sector.StartLatitude, sector.StartLongitude));
                sectorLine.Path.Add(new GeoCoordinate(sector.EndLatitude, sector.EndLongitude));
                mapCircuit.MapElements.Add(sectorLine);
            }

            LapDataPointViewModel previousGeographicDataPoint = App.ViewModel.Track.History.SelectedSession.SelectedLap.DataPoints.FirstOrDefault(dp => dp.Latitude != null && dp.Longitude != null);

            if (previousGeographicDataPoint == null)
            {
                return;
            }

            var finishLineMidPoint = finishLineSector == null
                                        ? null
                                        : GeoUtmConverter.Midpoint(GeographyPoint.Create(finishLineSector.StartLatitude, finishLineSector.StartLongitude),
                                                                   GeographyPoint.Create(finishLineSector.EndLatitude, finishLineSector.EndLongitude),
                                                                   finishLineSector.Heading);

            if (finishLineMidPoint != null && App.ViewModel.Track.History.SelectedSession.SelectedLap.IsComplete)
            {
                AddCoordinatesAsPathLineToMap(previousGeographicDataPoint.SectorNumber, sectorSplitsAhead, finishLineMidPoint, previousGeographicDataPoint.AsGeographyPoint());
            }

            LapDataPointViewModel previousAccelerometerDataPoint = null;
            LapDataPointViewModel maxSpeedDataPoint = null;
            double?minSpeed           = null;
            var    decelerationSpeeds = new List <Tuple <double?, TimeSpan> >();
            var    mapLineAheadColor  = new SolidColorBrush(System.Windows.Media.Colors.Green);
            var    mapLineBehindColor = new SolidColorBrush(System.Windows.Media.Colors.Red);

            foreach (var dataPoint in App.ViewModel.Track.History.SelectedSession.SelectedLap.DataPoints)
            {
                if (!dataPoint.Latitude.HasValue || !dataPoint.Longitude.HasValue)
                {
                    if (dataPoint.AccelerationX.HasValue || dataPoint.AccelerationY.HasValue || dataPoint.AccelerationZ.HasValue)
                    {
                        previousAccelerometerDataPoint = dataPoint;
                    }
                    continue;
                }

                if (maxSpeedDataPoint == null || dataPoint.Speed > maxSpeedDataPoint.Speed)
                {
                    maxSpeedDataPoint = dataPoint;
                }

                if (!minSpeed.HasValue || minSpeed < dataPoint.Speed)
                {
                    if (decelerationSpeeds.Count > 1 && decelerationSpeeds.Sum(ds => ds.Item2.TotalSeconds) > 0.5)
                    {
                        // Create a pushpin for maximum speed before braking
                        if (maxSpeedDataPoint != null)
                        {
                            string pushpinContent = string.Format("{0}{1}",
                                                                  Math.Round(maxSpeedDataPoint.Speed.Value).ToString(CultureInfo.CurrentCulture),
                                                                  App.ViewModel.Settings.IsMetricUnits
                                                                        ? AppResources.Text_Unit_MetricSpeed
                                                                        : AppResources.Text_Unit_ImperialSpeed);
                            var pushpinData = Tuple.Create(new GeoCoordinate(maxSpeedDataPoint.Latitude.Value, maxSpeedDataPoint.Longitude.Value), pushpinContent);
                            apexDataLayer.Add(pushpinData);

                            maxSpeedDataPoint = null;
                        }

                        double decelerationTime = decelerationSpeeds.Sum(ds => ds.Item2.TotalSeconds);
                        double?totalSpeedLost   = decelerationSpeeds.First().Item1 - decelerationSpeeds.Last().Item1;
                        if (totalSpeedLost.HasValue && (totalSpeedLost.Value / decelerationTime) > 3)
                        {
                            // Create a pushpin for minimum speed through corner and the nearest g-force reading (if available)
                            string pushpinContent;
                            if (previousAccelerometerDataPoint != null)
                            {
                                pushpinContent = string.Format("{0}{1} | {2}{3}",
                                                               Math.Round(minSpeed.Value).ToString(CultureInfo.CurrentCulture),
                                                               App.ViewModel.Settings.IsMetricUnits
                                                                    ? AppResources.Text_Unit_MetricSpeed
                                                                    : AppResources.Text_Unit_ImperialSpeed,
                                                               ComputeRelativeGforce(previousAccelerometerDataPoint, App.ViewModel.Track.History.SelectedSession.DeviceOrientation),
                                                               AppResources.Text_Unit_GForce);
                            }
                            else
                            {
                                pushpinContent = string.Format("{0}{1}",
                                                               Math.Round(minSpeed.Value).ToString(CultureInfo.CurrentCulture),
                                                               App.ViewModel.Settings.IsMetricUnits
                                                                    ? AppResources.Text_Unit_MetricSpeed
                                                                    : AppResources.Text_Unit_ImperialSpeed);
                            }
                            var pushpinData = Tuple.Create(new GeoCoordinate(previousGeographicDataPoint.Latitude.Value, previousGeographicDataPoint.Longitude.Value), pushpinContent);
                            apexDataLayer.Add(pushpinData);
                        }
                    }
                    decelerationSpeeds.Clear();
                    minSpeed = dataPoint.Speed;
                }
                else
                {
                    minSpeed = dataPoint.Speed;
                    decelerationSpeeds.Add(Tuple.Create(minSpeed, dataPoint.ElapsedTime - previousGeographicDataPoint.ElapsedTime));
                }
                AddCoordinatesAsPathLineToMap(dataPoint.SectorNumber, sectorSplitsAhead, previousGeographicDataPoint.AsGeographyPoint(), dataPoint.AsGeographyPoint());

                previousGeographicDataPoint = dataPoint;
            }

            if (finishLineMidPoint != null && App.ViewModel.Track.History.SelectedSession.SelectedLap.IsComplete)
            {
                AddCoordinatesAsPathLineToMap(previousGeographicDataPoint.SectorNumber, sectorSplitsAhead, previousGeographicDataPoint.AsGeographyPoint(), finishLineMidPoint);
            }

            base.OnNavigatedTo(e);
        }