public SensorEstimatedState(SensorModel sensorModel, PhysicalHeliState initialState)
 {
     _sensors = sensorModel;
     _initialState = initialState;
     _estimated = new PhysicalHeliState();
     Log = new List<HelicopterLogSnapshot>();
 }
        public override void Update(PhysicalHeliState startState, PhysicalHeliState endState, JoystickOutput output, TimeSpan startTime, TimeSpan endTime)
        {
            // Note: The sonic range finder is concerned with measuring at the new position in the timestep,
            // so always use endState here.
            PhysicalHeliState stateToMeasure = endState;
            Vector3 worldDirection = Vector3.Transform(_relativeRangeDirection, stateToMeasure.Orientation);
            worldDirection.Normalize();

            // Line equation, how long must line be to reach ground at y=y0 at given angle?
            // y = ax + b
            // y0 = worldDirection.Y * u + state.Position.Y =>
            // u = (y0-state.Position.Y)/worldDirection.Y
            Vector2 mapPosition = VectorHelper.ToHorizontal(stateToMeasure.Position);
            float y0 = _map.GetAltitude(mapPosition);

            if (stateToMeasure.Position.Y <= y0)
                FlatGroundHeight = stateToMeasure.Position.Y - y0;   // This will only be the case if we can fly through the terrain.. usually this should never happen
            else
            {
                const float maxRange = 10f;
                const float minDelta = 0.001f;    // Search resolution, returns distance when searching further does not improve the distance accuracy more than this
                float rayDistanceToGround = BinarySearchTerrainRayIntersection(0, maxRange, minDelta, stateToMeasure.Position, worldDirection);
                if (float.IsNaN(rayDistanceToGround))
                    FlatGroundHeight = float.NaN;
                else
                {
                    // A vector from the current position to the ground
                    Vector3 rangeVectorToGround = worldDirection*rayDistanceToGround;

                    // Invert vector and take its Y component to give flat ground height
                    FlatGroundHeight = Vector3.Negate(rangeVectorToGround).Y;
                }
            }
        }
 public void GetState(out PhysicalHeliState estimated, out PhysicalHeliState observed, out PhysicalHeliState blindEstimatedState)
 {
     estimated = new PhysicalHeliState(_currentState.Result.Orientation, _currentState.Result.Position,
                                       _currentState.Result.Velocity,
                                       _currentState.StartingCondition.Acceleration);
     observed = new PhysicalHeliState();
     blindEstimatedState = new PhysicalHeliState();
 }
 public SimulationStepResults(PhysicalHeliState startState, TimeSpan startTime)
 {
     StartingCondition = new TimestepStartingCondition(startState.Position, startState.Velocity,
                                                       startState.Acceleration, startState.Orientation, startTime);
     Result = new TimestepResult(startState.Position, startState.Velocity, startState.Orientation, startTime);
     StartTime = startTime;
     EndTime = startTime;
     Duration = TimeSpan.Zero;
 }
        public override void Update(PhysicalHeliState startState, PhysicalHeliState endState, JoystickOutput output, TimeSpan startTime, TimeSpan endTime)
        {
            PhysicalHeliState stateToMeasure = endState;

            // TODO Fill XYZ magnetic readings
            // TODO Simulate sensor lag, inaccuarcy, noise..
            _sampleHistory.Add(stateToMeasure.Axes);

            CurrentMeasuredAxes = IsPerfect ? stateToMeasure.Axes : ComputeMeanAxes();
        }
 public HelicopterLogSnapshot(PhysicalHeliState trueState, PhysicalHeliState observedState,
                              PhysicalHeliState estimatedState, PhysicalHeliState blindEstimatedState,
                              ForwardRightUp accelerometer, float groundAltitude, TimeSpan time)
 {
     True           = trueState;
     Estimated      = estimatedState;
     Observed       = observedState;
     Time           = time;
     Accelerometer  = accelerometer;
     BlindEstimated = blindEstimatedState;
     GroundAltitude = groundAltitude;
 }
 public HelicopterLogSnapshot(PhysicalHeliState trueState, PhysicalHeliState observedState,
                             PhysicalHeliState estimatedState, PhysicalHeliState blindEstimatedState, 
                             ForwardRightUp accelerometer, float groundAltitude, TimeSpan time)
 {
     True = trueState;
     Estimated = estimatedState;
     Observed = observedState;
     Time = time;
     Accelerometer = accelerometer;
     BlindEstimated = blindEstimatedState;
     GroundAltitude = groundAltitude;
 }
        public override void Update(PhysicalHeliState startState, PhysicalHeliState endState, JoystickOutput output, TimeSpan totalSimulationTime, TimeSpan endTime)
        {
            PhysicalHeliState stateToMeasure = endState;
            float trueAltitude = stateToMeasure.Position.Y;

            if (IsPerfect)
                Altitude = trueAltitude;
            else
            {
                float simulatedPressure = _pressureProvider.GetSimulatedStaticPressure(trueAltitude);
                Altitude = StaticPressureHelper.GetAltitude(simulatedPressure, SeaLevelStaticPressure);
            }
        }
        /// <summary>Calculates the input and external forces.</summary>
        /// <returns>The new orientation and the total acceleration for this orientation in this timestep.</returns>
        public SimulationStepResults PerformTimestep(PhysicalHeliState prev, JoystickOutput output, TimeSpan stepDuration,
                                                     TimeSpan stepEndTime)
        {
            if (!_isInitialized)
            {
            //                startCondition.Acceleration = CalculateAcceleration(startCondition.Orientation, startCondition.Velocity, input);
                _prevTimestepResult = new TimestepResult(prev.Position, prev.Velocity, prev.Orientation,
                                                         stepEndTime - stepDuration);

                _isInitialized = true;
            }

            // If the number of substeps is 0 then only the state at the end of the timestep will be calculated.
            // If the number is greater than 1, then the timestep will 1 then a substep will be calculated in the middle of the timestep.
            const int substeps = 0;
            if (substeps < 0)
                throw new Exception("The number of substeps is invalid.");

            TimeSpan substepDuration = stepDuration.Divide(1 + substeps);

            Vector3 initialAcceleration = CalculateAcceleration(prev.Orientation, prev.Velocity, output);
            var initialState = new TimestepStartingCondition(_prevTimestepResult, initialAcceleration);
                //_prevTimestepResult.Result;
            //            var substepResults = new List<SubstepResults> {initialState};

            // We always need to calculate at least the timestep itself, plus any optional substeps.
            // Substeps are used to provide sensors with a higher frequency of data than the simulator is capable of rendering real-time.
            //            const int stepsToCalculate = substeps + 1;
            //            SubstepResults prevSubstep = initialState;
            //            for (int i = 0; i < stepsToCalculate; i++)
            //            {
            //                prevSubstep.Acceleration = CalculateAcceleration(prevSubstep.Orientation, prevSubstep.Velocity, input);
            //                SubstepResults r = SimulateStep(prevSubstep, prevSubstep.Acceleration, input, substepDuration, stepEndTime);
            //
            //                substepResults.Add(r);
            //                prevSubstep = r;
            //            }

            TimestepResult result = SimulateStep(initialState, output, substepDuration, stepEndTime);
            //new SimulationStepResults(stepDuration, substepDuration, substepResults);
            _prevTimestepResult = result;

            //            DebugInformation.Time1 = stepEndTime;
            //            DebugInformation.Q1 = result.Orientation;
            //
            //            DebugInformation.Vectors["Pos"] = result.Position;
            //            DebugInformation.Vectors["Vel"] = result.Velocity;
            //            DebugInformation.Vectors["Acc"] = initialAcceleration;

            return new SimulationStepResults(initialState, result, stepEndTime - stepDuration, stepEndTime);
        }
        public override void Update(PhysicalHeliState startState, PhysicalHeliState endState, JoystickOutput output, TimeSpan startTime, TimeSpan endTime)
        {
            var dt = (float) ((endTime - startTime).TotalSeconds);
            _rate.Pitch = output.Pitch*PhysicsConstants.MaxPitchRate;
            _rate.Roll = output.Roll*PhysicsConstants.MaxRollRate;
            _rate.Yaw = output.Yaw*PhysicsConstants.MaxYawRate;

            // Note it is important to explicitly calculate the delta and not reuse the _rate values multiplied by dt
            // See http://stackoverflow.com/questions/2491161/why-differs-floating-point-precision-in-c-when-separated-by-parantheses-and-when
            // and QuaternionPrecisionTest.Test() for more information about precision.
            _delta.Pitch = output.Pitch*PhysicsConstants.MaxPitchRate*dt;
            _delta.Roll = output.Roll*PhysicsConstants.MaxRollRate*dt;
            _delta.Yaw = output.Yaw*PhysicsConstants.MaxYawRate*dt;

            // TODO Handle IsPerfect == false
        }
        /// <summary>
        ///   Returns a new physical state where the values represent the error in the estimated state to
        ///   the given true state. I.e. position = estimatedPosition - truePosition.
        /// </summary>
        /// <param name="trueState"></param>
        /// <param name="estimatedState"></param>
        /// <returns></returns>
        public static PhysicalHeliState GetError(PhysicalHeliState trueState, PhysicalHeliState estimatedState)
        {
            var axesError = new Axes
                                {
                                    Forward = estimatedState.Axes.Forward - trueState.Axes.Forward,
                                    Right = estimatedState.Axes.Right - trueState.Axes.Right,
                                    Up = estimatedState.Axes.Up - trueState.Axes.Up
                                };

            //            var rotationError = new Matrix {Forward = axesError.Forward, Right = axesError.Right, Up = axesError.Up};

            // TODO Verify this works, since axes were originally used to compute state instead of orientation (but should equivalent)
            return new PhysicalHeliState(
                Quaternion.Identity, // TODO Not sure how to represent rotation error in quaternion representation
                estimatedState.Position - trueState.Position,
                estimatedState.Velocity - trueState.Velocity,
                estimatedState.Acceleration - trueState.Acceleration);
        }
        /// <summary>
        /// TODO Temporary. We might later distinguish between physical state and navigation state.
        /// </summary>
        /// <param name="s"></param>
        /// <param name="heightAboveGround"></param>
        /// <param name="waypoint"></param>
        /// <param name="output"></param>
        /// <returns></returns>
        public static HeliState ToHeliState(PhysicalHeliState s, float heightAboveGround, Waypoint waypoint, JoystickOutput output)
        {
            // Update state
            var r = new HeliState
                        {
                            HeightAboveGround = heightAboveGround,
                            Orientation = s.Orientation,
                            Forward = s.Axes.Forward,
                            Up = s.Axes.Up,
                            Right = s.Axes.Right,
                            Output = output,
                            Position = s.Position,
                            Velocity = s.Velocity,
                            Acceleration = s.Acceleration,
                            Waypoint = waypoint,
                            HPositionToGoal = VectorHelper.ToHorizontal(waypoint.Position - s.Position),
                        };

            // Update angles from current state
            var radians = new Angles
                              {
                                  PitchAngle = VectorHelper.GetPitchAngle(s.Orientation),
                                  RollAngle = VectorHelper.GetRollAngle(s.Orientation),
                                  HeadingAngle = VectorHelper.GetHeadingAngle(s.Orientation),
                              };

            // Velocity can be zero, and thus have no direction (NaN angle)
            radians.BearingAngle = (s.Velocity.Length() < 0.001f)
                                       ? radians.HeadingAngle
                                       : VectorHelper.GetHeadingAngle(s.Velocity);
                //GetAngle(VectorHelper.ToHorizontal(s.Velocity));

            radians.GoalAngle = VectorHelper.GetAngle(r.HPositionToGoal);
            radians.BearingErrorAngle = VectorHelper.DiffAngle(radians.BearingAngle, radians.GoalAngle);

            // Store angles as both radians and degrees for ease-of-use later
            r.Radians = radians;
            r.Degrees = ToDegrees(radians);
            return r;
        }
        private void UpdatePhysicalState(GameTime gameTime, JoystickOutput output, out PhysicalHeliState trueState)
        {
            _simulationState = _physics.PerformTimestep(_physicalState, output, gameTime.ElapsedGameTime,
                                                        gameTime.TotalGameTime);

            TimestepResult final = _simulationState.Result;

            // We need to use the second last simulation substep to obtain the current acceleration used
            // because the entire substep has constant acceleration and it makes no sense
            // to use the acceleration calculated for the state after the timestep because no animation will occur after it.
            // TODO Support substeps
            Vector3 currentAcceleration = _simulationState.StartingCondition.Acceleration;

            trueState = new PhysicalHeliState(final.Orientation, final.Position, final.Velocity, currentAcceleration);
        }
        public override void Update(GameTime gameTime)
        {
            if (_playEngineSound && _engineSoundInst != null && _engineSoundInst.State != SoundState.Playing)
                _engineSoundInst.Play();


            long totalTicks = gameTime.TotalGameTime.Ticks;
            JoystickOutput output = GetJoystickInput(totalTicks);

            // Invert yaw because we want yaw rotation positive as clockwise seen from above
            output.Yaw *= -1;

            // Update physics and sensors + state estimators
            PhysicalHeliState trueState, observedState, estimatedState, blindEstimatedState;
            UpdatePhysicalState(gameTime, output, out trueState);
            UpdateStateEstimators(_simulationState, gameTime, output, out estimatedState, out blindEstimatedState, out observedState);

            float trueGroundAltitude = GetGroundAltitude(trueState.Position);
            float estimatedGroundAltitude = GetGroundAltitude(trueState.Position);
            float trueHeightAboveGround = GetHeightAboveGround(trueState.Position);
            float gpsinsHeightAboveGround = GetHeightAboveGround(estimatedState.Position);
            float rangefinderHeightAboveGround = _sensors.GroundRangeFinder.FlatGroundHeight;

            

            float estimatedHeightAboveGround;
            if (!_testConfiguration.UseGPS)
                throw new NotImplementedException();
            else if (!_testConfiguration.UseINS)
                throw new NotImplementedException();

            if (_testConfiguration.UseGPS && _testConfiguration.UseINS)
            {
                if (!_testConfiguration.UseRangeFinder)
                    estimatedHeightAboveGround = gpsinsHeightAboveGround;
                else
                {
                    // The range finder may be out of range (NaN) and typically requires <10 meters.
                    // In this case we need to fully trust INS/GPS estimate.
                    // Note GPS is easily out-bested by range finder, so no need to weight
                    estimatedHeightAboveGround = float.IsNaN(rangefinderHeightAboveGround)
                                                     ? gpsinsHeightAboveGround
                                                     : rangefinderHeightAboveGround;
                    //                                                   : 0.2f*gpsinsHeightAboveGround + 0.8f*rangefinderHeightAboveGround;
                }
            }
            else
                throw new NotImplementedException();


            // Override Kalman Filter estimate of altitude by the more accurate range finder.
            // However, there is a problem that the estimated map altitude depends on the estimated horizontal position; which is inaccurate.
            estimatedState.Position.Y = estimatedHeightAboveGround + estimatedGroundAltitude;

            _physicalState = trueState;
            _trueState = StateHelper.ToHeliState(trueState, trueHeightAboveGround, Autopilot.CurrentWaypoint, output);
            _estimatedState = StateHelper.ToHeliState(estimatedState, estimatedHeightAboveGround, Autopilot.CurrentWaypoint, output);

            

            // _observedState = observedState;

            // Calculate current error in estimated state
            _estimatedStateError = StateHelper.GetError(_physicalState, StateHelper.ToPhysical(_estimatedState));

            // Add current simulation step to log
            ForwardRightUp accelerometerData = _sensors.IMU.AccelerationLocal;
            Log.Add(new HelicopterLogSnapshot(trueState, observedState, estimatedState, blindEstimatedState, accelerometerData, trueGroundAltitude, gameTime.TotalGameTime));


            // Update visual appearances
            UpdateEffects();

            UpdateSound(output);

            // If we have disabled Jitter we must detect collisions ourselves for test scenarios
            if (!UseTerrainCollision)
            {
                if (IsCollidedWithTerrain())
                    if (Crashed != null) Crashed(gameTime);
            }

            // Handle crashes, user input etc.. that cause the helicopter to reset
            HandleResetting(gameTime);

            base.Update(gameTime);
        }
        public void GetState(out PhysicalHeliState estimated, out PhysicalHeliState observed, out PhysicalHeliState blindEstimatedState)
        {
            if (!Ready) throw new Exception("Can't call GetNextState() when Ready is false.");

            estimated = _currentEstimate;
            observed = _currentObservation;
            blindEstimatedState = _currentBlindEstimate;
        }
        public void Update(SimulationStepResults trueState, long elapsedMillis, long totalElapsedMillis,
                           JoystickOutput currentOutput)
        {
            #if XBOX
            // TODO Xbox equivalent or fix the GPSINSFilter dependency on Iridium Math.Net
            _isInitialized = true;
            #else

            // TODO If sensors are not ready we cannot produce an estimated state
            // However, currently the helicopter starts mid-air so we have no time to wait for it to initialize.. so this is cheating.
            // When the helicopter starts on the ground we can properly implement this process.
            if (!_isInitialized)
            {
                if (!_sensors.Ready) return;

                _estimated.Position = _initialState.Position;
                _estimated.Velocity = _initialState.Velocity;
                _estimated.Acceleration = _initialState.Acceleration;
                _estimated.Orientation = _initialState.Orientation;
                _prevGPSPos = _sensors.GPS.Output.Position;

                _gpsins = new GPSINSFilter(
                    TimeSpan.FromMilliseconds(totalElapsedMillis - elapsedMillis),
                    _initialState.Position,
                    _initialState.Velocity,
                    _initialState.Orientation,
                    _sensors.Specifications);

                _isInitialized = true;
            }

            TimeSpan totalTime = TimeSpan.FromMilliseconds(totalElapsedMillis);

            // Setup observations
            var observations = new GPSINSObservation();
            observations.Time = totalTime;
            observations.RangeFinderHeightOverGround = _sensors.GroundRangeFinder.FlatGroundHeight; // TODO Update frequency? Noise?

            if (_sensors.GPS.IsUpdated)
            {
                observations.GotGPSUpdate = true;
                observations.GPSPosition = _sensors.GPS.Output.Position;
                observations.GPSHVelocity = _sensors.GPS.Output.Velocity;
            }

            // Setup input to filter's internal model
            var input = new GPSINSInput
                            {
                                AccelerationWorld = _sensors.IMU.AccelerationWorld,
                                Orientation = _sensors.IMU.AccumulatedOrientation,
            //                                PitchDelta = _sensors.IMU.AngularDeltaBody.Radians.Pitch,
            //                                RollDelta = _sensors.IMU.AngularDeltaBody.Radians.Roll,
            //                                YawDelta = _sensors.IMU.AngularDeltaBody.Radians.Yaw,
            //                                PitchRate = _sensors.IMU.Output.AngularRate.Radians.Pitch,
            //                                RollRate = _sensors.IMU.Output.AngularRate.Radians.Roll,
            //                                YawRate = _sensors.IMU.Output.AngularRate.Radians.Yaw,
                            };

            // Estimate
            StepOutput<GPSINSOutput> gpsinsEstimate = _gpsins.Filter(observations, input);

            //            Vector3 deltaPosition = gpsinsEstimate.PostX.Position - _currentEstimate.Position;
            //            if (deltaPosition.Length() > 40)
            //                deltaPosition = deltaPosition;

            //            var trueState = CheatingTrueState.Result;
            GPSINSOutput observed = gpsinsEstimate.Z;
            GPSINSOutput estimated = gpsinsEstimate.PostX;
            GPSINSOutput blindEstimated = gpsinsEstimate.X;

            _currentEstimate = new PhysicalHeliState(estimated.Orientation, estimated.Position, estimated.Velocity,
                                                     input.AccelerationWorld);

            _currentBlindEstimate = new PhysicalHeliState(blindEstimated.Orientation, blindEstimated.Position, blindEstimated.Velocity,
                                                     input.AccelerationWorld);

            _currentObservation = new PhysicalHeliState(observed.Orientation, observed.Position, observed.Velocity,
                                                        Vector3.Zero);
            #endif
        }
 public abstract void Update(PhysicalHeliState startState, PhysicalHeliState endState, JoystickOutput output, TimeSpan totalSimulationTime, TimeSpan endTime);
