Files
lab1/processors/timeline_agents/air_combat_maneuvers.txt

684 lines
24 KiB
Plaintext
Raw Permalink Normal View History

2025-09-12 15:20:28 +08:00
# ****************************************************************************
# CUI
#
# The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
#
# The use, dissemination or disclosure of data in this file is subject to
# limitation or restriction. See accompanying README and LICENSE for details.
# ****************************************************************************
# * * ************************************** * *
# * ****** Demonstration input file ****** *
# * ****** UNCLASSIFIED ****** *
# * * ************************************** * *
# --------------------------------------------------------------
# This files defines air combat maneuvers to be used
# with a timeline agent. Maenuvers defined are:
# - engage/re-engage/pursue/push maneuver
# - beam maneuver
# - crank maneuver
# - drag maneuver
# Also included is a TurnHeading method that provides a new
# relative heading to turn to based on maneuver conditions.
# --------------------------------------------------------------
script_interface
script_debug_writes off
# Returns a relative heading (degrees) to turn
# relative to a track based on inputs
# aPlatform - the platform to get the new relative heading for
# aTrack - the track the new relative heading is in relation to
# aTurnAngle - the angle in degrees to turn relative to the track
# aTolerance - the values in degrees (+/-) within which the Platform will
# already be considered to be aTurnAngle from aTrack
script double TurnHeading(WsfPlatform aPlatform,
WsfTrack aTrack,
double aTurnAngle,
double aTolerance)
# Variable to store new relative heading return value
# Defualt to 0 degrees, no turn
double heading = 0;
if (aPlatform.IsValid() && aTrack.IsValid())
{
double relativeBearing = aPlatform.RelativeBearingTo(aTrack);
writeln_d("T= ", TIME_NOW);
writeln_d(" ", aPlatform.Name(), " heading: ", aPlatform.Heading(), " deg.");
writeln_d(" ", aPlatform.Name(), " relative bearing to track: ", relativeBearing, " deg.");
# Check that relative bearing is not already within tolerance of
# aTurnAngle and no need to change heading
if (!Math.AngleIsBetween(Math.Fabs(relativeBearing), aTurnAngle - aTolerance, aTurnAngle + aTolerance))
{
if (relativeBearing > 0 )
{
# Turn left
heading = -aTurnAngle + relativeBearing;
}
else
{
# Turn right
heading = aTurnAngle + relativeBearing;
}
# Because we are adding angle, make sure relative heading is valid
heading = Math.NormalizeAngleMinus180_180(heading);
writeln_d(" ", aPlatform.Name(), " new relative heading: ", heading, " deg.");
}
else
{
writeln_d(" ", aPlatform.Name(), " already within ",
aTolerance, " deg of ", aTurnAngle, " deg to track." );
}
}
return heading;
end_script
# Calculate the relative positioning of aPlatform and aTrack
# Borrowed from common_platform_script
# aPlatform - the platform of interest for relative position
# aTrack - the track to determine the relative position to
# with respect to the platform
# aAngleTolerance - angle in degrees used to bound how close
# two heading are to be considered the same
# returns a string indicating the relative positioning
# (head-to-head, head-to-tail, etc.)
script string GetPositioning(WsfPlatform aPlatform, WsfTrack aTrack, double aAngleTolerance)
# Are we heading the same direction?
bool sameHeading = false;
bool oppHeading = false;
bool oppHeadingValid = aTrack.HeadingValid();
double headingDiff = MATH.Fabs(MATH.NormalizeAngleMinus180_180(aPlatform.Heading() - aTrack.Heading()));
if (oppHeadingValid && headingDiff < aAngleTolerance)
{
sameHeading = true;
}
if (oppHeadingValid && headingDiff > (180 - aAngleTolerance))
{
oppHeading = true;
}
# Is either one of us pointing at the other?
# Apparently bearing is always valid (?)
bool pointingMeYou = false;
bool pointingYouMe = false;
double pMeYou = MATH.Fabs(aPlatform.RelativeBearingTo(aTrack));
double pYouMe = MATH.Fabs(aTrack.RelativeBearingTo(aPlatform));
if (pMeYou < aAngleTolerance)
{
pointingMeYou = true;
}
if (pYouMe < aAngleTolerance)
{
pointingYouMe = true;
}
# Put them together and we've got relative positioning
string positioning = "";
if (sameHeading && pointingMeYou)
{
positioning = "head-to-tail";
}
else if (sameHeading && pointingYouMe)
{
positioning = "tail-to-head";
}
else if (oppHeading && (pointingMeYou || pointingYouMe))
{
positioning = "head-to-head";
}
else if (pointingMeYou && pointingYouMe)
{
positioning = "head-to-head";
}
else if (oppHeading)
{
positioning = "tail-to-tail";
}
else if (pointingMeYou)
{
positioning = "me-facing-target";
}
else if (pointingYouMe)
{
positioning = "target-facing-me";
}
else
{
positioning = "none";
}
# Debug, in case angle math is bad
# writeln_d(" ", aPlatform.Name(), " to ", aTrack.TargetName(), ", headingDiff: ", headingDiff, ", pMeYou: ", pMeYou, ", pYouMe: ", pYouMe);
# writeln_d(" meHeading: ", aPlatform.Heading(), ", youHeading: ", aTrack.Heading());
# writeln_d(" sameHeading: ", sameHeading, ", oppHeading: ", oppHeading,
# ", pointingMeYou: ", pointingMeYou, ", pointingYouMe: ", pointingYouMe,
# ", positioning: ", positioning);
return positioning;
end_script
# Commands passed in platform to do an engage maneuver
# Relative to passed in track
# Mode (pure, lead) input string determines if mode will be
# pure pursuit or lead to predicted intercept point
# Return true if engage maneuver command is successful
script bool Engage(WsfPlatform aPlatform, WsfTrack aTrack, string aMode, double aSpeed, double aAlt, double aGs)
# Variables used in the script to generate the maneuver.
# Change these to modify the behavior.
double leadTime = 15.0; #sec
WsfGeoPoint targetPoint = WsfGeoPoint();
if (!aPlatform.IsValid() || !aTrack.IsValid())
{
return false;
}
# Determine location to fly toward based on pursuit mode
if (aMode == "intercept")
{
WsfWaypoint wpt = WsfWaypoint();
double tti = aPlatform.InterceptLocation3D(aTrack, wpt);
if (tti > 0.0)
{
targetPoint = wpt.Location();
}
else
{
targetPoint = aTrack.LocationAtTime(TIME_NOW + leadTime);
}
}
else if (aMode == "lead")
{
targetPoint = aTrack.LocationAtTime(TIME_NOW + leadTime);
}
else #if (aMode == "pure")
{
# All other input will be considered 'pure' pursuit
targetPoint = aTrack.LocationAtTime(TIME_NOW);
}
double relativeBearing = aPlatform.RelativeBearingTo(targetPoint);
# Fly determined course
double radialAccel = aGs * Earth.ACCEL_OF_GRAVITY();
bool ok = aPlatform.TurnToRelativeHeading(relativeBearing, radialAccel);
ok = ok && aPlatform.GoToSpeed(aSpeed);
ok = ok && aPlatform.GoToAltitude(aAlt);
return ok;
end_script
# Commands passed in platform to do a fire maneuver
# Relative to passed in track
# Return true if fire maneuver command is successful
script bool FireManeuver(WsfPlatform aPlatform, WsfTrack aTrack)
# Variables used in the script to generate the maneuver.
# Change these to modify the behavior.
# TODO make method arguments?
double fireMach = 1.5; # Speed to fire weapon
double pitchAngle = 15.0; # Degrees of pitch up (loft shot)
double distance = 100000.0; # Distance in meters to new altitude point
if (!aPlatform.IsValid() || !aTrack.IsValid())
{
return false;
}
# Determine heading toward track
double relativeBearing = aPlatform.RelativeBearingTo(aTrack);
# Determine new altitude to give desired pitch angle
# Arbitrary point that is pitch angle up, 100 km distant
double altitudeDelta = distance * Math.Tan(pitchAngle);
double newAltitude = aPlatform.Altitude() + altitudeDelta;
# Determine an approximate climb rate based on
# desired angle and new point
double speed = aPlatform.Speed();
double time = distance / speed;
double climbRate = altitudeDelta / time;
# Fly determined course
bool ok = aPlatform.TurnToRelativeHeading(relativeBearing);
ok = ok && aPlatform.GoToMachNumber(fireMach);
ok = ok && aPlatform.GoToAltitude(newAltitude, climbRate);
return ok;
end_script
# Commands passed in platform to do a beam maneuver
# Relative to passed in track
# Return final heading if crank maneuver command is successful
# or -1 if unsuccessful
script double Beam(WsfPlatform aPlatform, WsfTrack aTrack)
# Variables used in the script to generate the maneuver.
# Change these to modify the behavior.
# Angle in degrees to turn to get to "beam" on track.
double beamAngle = 90;
# Min/max angle in degrees that aircraft needs to be
# between relative to track to be considered on "beam"
double beamTolerance = 1.5;
if (!aPlatform.IsValid() || !aTrack.IsValid())
{
return false;
}
# Heading when we have finished maneuver
double finalHeading = 0;
# Determine new heading
if (aTrack.HeadingValid())
{
# Check for condition when we know track's heading
# this gives a more accurate "beam" to threat course
if (aPlatform.RelativeBearingTo(aTrack) > 0)
{
# Turn left
finalHeading = aTrack.Heading() + beamAngle;
}
else
{
# Turn right
finalHeading = aTrack.Heading() - beamAngle;
}
}
else
{
# Determine a relative heading based only on the track's location
double relativeHeading = TurnHeading(aPlatform, aTrack, beamAngle, beamTolerance);
finalHeading = Math.NormalizeAngle0_360(aPlatform.Heading() + relativeHeading);
}
# Turn to new heading, maintaining speed and altitude
bool ok = aPlatform.TurnToHeading(finalHeading);
if (!ok)
{
finalHeading = -1.0;
}
return finalHeading;
end_script
# Commands passed in platform to do a crank maneuver
# Relative to passed in track
# Return final heading if drag maneuver command is successful
# or -1 if unsuccessful
script double Crank(WsfPlatform aPlatform, WsfTrack aTrack, double crankAngle, double aSpeed)
# Variables used in the script to generate the maneuver.
# Change these to modify the behavior.
# TODO make method arguments?
# Angle in degrees to offset track. Should be close to,
# but not at, max radar azimuth
#double crankAngle = 35;
# Min/max angle in degrees that aircraft needs to be
# between relative to track to be considered cranking
double crankTolerance = 1.5;
if (!aPlatform.IsValid() || !aTrack.IsValid())
{
return false;
}
double relativeHeading = TurnHeading(aPlatform, aTrack, crankAngle, crankTolerance);
# Heading when we have finished maneuver
double finalHeading = Math.NormalizeAngleMinus180_180(aPlatform.Heading() + relativeHeading);
# Turn to new heading, maintaining speed and altitude
bool ok = aPlatform.TurnToRelativeHeading(relativeHeading);
ok = ok && aPlatform.GoToSpeed(aSpeed);
if (!ok)
{
finalHeading = -1.0;
}
return finalHeading;
end_script
# Commands passed in platform to do a drag maneuver
# Relative to passed in track
# aSpeed - speed (m/s) to drag at
# aAlt - Altitude to offset from current altitude
# Return final heading if drag maneuver command is successful
# or -1 if unsuccessful
script double Drag(WsfPlatform aPlatform, WsfTrack aTrack, double aSpeed, double aAlt, double aGs)
# Variables used in the script to generate the maneuver.
# Change these to modify the behavior.
# TODO make method arguments?
# Flight parameters when dragging for speed (Mach) and altitude offest (meters) from start
double dragMach = 1.1;
# Angle in degrees to turn to drag on track.
double dragAngle = 179;
# Min/max angle in degrees that aircraft needs to be
# between relative to track to be considered dragging
double dragTolerance = 5;
if (!aPlatform.IsValid() || !aTrack.IsValid())
{
return false;
}
# Heading when we have finished maneuver
double finalHeading = 0;
# Determine new heading for drag maneuver
string positioning = GetPositioning(aPlatform, aTrack, dragTolerance);
if (aTrack.HeadingValid() &&
(positioning == "tail-to-head" ||
positioning == "head-to-head" ||
positioning == "target-facing-me")
)
{
# Check for condition when we know track's heading and
# track is facing platform.
# This gives a more accurate "drag" to threat course
finalHeading = aTrack.Heading();
}
else
{
# Don't know track heading or not facing platform, turn
# based on relative bearing to track location
double relativeHeading = TurnHeading(aPlatform, aTrack, dragAngle, dragTolerance);
finalHeading = Math.NormalizeAngle0_360(aPlatform.Heading() + relativeHeading);
}
# Fly new course
double radialAccel = aGs * Earth.ACCEL_OF_GRAVITY();
bool ok = aPlatform.TurnToHeading(finalHeading, radialAccel);
ok = ok && aPlatform.GoToSpeed(aSpeed);
ok = ok && aPlatform.GoToAltitude(aAlt);
if (!ok)
{
finalHeading = -1.0;
}
return finalHeading;
end_script
# Do a 180 degree turn in the specified direction,
# at specified mach, maintaining altitude
script double TurnDirection(WsfPlatform aPlatform, string aDirection, double aSpeed, double aGs)
# Angle in degrees to turn.
double turnAngle = 179.5;
# Heading when we have finished maneuver
double finalHeading = 0;
if (aDirection == "left")
{
finalHeading = Math.NormalizeAngle0_360(aPlatform.Heading() - turnAngle);
}
else
{ # "right"
finalHeading = Math.NormalizeAngle0_360(aPlatform.Heading() + turnAngle);
}
# Fly new course
double radialAccel = aGs * Earth.ACCEL_OF_GRAVITY();
bool ok = aPlatform.TurnToHeading(finalHeading, radialAccel);
ok = ok && aPlatform.GoToSpeed(aSpeed);
ok = ok && aPlatform.GoToAltitude(aPlatform.Altitude());
if (!ok)
{
finalHeading = -1.0;
}
return finalHeading;
end_script
# Commands passed in platform to do a post hole maneuver
# Direction (left/right) controls direction of post hole
# Return final heading if drag maneuver command is successful
# or -1 if unsuccessful
script double PostHole(WsfPlatform aPlatform, string aDirection)
# Distance in meters to offset the range to the post hole waypoint
double offsetRange = 3000;
# Angle down (deg) to offset post hole waypoint
double offsetElevation = -75.0;
if (!aPlatform.IsValid())
{
return false;
}
# Use left/right direction to determine the offset bearing for
# the post hole waypoint. Heading + 120 deg will put the point
# slightly behind current location
double offsetBearing = 0;
if (aDirection == "left")
{
offsetBearing = Math.NormalizeAngleMinus180_180(aPlatform.Heading() + 120);
}
else
{
offsetBearing = Math.NormalizeAngleMinus180_180(aPlatform.Heading() - 120);
}
# Build point to fly to for post hole
WsfGeoPoint pt = aPlatform.Location();
pt.OffsetRBE(offsetRange, offsetBearing, offsetElevation);
# Create waypoint so we can specify final heading/speed
WsfWaypoint waypoint = WsfWaypoint();
waypoint.SetHeading(aPlatform.Heading());
waypoint.SetSpeed(aPlatform.Speed());
waypoint.SetLocation(pt);
# Build route from post hole waypoint
WsfRoute newRoute = WsfRoute();
newRoute.Append(waypoint);
# Heading when we have finished maneuver
double finalHeading = aPlatform.Heading();
bool ok = aPlatform.FollowRoute(newRoute);
if (!ok)
{
finalHeading = -1.0;
}
return finalHeading;
end_script
script double GetTurnRateLimit(WsfPlatform aPlatform)
Array<double> turnRadiusArray = aPlatform.Mover().PropertyDouble("turn_radius");
double turnRateLimit = 0;
if (turnRadiusArray.Size() > 0)
{
double turnRate = turnRadiusArray[0];
double speed = aPlatform.Speed();
turnRateLimit = speed/turnRate;
turnRateLimit = turnRateLimit * MATH.DEG_PER_RAD();
}
return turnRateLimit;
end_script
script double GetDefaultG(WsfPlatform aPlatform)
Array<double> defaultAccelArray = aPlatform.Mover().PropertyDouble("default_radial_acceleration");
if (defaultAccelArray.Size() > 0)
{
return defaultAccelArray[0];
}
return 0.0;
end_script
script double TimeToPerformBeam(WsfPlatform aPlatform, WsfTrack aTrack)
# Variables used in the script to generate the maneuver.
# Change these to modify the behavior.
# Angle in degrees to turn to get to "beam" on track.
double beamAngle = 90;
# Min/max angle in degrees that aircraft needs to be
# between relative to track to be considered on "beam"
double beamTolerance = 1.5;
if (!aPlatform.IsValid() || !aTrack.IsValid())
{
return -1.0;
}
# Heading when we have finished maneuver
double finalHeading = 0;
# Determine new heading
if (aTrack.HeadingValid())
{
# Check for condition when we know track's heading
# this gives a more accurate "beam" to threat course
if (aPlatform.RelativeBearingTo(aTrack) > 0)
{
# Turn left
finalHeading = aTrack.Heading() + beamAngle;
}
else
{
# Turn right
finalHeading = aTrack.Heading() - beamAngle;
}
}
else
{
# Determine a relative heading based only on the track's location
double relativeHeading = TurnHeading(aPlatform, aTrack, beamAngle, beamTolerance);
finalHeading = Math.NormalizeAngle0_360(aPlatform.Heading() + relativeHeading);
}
//calculate time to perform the beam given the calculated heading
#RadialAccel = Speed^2/Radius
double RadialAccel = GetDefaultG(aPlatform) * Earth.ACCEL_OF_GRAVITY();
if (RadialAccel < 2.0)
{
RadialAccel = 2.0 * Earth.ACCEL_OF_GRAVITY();
}
double TurnAzimuth = MATH.Fabs(finalHeading);
double Speed = aPlatform.Speed();
double Radius = Speed * Speed / RadialAccel;
double TurningCircleCircumference = 2.0 * MATH.PI() * Radius;
double TurningArc = (TurnAzimuth/360) * TurningCircleCircumference;
double TimeToTurn = TurningArc / Speed;
return TimeToTurn;
end_script
script double TimeToPerformCrank(WsfPlatform aPlatform, WsfTrack aTrack)
# Variables used in the script to generate the maneuver.
# Change these to modify the behavior.
# TODO make method arguments?
# Angle in degrees to offset track. Should be close to,
# but not at, max radar azimuth
double crankAngle = 35;
# Min/max angle in degrees that aircraft needs to be
# between relative to track to be considered cranking
double crankTolerance = 1.5;
if (!aPlatform.IsValid() || !aTrack.IsValid())
{
return -1.0;
}
double relativeHeading = TurnHeading(aPlatform, aTrack, crankAngle, crankTolerance);
# Heading when we have finished maneuver
double finalHeading = Math.NormalizeAngleMinus180_180(aPlatform.Heading() + relativeHeading);
double RadialAccel = GetDefaultG(aPlatform) * Earth.ACCEL_OF_GRAVITY();
if (RadialAccel < 2.0)
{
RadialAccel = 2.0 * Earth.ACCEL_OF_GRAVITY();
}
double TurnAzimuth = MATH.Fabs(finalHeading);
double Speed = aPlatform.Speed();
double Radius = Speed * Speed / RadialAccel;
double TurningCircleCircumference = 2.0 * MATH.PI() * Radius;
double TurningArc = (TurnAzimuth/360) * TurningCircleCircumference;
double TimeToTurn = TurningArc / Speed;
return TimeToTurn;
end_script
script double TimeToPerformDrag(WsfPlatform aPlatform, WsfTrack aTrack)
# Variables used in the script to generate the maneuver.
# Change these to modify the behavior.
# TODO make method arguments?
# Flight parameters when dragging for speed (Mach) and altitude offest (meters) from start
double dragMach = 1.1;
double dragAltitudeOffset = -1000;
# Angle in degrees to turn to drag on track.
double dragAngle = 179;
# Min/max angle in degrees that aircraft needs to be
# between relative to track to be considered dragging
double dragTolerance = 5;
if (!aPlatform.IsValid() || !aTrack.IsValid())
{
return -1.0;
}
# Heading when we have finished maneuver
double finalHeading = 0;
# Determine new heading for drag maneuver
string positioning = GetPositioning(aPlatform, aTrack, dragTolerance);
if (aTrack.HeadingValid() &&
(positioning == "tail-to-head" ||
positioning == "head-to-head" ||
positioning == "target-facing-me")
)
{
# Check for condition when we know track's heading and
# track is facing platform.
# This gives a more accurate "drag" to threat course
finalHeading = aTrack.Heading();
}
else
{
# Don't know track heading or not facing platform, turn
# based on relative bearing to track location
double relativeHeading = TurnHeading(aPlatform, aTrack, dragAngle, dragTolerance);
finalHeading = Math.NormalizeAngle0_360(aPlatform.Heading() + relativeHeading);
}
double RadialAccel = GetDefaultG(aPlatform) * Earth.ACCEL_OF_GRAVITY();
if (RadialAccel < 2.0)
{
RadialAccel = 2.0 * Earth.ACCEL_OF_GRAVITY();
}
double TurnAzimuth = MATH.Fabs(finalHeading);
double Speed = aPlatform.Speed();
double Radius = Speed * Speed / RadialAccel;
double TurningCircleCircumference = 2.0 * MATH.PI() * Radius;
double TurningArc = (TurnAzimuth/360) * TurningCircleCircumference;
double TimeToTurn = TurningArc / Speed;
return TimeToTurn;
end_script
script void Offset(WsfPlatform flyer, WsfGeoPoint target, double angle, double alt, double speed, double gees)
angle = MATH.NormalizeAngleMinus180_180(angle);
double offsetTrueBearing = flyer.TrueBearingTo(target) + angle;
double radialAccel = gees * Earth.ACCEL_OF_GRAVITY();
double climbRate = MATH.Sin(30) * speed;
bool ok = flyer.GoToSpeed(speed, 9999, true);
ok = ok && flyer.GoToAltitude(alt, climbRate, true);
ok = ok && flyer.TurnToHeading(offsetTrueBearing, radialAccel);
end_script
end_script_interface