< Summary

Class:PnController
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/Controller/PnController.cs
Covered lines:27
Uncovered lines:1
Coverable lines:28
Total lines:67
Line coverage:96.4% (27 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.014092.59%

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
 3522  public float Gain { get; set; }
 23
 3024  public PnController(IAgent agent, float gain) : base(agent) {
 1525    Gain = gain;
 1526  }
 27
 28  // Controller-dependent implementation of the control law.
 1429  protected override Vector3 Plan(in Transformation relativeTransformation) {
 1430    Vector3 relativePosition = relativeTransformation.Position.Cartesian;
 1431    Vector3 relativeVelocity = relativeTransformation.Velocity.Cartesian;
 1432    Vector3 normalRelativeVelocity = Vector3.ProjectOnPlane(relativeVelocity, relativePosition);
 33
 1434    float distance = relativeTransformation.Position.Range;
 1435    if (distance < _epsilon) {
 036      return Vector3.zero;
 37    }
 38
 39    // Line-of-sight unit vector.
 1440    Vector3 losRHat = relativePosition / distance;
 41    // Line-of-sight rate vector.
 1442    Vector3 losVHat = normalRelativeVelocity / distance;
 1443    Vector3 losRotation = Vector3.Cross(losRHat, losVHat);
 44
 45    // The closing velocity is negative because the closing velocity is opposite to the range rate.
 1446    float closingVelocity = -relativeTransformation.Velocity.Range;
 1447    float closingSpeed = Mathf.Abs(closingVelocity);
 48    // Set the turn factor, which is equal to the closing velocity by default.
 1449    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.
 1553    if (closingVelocity < 0) {
 154      turnFactor = Mathf.Max(1f, closingSpeed) * _strongTurnFactor;
 155    }
 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.
 1458    bool isAbeam = Mathf.Abs(Vector3.Dot(Agent.Velocity.normalized, relativePosition.normalized)) <
 59                   _abeamThreshold;
 1860    if (isAbeam) {
 461      turnFactor = Mathf.Max(1f, closingSpeed) * _strongTurnFactor;
 462      float clampedLosRate = Mathf.Max(losRotation.magnitude, _minLosRate);
 463      losRotation = losRotation.normalized * clampedLosRate;
 464    }
 1465    return Gain * turnFactor * Vector3.Cross(losRotation, Agent.Velocity.normalized);
 1466  }
 67}