Пример #18
0
        public override void Update(PhysicalHeliState startState, PhysicalHeliState endState, JoystickOutput output, TimeSpan startTime, TimeSpan endTime)
        {
            _accelerometer.Update(startState, endState, output, startTime, endTime);
            _gyroscope.Update(startState, endState, output, startTime, endTime);

            // Make sure we use orientation at start of timestep to transform to world, since IMU should be able to calculate accumulated position
            // by this world acceleration, and according to my physics simulation the position is calculated before rotation is.
            AccelerationWorld = _accelerometer.AccelerationWorld;
            AccelerationLocal = new ForwardRightUp(_accelerometer.Forward, _accelerometer.Right, _accelerometer.Up);
            AngularRateBody = AngularValues.FromRadians(_gyroscope.Rate);
            AngularDeltaBody = AngularValues.FromRadians(_gyroscope.Delta);

            PitchRollYaw delta = AngularDeltaBody.Radians;
            AccumulatedOrientation =
                VectorHelper.AddPitchRollYaw(AccumulatedOrientation, delta.Pitch, delta.Roll, delta.Yaw);
        }
        private void InitHelicopters(SunlightParameters skyParameters, NavigationMap heightmap)
        {
            // TODO 1 or more helicopters?
            if (_scenario.HelicopterScenarios == null || _scenario.HelicopterScenarios.Count == 0)
                throw new Exception("Could not find helicopter scenario.");

            HelicopterScenario heliScenario = _scenario.HelicopterScenarios[0];

            var startVelocity = new Vector3(); //new Vector3(-0.5f, 0, -1.0f)
            var startState = new PhysicalHeliState(
                Quaternion.Identity,
                heliScenario.StartPosition,
                startVelocity,
                Vector3.Zero);

            
            _helicopter = new HelicopterBase(this, _testConfiguration, _terrainCollision, this, _basicEffect, skyParameters, heliScenario, 
                                             heliScenario.EngineSound, heliScenario.PlayerControlled, DrawText);


            // If testing then we will continue to the next waypoint as soon as the true position is within
            // the radius of the waypoint. If not testing (non-simulated scenarios), we typically want to
            // progress only if the estimated state is belived to be within the radius.
            _helicopter.Autopilot.IsTestMode = IsTestMode;
            _helicopter.Autopilot.IsTruePositionWithinRadius = false;

            _helicopter.Autopilot.MaxHVelocity = _testMaxHVelocityIter.Current;

            // Optionally a PID setup may have been provided to use with the helicopters.
            // If not a default setup is used.
            if (_initialPIDSetup.HasValue)
                SetPIDSetup(_initialPIDSetup.Value);

            //            InitFormationHelicopters(startPos, skyParameters);

            _helicopter.Autopilot.Map = heightmap;
        }
