< Summary

Class:PnController
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/Controller/PnController.cs
Covered lines:25
Uncovered lines:3
Coverable lines:28
Total lines:67
Line coverage:89.2% (25 of 28)
Covered branches:0
Total branches:0
Covered methods:4
Total methods:4
Method coverage:100% (4 of 4)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
PnController(...)0%110100%
Plan(...)0%4.14081.48%

File(s)

/github/workspace/Assets/Scripts/Controller/PnController.cs

#LineLine coverage
 1using UnityEngine;
 2
 3// The proportional navigation controller applies an input that is proportional to the line of
 4// sight's rotation rate to steer the agent towards its target.
 5// The controller uses pure proportional navigation, where the acceleration input is normal to
 6// the agent's velocity.
 7// Refer to "Basic Principles of Homing Guidance" by N. F. Palumbo (2010) for more information.
 8public class PnController : ControllerBase {
 9  private const float _epsilon = 1e-3f;
 10
 11  // Multiplication factor applied to the closing velocity when the target is moving away or abeam
 12  // to force an aggressive turn and prevent spiraling or divergence.
 13  private const float _strongTurnFactor = 100f;
 14
 15  // Cosine threshold for determining whether the target is abeam to the agent and preventing spiral
 16  // behavior. A value of 0.2 corresponds to roughly +-10 degrees around 90 degrees.
 17  private const float _abeamThreshold = 0.2f;
 18
 19  // Minimum line-of-sight rate used for clamping when the target is abeam to the agent.
 20  private const float _minLosRate = 0.2f;
 21
 744622  public float Gain { get; set; }
 23
 179824  public PnController(IAgent agent, float gain) : base(agent) {
 89925    Gain = gain;
 89926  }
 27
 28  // Controller-dependent implementation of the control law.
 654729  protected override Vector3 Plan(in Transformation relativeTransformation) {
 654730    Vector3 relativePosition = relativeTransformation.Position.Cartesian;
 654731    Vector3 relativeVelocity = relativeTransformation.Velocity.Cartesian;
 654732    Vector3 normalRelativeVelocity = Vector3.ProjectOnPlane(relativeVelocity, relativePosition);
 33
 654734    float distance = relativeTransformation.Position.Range;
 654735    if (distance < _epsilon) {
 036      return Vector3.zero;
 37    }
 38
 39    // Line-of-sight unit vector.
 654740    Vector3 losRHat = relativePosition / distance;
 41    // Line-of-sight rate vector.
 654742    Vector3 losVHat = normalRelativeVelocity / distance;
 654743    Vector3 losRotation = Vector3.Cross(losRHat, losVHat);
 44
 45    // The closing velocity is negative because the closing velocity is opposite to the range rate.
 654746    float closingVelocity = -relativeTransformation.Velocity.Range;
 654747    float closingSpeed = Mathf.Abs(closingVelocity);
 48    // Set the turn factor, which is equal to the closing velocity by default.
 654749    float turnFactor = closingVelocity;
 50    // Handle a negative closing velocity. If the target is moving away from the agent, negate the
 51    // turn factor and apply a stronger turn as the agent most likely passed the target already and
 52    // should turn around.
 654753    if (closingVelocity < 0) {
 054      turnFactor = Mathf.Max(1f, closingSpeed) * _strongTurnFactor;
 055    }
 56    // If the target is abeam to the agent, apply a stronger turn and clamp the line-of-sight rate
 57    // to avoid spiral behavior.
 654758    bool isAbeam = Mathf.Abs(Vector3.Dot(Agent.Velocity.normalized, relativePosition.normalized)) <
 59                   _abeamThreshold;
 719860    if (isAbeam) {
 65161      turnFactor = Mathf.Max(1f, closingSpeed) * _strongTurnFactor;
 65162      float clampedLosRate = Mathf.Max(losRotation.magnitude, _minLosRate);
 65163      losRotation = losRotation.normalized * clampedLosRate;
 65164    }
 654765    return Gain * turnFactor * Vector3.Cross(losRotation, Agent.Velocity.normalized);
 654766  }
 67}