/// <summary> /// calculate vmc and vmg values if possible with current state /// </summary> /// <param name="state">current race state</param> public void Calculate(State state) { if(state.Location!=null && state.TargetMark!=null && state.TargetMark.Location!=null && state.Course is CourseByMarks) { double meters = CoordinatePoint.HaversineDistance(state.Location, state.TargetMark.Location); if (meters < _distanceCutoff)//if the reported distance is more than this threshold, it's probably garbage data { state.StateValues[StateValue.DistanceToTargetMarkInYards] = meters * MetersToYards; } var calculation = new MarkCalculation(); calculation.Location = state.Location; calculation.Time = state.BestTime; _previousCalculations.Add(calculation); while(_previousCalculations.Count>_previousCalculationCount) { _previousCalculations.RemoveAt(0); } if (_previousCalculations.Count > 1) { var previous = _previousCalculations[_previousCalculations.Count - 2]; var duration = calculation.Time - previous.Time; //calculate vmc var previousDistanceMeters = CoordinatePoint.HaversineDistance(previous.Location, state.TargetMark.Location); var distanceDelta = previousDistanceMeters - meters; var vmcMetersPerSecond = distanceDelta/duration.TotalSeconds; var vmcKnots = MetersPerSecondToKnots * vmcMetersPerSecond; calculation.VelocityMadeGoodOnCourse = vmcKnots; state.StateValues[StateValue.VelocityMadeGoodOnCourse] = vmcKnots;//_previousCalculations.Average(x => x.VelocityMadeGoodOnCourse); state.StateValues[StateValue.VelocityMadeGoodOnCoursePercent] = vmcKnots / state.StateValues[StateValue.SpeedInKnots] * 100; //TODO: calculate vmg if (state.PreviousMark != null && state.StateValues.ContainsKey(StateValue.SpeedInKnots)) { calculation.VelocityMadeGood = VelocityMadeGood(state.TargetMark, state.PreviousMark, calculation.Location, previous.Location, state.StateValues[StateValue.SpeedInKnots]); state.StateValues[StateValue.VelocityMadeGoodPercent] = calculation.VelocityMadeGood.Value / state.StateValues[StateValue.SpeedInKnots] * 100; var relativeAngle = RelativeAngleToCourse(state.TargetMark, state.PreviousMark, calculation.Location, previous.Location); state.StateValues[StateValue.CourseOverGroundRelativeToCourse] = AngleUtilities.RadiansToDegrees(relativeAngle); } } } else if(state.Course is CourseByAngle && state.StateValues.ContainsKey(StateValue.CourseOverGroundDirection) && state.StateValues.ContainsKey(StateValue.SpeedInKnots)) { state.StateValues[StateValue.VelocityMadeGood] = VelocityMadeGood((state.Course as CourseByAngle).CourseAngle, state.StateValues[StateValue.CourseOverGroundDirection], state.StateValues[StateValue.SpeedInKnots]); state.StateValues[StateValue.VelocityMadeGoodPercent] = state.StateValues[StateValue.VelocityMadeGood] / state.StateValues[StateValue.SpeedInKnots] * 100; var relativeAngle = AngleUtilities.AngleDifference(AngleUtilities.DegreestoRadians((state.Course as CourseByAngle).CourseAngle), AngleUtilities.DegreestoRadians(state.StateValues[StateValue.CourseOverGroundDirection])); state.StateValues[StateValue.CourseOverGroundRelativeToCourse] = AngleUtilities.RadiansToDegrees(relativeAngle); } }
/// <summary> /// calculate vmc and vmg values if possible with current state /// </summary> /// <param name="state">current race state</param> public void Calculate(State state) { if (state.Location != null && state.TargetMark != null && state.TargetMark.Location != null && state.Course is CourseByMarks) { double meters = CoordinatePoint.HaversineDistance(state.Location, state.TargetMark.Location); if (meters < _distanceCutoff)//if the reported distance is more than this threshold, it's probably garbage data { state.StateValues[StateValue.DistanceToTargetMarkInYards] = meters * MetersToYards; } var calculation = new MarkCalculation(); calculation.Location = state.Location; calculation.Time = state.BestTime; _previousCalculations.Add(calculation); while (_previousCalculations.Count > _previousCalculationCount) { _previousCalculations.RemoveAt(0); } if (_previousCalculations.Count > 1) { var previous = _previousCalculations[_previousCalculations.Count - 2]; var duration = calculation.Time - previous.Time; //calculate vmc var previousDistanceMeters = CoordinatePoint.HaversineDistance(previous.Location, state.TargetMark.Location); var distanceDelta = previousDistanceMeters - meters; var vmcMetersPerSecond = distanceDelta / duration.TotalSeconds; var vmcKnots = MetersPerSecondToKnots * vmcMetersPerSecond; calculation.VelocityMadeGoodOnCourse = vmcKnots; state.StateValues[StateValue.VelocityMadeGoodOnCourse] = vmcKnots; //_previousCalculations.Average(x => x.VelocityMadeGoodOnCourse); state.StateValues[StateValue.VelocityMadeGoodOnCoursePercent] = vmcKnots / state.StateValues[StateValue.SpeedInKnots] * 100; //TODO: calculate vmg if (state.PreviousMark != null && state.StateValues.ContainsKey(StateValue.SpeedInKnots)) { calculation.VelocityMadeGood = VelocityMadeGood(state.TargetMark, state.PreviousMark, calculation.Location, previous.Location, state.StateValues[StateValue.SpeedInKnots]); state.StateValues[StateValue.VelocityMadeGoodPercent] = calculation.VelocityMadeGood.Value / state.StateValues[StateValue.SpeedInKnots] * 100; var relativeAngle = RelativeAngleToCourse(state.TargetMark, state.PreviousMark, calculation.Location, previous.Location); state.StateValues[StateValue.CourseOverGroundRelativeToCourse] = AngleUtilities.RadiansToDegrees(relativeAngle); } } } else if (state.Course is CourseByAngle && state.StateValues.ContainsKey(StateValue.CourseOverGroundDirection) && state.StateValues.ContainsKey(StateValue.SpeedInKnots)) { state.StateValues[StateValue.VelocityMadeGood] = VelocityMadeGood((state.Course as CourseByAngle).CourseAngle, state.StateValues[StateValue.CourseOverGroundDirection], state.StateValues[StateValue.SpeedInKnots]); state.StateValues[StateValue.VelocityMadeGoodPercent] = state.StateValues[StateValue.VelocityMadeGood] / state.StateValues[StateValue.SpeedInKnots] * 100; var relativeAngle = AngleUtilities.AngleDifference(AngleUtilities.DegreestoRadians((state.Course as CourseByAngle).CourseAngle), AngleUtilities.DegreestoRadians(state.StateValues[StateValue.CourseOverGroundDirection])); state.StateValues[StateValue.CourseOverGroundRelativeToCourse] = AngleUtilities.RadiansToDegrees(relativeAngle); } }