Пример #20
0
        public void Update(PhysicalHeliState tartState, PhysicalHeliState endState, JoystickOutput output, TimeSpan startTime, TimeSpan endTime)
        {
            if (!_isInitialized)
            {
                _initStartTime = startTime;
                _isInitialized = true;
            }

            PhysicalHeliState stateToMeasure = endState;

            // Only update GPS readout as often as SecondsPerUpdate says
            if (_useUpdateFrequency && startTime < _prevUpdate + TimeSpan.FromSeconds(SecondsPerUpdate))
                return;

            // It takes some time for a GPS to connect and produce positional values
            if (!Ready && startTime >= _initStartTime + _setupDuration)
                Ready = true;

            // Update the GPS information if the device is ready
            if (Ready)
            {
                _prevUpdate = startTime;

                Vector3 position = stateToMeasure.Position;
                Vector3 velocity = stateToMeasure.Velocity;

                if (!_isPerfect)
                {
                    position += VectorHelper.GaussRandom3(_sensorSpecifications.GPSPositionStdDev);
                    velocity += VectorHelper.GaussRandom3(_sensorSpecifications.GPSVelocityStdDev);
                }

                Output = new GPSOutput(position, velocity);
            }
        }
        private void UpdateStateEstimators(SimulationStepResults simulationState, GameTime gameTime, JoystickOutput output,
                                           out PhysicalHeliState estimatedState,
                                           out PhysicalHeliState blindEstimatedState,
                                           out PhysicalHeliState observedState)
        {
            var dtMillis = (long)gameTime.ElapsedGameTime.TotalMilliseconds;
            var totalMillis = (long)gameTime.TotalGameTime.TotalMilliseconds;

            //            TimeSpan totalSimulationTime = gameTime.TotalGameTime;

            // Update sensors prior to using the state provider, 
            // because they are typically dependent on the state of the sensors.
            Sensors.Update(_simulationState, output);

            // Update states
            ((SensorEstimatedState)_estimatedStateProvider).CheatingTrueState = simulationState; // TODO Temp cheating
            _estimatedStateProvider.Update(simulationState, dtMillis, totalMillis, output);

            // TODO Separate physical and navigational states
            _estimatedStateProvider.GetState(out estimatedState, out observedState, out blindEstimatedState);
        }
        public void Reset(TimeSpan totalGameTime, HelicopterScenario scenario, NavigationMap heightmap)
        {
            Console.WriteLine(@"Resetting helicopter.");

            _scenario = scenario;
            

            // TODO We would rather want to do Autopilot.Reset() than this fugly code
            Autopilot.IsAtDestination = false;
            Autopilot = Autopilot.Clone();
            Autopilot.Task = scenario.Task.Clone();
            Autopilot.Map = heightmap;


            Vector3 startPosition = scenario.StartPosition;
            Vector3 startVelocity = Vector3.Zero;
            Vector3 startAcceleration = Vector3.Zero;
            Quaternion startOrientation = Quaternion.Identity;

            if (Task.HoldHeightAboveGround > 0)
                startPosition.Y = Autopilot.Map.GetAltitude(VectorHelper.ToHorizontal(startPosition)) + Task.HoldHeightAboveGround;

            var startPhysicalState = new PhysicalHeliState(
                startOrientation, startPosition, startVelocity, startAcceleration);
            var initialState = new SimulationStepResults(startPhysicalState, totalGameTime);

            _physicalState = startPhysicalState;

            // Re-create the state provider when resetting because some sensors will have to re-initialize.
            _physics = new HeliPhysics(_collision, UseTerrainCollision);
            _sensors = new SensorModel(_sensorSpecifications, Autopilot.Map, startPosition, startOrientation);
            _perfectStateProvider = new PerfectState();
            _estimatedStateProvider = new SensorEstimatedState(_sensors, startPhysicalState);

            // Wait for state to become stable.
//            while (!_perfectStateProvider.Ready)
//            {
                // TODO GPS will require N seconds of startup time
//                _perfectStateProvider.Update(initialState, 0, 0, new JoystickOutput());
//                Sensors.Update(initialState, new JoystickOutput());
                //                Thread.Sleep(100);
//            }

            // When resetting, use perfect state as starting point.
            // TODO It should not be necessary to create this waypoint since it should never be used for navigation! Delete if safe.
            // Use start position and start orientation instead.
            const float defaultWaypointRadius = 5;
            var startWaypoint = new Waypoint(startPosition, 0, WaypointType.Intermediate, defaultWaypointRadius);
            _trueState = StateHelper.ToHeliState(startPhysicalState, GetHeightAboveGround(startPhysicalState.Position), startWaypoint, new JoystickOutput());
            _estimatedState = _trueState;

            Log.Clear();
        }
        public void Update(SimulationStepResults step, JoystickOutput output)
        {
            // TODO Use substeps in physics simulation when sensors require higher frequency than the main-loop runs at
            // Currently we simply use the state at the start of the timestep to feed into the sensors, so they read the state
            // that was before any motion took place in that timestep.
            // Later we may need to update sensors multiple times for each timestep.
            TimestepStartingCondition start = step.StartingCondition;
            TimestepResult end = step.Result;

            var startPhysicalState = new PhysicalHeliState(start.Orientation, start.Position, start.Velocity,
                                                      start.Acceleration);

            var endPhysicalState = new PhysicalHeliState(end.Orientation, end.Position, end.Velocity,
                                                      new Vector3(float.NaN));

            // Update all sensors
            foreach (ISensor sensor in _sensors)
                sensor.Update(startPhysicalState, endPhysicalState, output, step.StartTime, step.EndTime);
        }