This commit is contained in:
2025-09-12 15:20:28 +08:00
commit 3257a14c32
449 changed files with 388780 additions and 0 deletions

View File

@@ -0,0 +1,683 @@
# ****************************************************************************
# 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

View File

@@ -0,0 +1,271 @@
# ****************************************************************************
# 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.
# ****************************************************************************
include_once weapon_defs.txt
behavior engage_on_timeline
script_debug_writes off
script_variables
//**********************************************************************//
//** platform / agent specific shooting parameters **//
//**********************************************************************//
#bool mCoopEngageOne = false;
#bool mCoopEngageOneFlightOnly = false;
# double mDegradedFiringAngle = 55.0; //negative if not valid
# double mDegradedPercentRange = 0.50; //range constraint if past degraded firing angle
# //specify orientation limits for shooting
# double mMaxFiringRollAngle = 10.0; //dont shoot if rolled more/less than this
# double mMaxFiringPitchAngle = 15.0; //dont shoot if pitched more than this
# double mMinFiringPitchAngle = -10.0; //dont shoot if pitched less than this
//**********************************************************************//
//** threat specific shooting parameters **//
//**********************************************************************//
//require different track qualities to fire on different kinds of threats
# double DefaultRequiredTrackQuality = 0.49;
# Map<string, double> ThreatTypeRequiredTrackQuality = Map<string, double>();
# ThreatTypeRequiredTrackQuality["bomber"] = 0.49;
# ThreatTypeRequiredTrackQuality["fighter"] = 0.49;
//fire off different salvos at different types of threats
# int DefaultAirSalvo = 1;
# int DefaultGndSalvo = 1;
# Map<string, int> ThreatTypeSalvo = Map<string, int>();
# ThreatTypeSalvo["sam"] = 2;
# ThreatTypeSalvo["ship"] = 2;
# ThreatTypeSalvo["bomber"] = 2;
# ThreatTypeSalvo["fighter"] = 1;
# ThreatTypeSalvo["FIRE_CONTROL"] = 1;
# ThreatTypeSalvo["primary_target"] = 2;
# ThreatTypeSalvo["secondary_target"] = 2;
//**********************************************************************//
//** weapon + threat specific shooting parameters **//
//**********************************************************************//
//specify an Rmax based on which weapon used and which threat engaged
double DefaultPercentRangeMax = 0.80; // don't launch unless within this percent of Rmax
double DefaultPercentRangeMin = 1.20; // don't launch unless beyond this percent of Rmin
Map<string, Map<string, double>> WeaponThreatRmaxMap = Map<string, Map<string, double>>();
WeaponThreatRmaxMap["base_weapon"] = Map<string, double>();
WeaponThreatRmaxMap["base_weapon"].Set("fighter", 0.80);
end_script_variables
# script int GetSalvoForThreat(WsfTrack track)
# #writeln_d("checking salvo size for category: ", category);
# #WsfPlatform plat = WsfSimulation.FindPlatform( track.TargetIndex() );
# WsfPlatform plat = WsfSimulation.FindPlatform( track.TargetName() );
# if (plat.IsValid())
# {
# foreach( string aCategory : int salvo in ThreatTypeSalvo )
# {
# if( plat.CategoryMemberOf( aCategory ) )
# {
# writeln_d("salvo for type ", aCategory, " = ", salvo);
# return salvo;
# }
# }
# }
# #extern string GetTargetDomain(WsfTrack);
# string sTargetDomain = GetTargetDomain(track);
# if ( (sTargetDomain == "LAND") || (sTargetDomain == "SURFACE") )
# {
# return DefaultGndSalvo;
# }
# return DefaultAirSalvo;
# end_script
# script double GetRequiredTrackQualityForThreat(WsfTrack threat)
# writeln_d("checking required TQ for track: ", threat.TargetName());
# WsfPlatform plat = WsfSimulation.FindPlatform( threat.TargetName() );
# if (plat.IsValid())
# {
# foreach( string aCategory : double quality in ThreatTypeRequiredTrackQuality )
# {
# if( plat.CategoryMemberOf( aCategory ) )
# {
# writeln_d("TQ for type ", aCategory, " = ", quality);
# return quality;
# }
# }
# }
# return DefaultRequiredTrackQuality;
# end_script
script double GetLaunchPercentRangeMaxOnThreat(string weaponName, WsfTrack threat)
WsfPlatform plat = WsfSimulation.FindPlatform( threat.TargetName() );
if (plat.IsValid())
{
if (WeaponThreatRmaxMap.Exists(weaponName))
{
Map<string, double> categoryRangeMap = WeaponThreatRmaxMap.Get(weaponName);
foreach (string aCategory : double percent in categoryRangeMap)
{
if( plat.CategoryMemberOf( aCategory ) )
{
return percent;
}
}
}
}
return DefaultPercentRangeMax;
end_script
#on_init
#end_on_init
precondition
#writeln_d("precondition engage-target");
if (!PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
{
writeln_d("behavior engage... not on quantum tasker processor");
return Failure("behavior not attached to a quantum tasker processor!");
}
# todo - try using a state machine to setup the shot ??
# double pitch = PLATFORM.Pitch();
# if (MATH.Fabs(PLATFORM.Roll()) > mMaxFiringRollAngle ||
# pitch > mMaxFiringPitchAngle ||
# pitch < mMinFiringPitchAngle)
# {
# string msgStr = write_str(" ", PLATFORM.Name(), " orientation too far off to fire! (roll or pitch)");
# writeln_d(msgStr);
# //PLATFORM.Comment(msgStr);
# return Failure(msgStr);
# }
WsfQuantumTaskerProcessor proc = (WsfQuantumTaskerProcessor)PROCESSOR;
# WsfTaskList tasks = proc.TasksReceivedOfType("WEAPON");
# if (tasks.Count() > 0)
# {
# writeln_d("behavior engage precondition passes!");
# return true;
# }
# writeln_d("no weapon task target to shoot at!");
if (GetTimelineTask(proc).IsValid())
{
writeln_d("behavior engage precondition passes!");
return true;
}
writeln_d("no timeline task to engage!");
return Failure("no timeline task to engage!");
end_precondition
execute
writeln_d(PLATFORM.Name(), " executing engage-target, T=", TIME_NOW);
#extern WsfTrack GetTrackByName(WsfPlatform, string);
#check all possible targets on all channels
########################################################################
### fire on any pursue-target jobs we are assigned to
########################################################################
WsfQuantumTaskerProcessor proc = (WsfQuantumTaskerProcessor)PROCESSOR;
WsfTask timeline = GetTimelineTask(proc);
if (timeline.IsValid() && timeline.AuxDataExists("targets"))
{
Array<WsfTrack> targets = (Array<WsfTrack>)timeline.AuxDataObject("targets");
foreach (WsfTrack target in targets)
{
if (!target.IsValid())
{
continue;
}
#do we need master track?
WsfLocalTrack masterTarget = GetMasterTrackByName(PLATFORM, target.TargetName());
if (!masterTarget.IsValid())
{
continue;
}
# if (mCoopEngageOne == false)
# {
# WsfLocalTrack targetLocalTrack = (WsfLocalTrack)targetTrack;
# if (targetLocalTrack.IsValid())
# {
# if(!targetLocalTrack.ContributorOf(PLATFORM) &&
# !targetLocalTrack.IsPredefined())
# {
# writeln_d(" FAIL: Not able to coop engage! ", PLATFORM.Name(), " targeting ",targetTrack.TargetName(), ". NumContributors: ", targetLocalTrack.NumContributors() );
# return;
# }
# }
# }
#
# writeln_d (" targetTrack.TrackQuality == ", targetTrack.TrackQuality());
# if (targetTrack.TrackQuality() < GetRequiredTrackQualityForThreat(targetTrack))
# {
# writeln_d(" FAIL: track quality not good enough to fire on target");
# return;
# }
if ((PLATFORM.WeaponsPendingFor(masterTarget.TrackId()) + PLATFORM.WeaponsActiveFor(masterTarget.TrackId())) > 0)
{
writeln_d("already have weapons assigned for target: ", masterTarget.TargetName());
continue;
}
WsfWeapon weapon;
bool weaponUsable = false;
#first weapon found will be used
for (int i=0; i < PLATFORM.WeaponCount(); i+=1)
{
weapon = PLATFORM.WeaponEntry(i);
writeln_d("checking if weapon ", weapon.Name(), " is usable.");
if (WeaponCapableAvailableAgainstThreat(weapon, masterTarget) &&
InRangeToFire(PLATFORM, weapon, masterTarget, GetLaunchPercentRangeMaxOnThreat(weapon.Name(), masterTarget), DefaultPercentRangeMin))
{
weaponUsable = true;
break;
}
}
if (weaponUsable == false)
{
writeln_d("no usable weapon found!");
continue;
}
#int salvoCount = GetSalvoForThreat(targetTrack);
#writeln_d(" salvo count for ", targetTrack, " is: ", salvoCount);
int salvoCount = 1;
bool launched = false;
if (weapon.IsTurnedOn())
{
writeln_d(" Attempting launch at ", masterTarget.TargetName());
# if (salvoCount > 1)
# {
# writeln_d("FIRING SALVO AT ", masterTarget.TargetName());
# launched = weapon.FireSalvo(masterTarget, salvoCount);
# }
# else
# {
# writeln_d("FIRING AT ", masterTarget.TargetName());
# launched = weapon.Fire(masterTarget);
# }
launched = weapon.FireSalvo(masterTarget, salvoCount);
}
writeln_d(" launched == ", launched, ", weapon: ", weapon.Name());
if(launched == false)
{
writeln_d(" ", PLATFORM.Name(), " could NOT fire at track: ", masterTarget.TargetName(), " at time: ", TIME_NOW);
}
}
}
end_execute
end_behavior

View File

@@ -0,0 +1,470 @@
# ****************************************************************************
# 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.
# ****************************************************************************
behavior evade
script_debug_writes off
script_variables
double cDEFAULT_ALTITUDE = 9144; // ~30,000 feet
double mEngagementAggressiveness = 0.4; // value in range [0, 1]. 1 is suicidal, 0 is very cautious.
// used by behavior_in_danger, behavior_evade, & behavior_disengage.
//**********************************************************************//
//** debugging parameters **//
//**********************************************************************//
bool mDrawNearestThreat = false;
bool mDrawEvasionVector = false;
//**********************************************************************//
//** control / mode of operation parameters **//
//**********************************************************************//
double weightPeersForEvade = 0.25; //percentage to scale peers influence for evasion vector
bool mWobbleLeftRight = true;
bool mWobbleUpDown = true;
double cFAST_UPDATE_INTERVAL = 0.25;
double cSLOW_UPDATE_INTERVAL = 2.0;
//**********************************************************************//
//** flying parameters, for for evasive manuevering **//
//**********************************************************************//
double cNORMAL_SPEED = 200.0; // m/s
double cEVADE_SPEED = 1000.0; // m/s (faster than a speeding bullet...M3.0+)
double mAltitudeMin = 1000.0; //meters
double mAltitudeMax = 20000.0; //meters
double mAltitudeToDiveEvade = 1500.0; //distance to dive (meters) (~5000 ft)
double mBankAngleForEvading = 45.0; //banks left & right, back & forth, at this angle, while evading
double mMaxClimbRate = 500.0; // meters/second
double mVerticalAccel = 1.5 * Earth.ACCEL_OF_GRAVITY(); // meters/second^2
//**********************************************************************//
//********* VARIABLES BELOW THIS LINE ARE NOT FOR USER EDITING *********//
//**********************************************************************//
double mLastTime = 0.0;
double mClimbRate = 0.0;
Array<WsfPlatform> mIncoming = Array<WsfPlatform>();
WsfDraw mDraw = WsfDraw();
bool mDiveDownFlag = true;
bool mBankLeftFlag = true;
end_script_variables
script WsfThreatProcessor GetThreatProcessor()
for (int i=0; i<PLATFORM.ProcessorCount(); i+=1)
{
WsfProcessor proc = PLATFORM.ProcessorEntry(i);
if (proc.IsValid() && proc.IsA_TypeOf("WSF_THREAT_PROCESSOR"))
{
return (WsfThreatProcessor)proc;
}
}
WsfThreatProcessor empty;
return empty;
end_script
script bool Fly(WsfPlatform flyer, double heading, double altitude, double speed)
WsfGeoPoint pt = flyer.Location();
pt.Extrapolate(heading, 1852*3);
pt.Set(pt.Latitude(), pt.Longitude(), MATH.Max(altitude, mAltitudeMin));
if (mDrawEvasionVector == true)
{
mDraw.SetLayer("behavior_evade");
mDraw.SetDuration(PROCESSOR.UpdateInterval());
mDraw.SetColor(0,1,0);
mDraw.SetLineSize(1);
mDraw.SetLineStyle("dash_dot");
mDraw.BeginLines();
mDraw.Vertex(PLATFORM.Location());
mDraw.Vertex(pt);
mDraw.End();
}
if (flyer.Mover().IsValid())
{
if (flyer.Mover().IsA_TypeOf("WSF_6DOF_MOVER"))
{
##flyer.GoToSpeed(speed);
##flyer.GoToAltitude(MATH.Max(altitude, mAltitudeMin), 200);
##flyer.TurnToHeading(heading, 500);
#flyer.GoToSpeed(speed);
#flyer.GoToLocation(pt);
flyer.GoToSpeed(speed);
double altRate = flyer.Speed() * MATH.Sin(40.0);
flyer.GoToAltitude(pt.Altitude(), altRate);
flyer.TurnToHeading(flyer.TrueBearingTo(pt), 500);
return true;
}
else if (flyer.Mover().IsA_TypeOf("WSF_AIR_MOVER"))
{
double delta = TIME_NOW - mLastTime;
mLastTime = TIME_NOW;
if (delta > cSLOW_UPDATE_INTERVAL)
{
delta = cSLOW_UPDATE_INTERVAL;
}
double deltaV = mVerticalAccel * delta;
if (altitude > PLATFORM.Altitude())
{
if (mClimbRate < 0.0)
{
//increase climb rate to a positive value (must keep pursuing a lower altitude though)
altitude = mAltitudeMin;
mClimbRate = mClimbRate + deltaV;
}
else if (mClimbRate < mMaxClimbRate)
{
//increase climb rate to max value (can pursue target altitude now)
mClimbRate = mClimbRate + deltaV;
}
}
else if (altitude < PLATFORM.Altitude())
{
if (mClimbRate > 0.0)
{
//decrease climb rate to a negative value (must keep pursuing a higher altitude though)
altitude = 9999999;
mClimbRate = mClimbRate - deltaV;
}
else if (mClimbRate > -mMaxClimbRate)
{
//decrease climb rate to max value (can pursue target altitude now)
mClimbRate = mClimbRate - deltaV;
}
}
double climbRateUsed = MATH.Fabs(mClimbRate);
flyer.GoToSpeed(speed);
flyer.GoToAltitude(altitude, climbRateUsed);
flyer.TurnToHeading(heading);
return true;
}
}
writeln_d(flyer.Name(), " aiai error: unknown or invalid mover for flight");
return false;
end_script
precondition
writeln_d(PLATFORM.Name(), " precondition evade, T=", TIME_NOW);
//always evade incoming weapons
int ownshipWeaponsIncoming = 0;
WsfProcessor proc = PLATFORM.Processor("incoming_threats");
if (proc.IsValid() && proc.ScriptExists("ThreatProcessorWeaponsIncoming"))
{
writeln_d("using incoming_threats threat processor!!!");
mIncoming = (Array<WsfPlatform>)(proc.Execute("ThreatProcessorWeaponsIncoming"));
ownshipWeaponsIncoming = mIncoming.Size();
}
else
{
writeln_d("using standard WSF_THREAT_PROCESSOR!!!");
mIncoming.Clear(); ownshipWeaponsIncoming = 0;
WsfThreatProcessor threatProc = GetThreatProcessor();
if (!threatProc.IsNull() && threatProc.IsValid())
{
Array<WsfTrackId> threatIds = threatProc.Threats();
foreach(WsfTrackId id in threatIds)
{
WsfTrack track = PLATFORM.MasterTrackList().Find(id);
if (track.IsValid())
{
mIncoming.PushBack(track.Target());
}
}
ownshipWeaponsIncoming = mIncoming.Size();
}
}
if (ownshipWeaponsIncoming > 0)
{
//we now have weapons incoming, check to see if we are currently guiding any missiles with an uplink
//basically: check if we have any active weapons
int WeaponCount = PLATFORM.WeaponsActiveFor(WsfTrackId());
if (WeaponCount > 0)
{
writeln_d(PLATFORM.Name(), " weapons incoming, checking weapons active, count: ", WeaponCount);
double threat = 1000.0;
double asset = 1000.0;
//find most threatening incoming weapon
foreach(WsfPlatform p in mIncoming)
{
double range = PLATFORM.SlantRangeTo(p); # meters
double speed = p.Speed(); # meters/sec
double time = range / speed; # seconds
if (time < threat)
{
threat = time;
}
}
//find most valuable launched weapon (most threatening to enemy)
WsfPlatformList activeWeapons = PLATFORM.ActiveWeaponPlatformsFor(WsfTrackId());
for (int i=0; i< activeWeapons.Count(); i=i+1)
{
WsfPlatform w = activeWeapons.Entry(i);
WsfTrack t = w.CurrentTargetTrack();
//TODO - replace with check for weather or not the weapon has active sensors
# // include weapons we are uplinking to.
# if (((WsfRIPRProcessor)PROCESSOR).IsUplinkingTo(w) &&
# t.IsValid())
# {
double range = w.SlantRangeTo(t); # meters
double speed = w.Speed(); # meters/sec
double time = range / speed; # seconds
if (time < asset)
{
asset = time;
}
# }
}
//factor in our aggressiveness, and decide whether or not we should evade yet
double requiredAggressiveness = asset / (threat + asset);
writeln_d(PLATFORM.Name(), " threat: ", threat, ", asset: ", asset, ", required aggressiveness: ", requiredAggressiveness);
if (mEngagementAggressiveness < requiredAggressiveness)
{
#PROCESSOR.SetUpdateInterval(cFAST_UPDATE_INTERVAL);
return true;
}
else
{
Failure("my active weapons are closer, hold course");
}
}
else
{
#PROCESSOR.SetUpdateInterval(cFAST_UPDATE_INTERVAL);
return true;
}
}
else
{
writeln_d("no weapons incoming");
Failure("no weapons incoming");
}
#PROCESSOR.SetUpdateInterval(cSLOW_UPDATE_INTERVAL);
return false;
end_precondition
on_new_execute
PLATFORM.Comment("evade");
end_on_new_execute
execute
writeln_d(PLATFORM.Name(), " executing evade, T=", TIME_NOW);
//PLATFORM.Comment("evade");
### transition to evade incoming
PLATFORM.GoToSpeed(cEVADE_SPEED);
writeln_d(" GoToSpeed( ", cEVADE_SPEED," )");
double safeAltitudeToDiveTo = MATH.Max(mAltitudeMin, (cDEFAULT_ALTITUDE - mAltitudeToDiveEvade));
// Calculate a heading to evade all incoming missiles, weighted by distance, and then turn to it
double evadeHeading = 0;
double evadeAltitude = 0;
double evadeDivisor = 0;
double distMod = 0;
double bearingMod = 0;
int incomingCount = mIncoming.Size();
writeln_d(PLATFORM.Name(), " evade. dive/climb between altitudes: ", cDEFAULT_ALTITUDE, " <-> ", safeAltitudeToDiveTo);
writeln_d("evading ", incomingCount, " threats");
double y = 0;
double x = 0;
double dist = MATH.DOUBLE_MAX();
WsfPlatform nearestThreat;
// calculate an average heading to incoming platforms weighted by distance
for (int i = 0; i < incomingCount; i = i + 1)
{
WsfPlatform temp = (WsfPlatform)(mIncoming[i]);
if (!temp.IsValid() || (temp.Index() == PLATFORM.Index()))
{
continue;
}
double range = PLATFORM.SlantRangeTo(temp);
if (range < dist)
{
dist = range;
nearestThreat = (WsfPlatform)(mIncoming[i]);
}
if (range > 0)
{
distMod = 1 / range;
}
else
{
distMod = 1000000;
}
bearingMod = MATH.NormalizeAngle0_360(PLATFORM.TrueBearingTo(temp));
x = x + MATH.Sin(bearingMod) * distMod;
y = y + MATH.Cos(bearingMod) * distMod;
writeln_d(" Incoming ", temp.Name(), " at distance: ", 1 / distMod, ", bearing: ", bearingMod, ", x: ", MATH.Sin(bearingMod), ", y: ", MATH.Cos(bearingMod), ", (", temp.Latitude(), ", ", temp.Longitude(), ", ", temp.Altitude(), ") @ ", temp.Speed(), "m/s");
evadeDivisor = evadeDivisor + distMod;
evadeHeading = evadeHeading + bearingMod * distMod;
}
# // get the GCI if there is one
WsfPlatform aiflPlat = PLATFORM.Commander();
WsfPlatform gciPlat;
if (aiflPlat.IsValid())
{
gciPlat = aiflPlat.Commander();
}
// if there's a gci, avoid centroids of all his subs
if (gciPlat.IsValid() && gciPlat.Index() != aiflPlat.Index())
{
writeln_d("found gci : ", gciPlat.Name());
foreach (WsfPlatform aifl in gciPlat.Subordinates())
{
if (!aifl.IsValid() || aifl == PLATFORM || aifl == aiflPlat)
{
continue;
}
WsfGeoPoint centroid = aifl.GetSubsCentroid();
if (centroid.X() == 0 && centroid.Y() == 0 && centroid.Z() == 0)
{
continue;
}
distMod = 1 / (PLATFORM.SlantRangeTo(centroid));
bearingMod = MATH.NormalizeAngle0_360(PLATFORM.TrueBearingTo(centroid));
x = x + MATH.Sin(bearingMod) * distMod * weightPeersForEvade;
y = y + MATH.Cos(bearingMod) * distMod * weightPeersForEvade;
writeln_d(" AIFL ", aifl.Name(), " at distance: ", 1 / distMod, ", bearing: ", bearingMod, ", x: ", MATH.Sin(bearingMod), ", y: ", MATH.Cos(bearingMod), ", (", centroid.Latitude(), ", ", centroid.Longitude(), ", ", centroid.Altitude(), ")");
// but we'll avoid peers with less strength than we avoid incoming missiles
// so let's divide the distMod by 2
evadeDivisor = evadeDivisor + distMod;
evadeHeading = evadeHeading + bearingMod * distMod;
}
}
// otherwise, avoid members in your own squadron
else if (aiflPlat.IsValid())
{
writeln_d("also evading flight lead subordinates (peers)");
foreach (WsfPlatform sub in aiflPlat.Subordinates())
{
writeln_d("considering peer ", sub.Name());
if (!sub.IsValid() ||
sub == PLATFORM)
{
continue;
}
distMod = 1 / (PLATFORM.SlantRangeTo(sub));
bearingMod = MATH.NormalizeAngle0_360(PLATFORM.TrueBearingTo(sub));
x = x + MATH.Sin(bearingMod) * distMod * weightPeersForEvade;
y = y + MATH.Cos(bearingMod) * distMod * weightPeersForEvade;
writeln_d(" Peer ", sub.Name(), " at distance: ", 1 / distMod, ", bearing: ", bearingMod, ", x: ", MATH.Sin(bearingMod), ", y: ", MATH.Cos(bearingMod), ", (", sub.Latitude(), ", ", sub.Longitude(), ", ", sub.Altitude(), ") @ ", sub.Speed(), "m/s");
// but we'll avoid peers with less strength than we avoid incoming missiles
// so let's divide the distMod by 2
evadeDivisor = evadeDivisor + distMod;
evadeHeading = evadeHeading + bearingMod * distMod;
}
}
else
{
writeln_d("evade : no commanders found!!!");
}
if (evadeDivisor == 0)
{
return;
}
// correct for quadrant
double theta = MATH.NormalizeAngle0_360(MATH.ATan(x/y));
if (y < 0)
{
theta = MATH.NormalizeAngle0_360(theta - 180);
}
writeln_d(" x: ", x, ", y: ", y, ", theta: ", theta);
evadeHeading = MATH.NormalizeAngle0_360(theta - 180);
if (mWobbleLeftRight == true)
{
//turn to angle to evade
if (mBankLeftFlag == true)
{
evadeHeading = MATH.NormalizeAngle0_360(theta - 180 - mBankAngleForEvading);
double headDiff = MATH.NormalizeAngleMinus180_180( evadeHeading - MATH.NormalizeAngle0_360(PLATFORM.Heading()) );
if (MATH.Fabs(headDiff) < 2.0 &&
nearestThreat.RelativeBearingTo(PLATFORM) > 0.0)
{
mBankLeftFlag = false;
}
}
else // bank right
{
evadeHeading = MATH.NormalizeAngle0_360(theta - 180 + mBankAngleForEvading);
double headDiff = MATH.NormalizeAngleMinus180_180(evadeHeading - MATH.NormalizeAngle0_360(PLATFORM.Heading()));
if (MATH.Fabs(headDiff) < 2.0 &&
nearestThreat.RelativeBearingTo(PLATFORM) < 0.0)
{
mBankLeftFlag = true;
}
}
}
evadeAltitude = MATH.Max(safeAltitudeToDiveTo, evadeAltitude);
if (mWobbleUpDown == true)
{
//compute the dive or climb
if (mDiveDownFlag == true)
{
writeln_d(PLATFORM.Name(), " diving to ", safeAltitudeToDiveTo);
evadeAltitude = safeAltitudeToDiveTo;
if (PLATFORM.Altitude() <= (cDEFAULT_ALTITUDE - 0.95*(cDEFAULT_ALTITUDE-safeAltitudeToDiveTo)))
{
mDiveDownFlag = false;
}
}
else
{
writeln_d(PLATFORM.Name(), " climbing to ", cDEFAULT_ALTITUDE);
evadeAltitude = cDEFAULT_ALTITUDE;
if (PLATFORM.Altitude() >= (safeAltitudeToDiveTo + 0.95*(cDEFAULT_ALTITUDE-safeAltitudeToDiveTo)))
{
mDiveDownFlag = true;
}
}
}
writeln_d(" Evading incoming at heading ", evadeHeading);
writeln_d(" Evading incoming, dive/climb to ", evadeAltitude);
Fly(PLATFORM, evadeHeading, evadeAltitude, cEVADE_SPEED);
if (mDrawNearestThreat)
{
mDraw.SetLayer("behavior_evade");
mDraw.SetDuration(PROCESSOR.UpdateInterval());
mDraw.SetColor(1,0,0);
mDraw.SetLineSize(5);
mDraw.SetLineStyle("dashed");
mDraw.BeginLines();
mDraw.Vertex(PLATFORM.Location());
mDraw.Vertex(nearestThreat.Location());
mDraw.End();
}
end_execute
end_behavior

View File

@@ -0,0 +1,160 @@
# ****************************************************************************
# 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.
# ****************************************************************************
include_once common_timeline_scripts.txt
include_once air_combat_maneuvers.txt
behavior fly_timeline
script_debug_writes off
script_variables
WsfQuantumTaskerProcessor processor;
#***********************************************************************#
#** debugging parameters ***#
#***********************************************************************#
bool mDebugDraw = true;
#***********************************************************************#
#********* VARIABLES BELOW THIS LINE ARE NOT FOR USER EDITING **********#
#***********************************************************************#
WsfDraw mDraw = WsfDraw();
end_script_variables
on_init
if (PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
{
processor = (WsfQuantumTaskerProcessor)PROCESSOR;
}
end_on_init
precondition
writeln_d(PLATFORM.Name(), " precondition fly_timeline, T=", TIME_NOW);
#if (!PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
if (!processor.IsValid())
{
return Failure("behavior not attached to a WSF_QUANTUM_TASKER_PROCESSOR");
}
# WsfTaskList tasks = processor.TasksReceivedOfType("TIMELINE");
# if (tasks.Count() <= 0)
# {
# return Failure("no TIMELINE tasks received yet");
# }
# writeln_d(PLATFORM.Name(), " received TIMELINE tasks: ", tasks.Count());
if (!GetTimeLineTask(processor).IsValid())
{
return Failure("no TIMELINE tasks received yet");
}
return true;
end_precondition
execute
# WsfTrack target = GetNearestTimelineTarget(PLATFORM, processor);
# #Engage(PLATFORM, target, "pure");
# Engage(PLATFORM, target, "lead");
#look for the soonest intercepting target that we don't already have a weapon active or pending against
#sort them
Array<WsfTrack> targets = GetTimeLineTargets(processor);
Array<double> times = Array<double>();
foreach(WsfTrack target in targets)
{
double time = TimeToReachPoint(target, PLATFORM.Location(), 6.0); //6 G turns
times.PushBack(time);
}
SortTracksByValue(targets, times);
#check for no active or pending weapons
foreach(WsfTrack target in targets)
{
#do we need master track?
WsfLocalTrack masterTarget = GetMasterTrackByName(PLATFORM, target.TargetName());
if (!masterTarget.IsValid())
{
continue;
}
if ((PLATFORM.WeaponsPendingFor(masterTarget.TrackId()) + PLATFORM.WeaponsActiveFor(masterTarget.TrackId())) > 0)
{
continue;
}
Engage(PLATFORM, target, "lead", PLATFORM.Speed(), PLATFORM.Altitude(), 6.0);
break;
}
if (mDebugDraw == true)
{
mDraw.SetLayer("behavior_fly_timeline");
mDraw.SetDuration(processor.UpdateInterval());
string side = PLATFORM.Side().Lower();
mDraw.SetLineSize(1);
mDraw.SetLineStyle("solid");
WsfTaskList tasks = processor.TasksReceivedOfType("TIMELINE");
foreach(WsfTask task in tasks)
{
Array<WsfTrack> targets = (Array<WsfTrack>)task.AuxDataObject("targets");
WsfGeoPoint SeparateA;
WsfGeoPoint SeparateB;
if (task.AuxDataExists("point_a"))
{
SeparateA = (WsfGeoPoint)task.AuxDataObject("point_a");
SeparateB = (WsfGeoPoint)task.AuxDataObject("point_b");
}
#draw lines to targets
if (side == "blue") {
mDraw.SetColor(0.0, 0.0, 1.0); #blue
} else if (side == "red") {
mDraw.SetColor(1.0, 0.0, 0.0); #red
} else {
mDraw.SetColor(0.0, 1.0, 0.0); #green
}
if (SeparateA.IsValid() && SeparateB.IsValid())
{
#draw lines for separation divide
mDraw.SetColor(0.0, 0.0, 0.0); #black
mDraw.BeginLines();
mDraw.Vertex(SeparateA);
mDraw.Vertex(SeparateB);
mDraw.End();
#check if platform is on the correct side of separation line
WsfCluster targetCluster = GetCluster(targets);
double refAz = SeparateA.TrueBearingTo(SeparateB);
double ownAz = SeparateA.TrueBearingTo(PLATFORM.Location()) - refAz;
double tgtAz = SeparateA.TrueBearingTo(targetCluster.MeanLocation()) - refAz;
ownAz = MATH.NormalizeAngleMinus180_180(ownAz);
tgtAz = MATH.NormalizeAngleMinus180_180(tgtAz);
if ((ownAz * tgtAz) > 0)
{
#on correct side of line
mDraw.SetColor(1.0, 0.0, 1.0); #purple
}
else
{
#incorrect side
mDraw.SetColor(1.0, 0.5, 0.0); #orange
}
}
mDraw.BeginLines();
foreach(WsfTrack target in targets)
{
mDraw.Vertex(PLATFORM);
mDraw.Vertex(target.CurrentLocation());
}
mDraw.End();
}
}
end_execute
end_behavior

View File

@@ -0,0 +1,308 @@
# ****************************************************************************
# 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.
# ****************************************************************************
behavior go_home
script_debug_writes off
script_variables
double cDEFAULT_ALTITUDE = 9144; // ~30,000 feet
bool mDrawRoute = false; // draw the home route
WsfDraw mDraw = WsfDraw();
bool mCheckFuel = true; // check fuel levels
bool mCheckMyWeapons = true; // check the remaining quanitity of munitions on this platform
#bool mCheckAllWeapons = false; // check the remaining quantity of munitions on all platforms in the peer group, including my own
#bool mCheckUplinks = false; // check if uplinks are still existent
#use: mRequiredToSupportWeapons instead
#bool mWaitOnActiveWeapons = false; // don't go home as long as active weapons are being controlled
string mMainWeapon = ""; // main weapon of concern, not necessarily this platform's main weapon
bool mCheckEscorts = false; // check if escort platforms are still existent and on course
Array<string> mEscortNames = Array<string>(); // the names of platforms this platform is responsible for escorting
double cBINGO_SPEED = 516 * MATH.MPS_PER_NMPH(); // ~M0.8 or 248 m/s @ 25 kft#removed 1.5 * that was added to help 6dof movers 14Oct11
WsfGeoPoint mFirstKnownPoint = WsfGeoPoint();
string mRouteName = "";
int mRouteHomeIndex = 0; // the index of the waypoint on the default route the platform should return to
string mLastComment = "<none yet>";
end_script_variables
script bool HasEscorts()
WsfPlatform escortPlatform;
foreach ( string sEscortName in mEscortNames )
{
escortPlatform = WsfSimulation.FindPlatform(sEscortName);
if (escortPlatform.IsValid() )
{
return true;
}
}
return false;
end_script
script double NumWeaponsRemaining(WsfPlatform aPlatform)
double total = 0.0;
for (int i=0; i<aPlatform.WeaponCount(); i=i+1)
{
total = total + aPlatform.WeaponEntry(i).QuantityRemaining();
}
return total;
end_script
script void DrawRoute()
WsfRoute currRoute = PLATFORM.Route();
if (currRoute.IsValid())
{
//PLATFORM.Comment("draw current route : " + PLATFORM.Route().Name());
mDraw.SetLayer("behavior_go_home");
mDraw.Erase(PLATFORM.Name());
mDraw.SetId(PLATFORM.Name());
mDraw.SetColor(0,1,1);
mDraw.SetLineSize(2);
mDraw.SetLineStyle("dash_dot2");
mDraw.BeginPolyline();
for (int i=0; i<currRoute.Size(); i=i+1)
{
mDraw.Vertex(currRoute.Waypoint(i).Location());
}
mDraw.End();
}
end_script
on_init
mFirstKnownPoint = PLATFORM.Location();
end_on_init
precondition
writeln_d("precondition go_home");
# if (!PROCESSOR.IsA_TypeOf("WSF_RIPR_PROCESSOR"))
# {
# return Failure("behavior not attached to a RIPR processor!");
# }
string msg = "";
bool result = false;
// go home if bingo on fuel
if (mCheckFuel == true &&
PLATFORM.FuelRemaining() < PLATFORM.FuelBingoQuantity())
{
msg = "GO HOME: bingo fuel";
result = true;
}
// go home if all my escorts are dead or gone
else if (mCheckEscorts == true &&
!HasEscorts())
{
msg = "GO HOME: all escorts are dead or gone";
result = true;
}
# // go home if there are no uplinks remaining
# else if (mCheckUplinks == true &&
# ((WsfRIPRProcessor)PROCESSOR).UplinkCount() <= 0)
# {
# msg = "GO HOME: no active uplinks";
# result = true;
# }
// go home if all weapons, including my own, in the peer group are out of ammo
# else if (mCheckAllWeapons == true &&
# ((WsfRIPRProcessor)PROCESSOR).GetRIPRCommanderPlatform().IsValid())
# {
# msg = "GO HOME: all weapons in group are out of ammo";
# result = true;
# foreach( WsfPlatform sub in ((WsfRIPRProcessor)PROCESSOR).GetRIPRCommanderPlatform().Subordinates() )
# {
# if (mMainWeapon != "")
# {
# WsfWeapon temp = sub.Weapon(mMainWeapon);
# if (temp.IsValid() &&
# temp.QuantityRemaining() > 0)
# {
# msg = "";
# result = false;
# }
# }
# else if (NumWeaponsRemaining(sub) > 0.0)
# {
# msg = "";
# result = false;
# }
# }
# }
// go home if my weapons are out of ammo
else if (mCheckMyWeapons == true)
{
extern bool mRequiredToSupportWeapons;
if (mMainWeapon != "")
{
WsfWeapon temp = PLATFORM.Weapon(mMainWeapon);
if (temp.IsValid() &&
temp.QuantityRemaining() <= 0 &&
(mRequiredToSupportWeapons == false ||
PLATFORM.WeaponsActiveFor(WsfTrackId()) <= 0))
{
msg = "GO HOME: main weapon out of ammo";
result = true;
}
}
else if (NumWeaponsRemaining(PLATFORM) <= 0.0 &&
(mRequiredToSupportWeapons == false ||
PLATFORM.WeaponsActiveFor(WsfTrackId()) <= 0))
{
msg = "GO HOME: out of weapons";
result = true;
}
}
// debug - why is this platform going home?
if (result)
{
writeln_d(" T=", TIME_NOW, " ", PLATFORM.Name(), " ", msg);
if (msg != mLastComment)
{
PLATFORM.Comment(msg);
mLastComment = msg;
}
return true;
}
return Failure("plenty of weapons & fuel.");
end_precondition
on_new_execute
PLATFORM.Comment("home");
end_on_new_execute
execute
writeln_d(PLATFORM.Name(), " executing go_home, T=", TIME_NOW);
//PLATFORM.Comment("go_home");
string msg = "";
# WsfRIPRProcessor commander = ((WsfRIPRProcessor)PROCESSOR).CommanderProcessor();
# if (commander.IsValid())
# {
# for (int i = 0; i < ((WsfRIPRProcessor)PROCESSOR).NumJobChannels(); i = i + 1)
# {
# commander.ClearBidsFor(((WsfRIPRProcessor)PROCESSOR), i);
# }
# }
//TODO - reject or cancel any received tasks, we are going home
// make sure we are at the right speed so we don't burn the fuel we have left too fast
PLATFORM.GoToSpeed(cBINGO_SPEED);
writeln_d(" GoToSpeed( ", cBINGO_SPEED," )");
bool onHomeRoute = false;
// if an egress route is defined, and that is the current route,
// find the closest waypoint and fly the route from there.
// this is mainly used to correct some bugs in the 6dof mover
if (mRouteName != "")
{
WsfRoute myRoute = PLATFORM.Route();
if(myRoute.Name() == "home_route")
{
int routeIndex = PLATFORM.RoutePointIndex();
if (routeIndex < 0 || routeIndex >= myRoute.Size())
{
routeIndex = 0;
}
double distThreshold = 4.0*185.2; ## 4/10th nm
if (myRoute.Waypoint(routeIndex).Location().GroundRangeTo(PLATFORM.Location()) < distThreshold)
{
routeIndex = routeIndex + 1;
if (routeIndex >= myRoute.Size())
{
routeIndex = 1;
}
}
onHomeRoute = PLATFORM.FollowRoute(myRoute, routeIndex);
#string msg = write_str("FollowRoute(home_route, ", routeIndex, ")");
#PLATFORM.Comment(msg);
}
// if there is an egress route defined, follow it
if (!onHomeRoute)
{
msg = write_str("attempting to construct and fly route: ", mRouteName);
//PLATFORM.Comment(msg);
writeln_d(" T=", TIME_NOW, " ", PLATFORM.Name(), " ", msg);
WsfRoute original = PLATFORM.Route();
WsfRoute homeRoute = WsfRoute.Create("home_route");
WsfRoute capRoute = WsfRoute.CopyGlobal(mRouteName);
double goHomeSpeed = cBINGO_SPEED;
#if (capRoute.Front().IsValid() && capRoute.Front().Speed() > 0)
#{
# goHomeSpeed = capRoute.Front().Speed();
#}
WsfGeoPoint startPoint = WsfGeoPoint.Construct(mFirstKnownPoint.Latitude(), mFirstKnownPoint.Longitude(), cDEFAULT_ALTITUDE);
homeRoute.Append(startPoint, goHomeSpeed);
capRoute.Transform(startPoint.Latitude(), startPoint.Longitude(), 0);
homeRoute.Append(capRoute);
onHomeRoute = PLATFORM.FollowRoute(homeRoute);
#PLATFORM.Comment("go_home FollowRoute(homeRoute)");
}
}
// if the platform is still not on an egress route, then there is not one
// defined or there was a problem with it, so one needs to be created.
if (!onHomeRoute)
{
//now try going back to route
if (PLATFORM.FollowRoute("DEFAULT_ROUTE", mRouteHomeIndex))
{
msg = write_str("should be flying default route");
//PLATFORM.Comment(msg);
writeln_d(" T=", TIME_NOW, " ", PLATFORM.Name(), " ", msg);
//PLATFORM.Comment("FollowRoute(DEFAULT_ROUTE, 1)");
}
else // what if no default route is provided? (fly to first known point)
{
WsfGeoPoint tempPt = WsfGeoPoint.Construct(mFirstKnownPoint.Latitude(), mFirstKnownPoint.Longitude(), cDEFAULT_ALTITUDE);
PLATFORM.TurnToHeading(PLATFORM.TrueBearingTo(tempPt), 6.0 * Earth.ACCEL_OF_GRAVITY());
msg = write_str("should be flying to point: ", mFirstKnownPoint.ToString());
//PLATFORM.Comment(msg);
writeln_d(" T=", TIME_NOW, " ", PLATFORM.Name(), " ", msg);
//PLATFORM.Comment("GoToLocation(tempPt)");
}
}
// debug
if (mDrawRoute)
{
DrawRoute();
}
end_execute
end_behavior

View File

@@ -0,0 +1,124 @@
# ****************************************************************************
# 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.
# ****************************************************************************
//TODO use default values from higher level context (processor or parent behavior that holds default)
#assumes aDraw duration & layer is set
script void DrawRoute(WsfDraw aDraw, WsfRoute aRoute)
if (aRoute.IsValid())
{
aDraw.SetColor(0,1,1); //teal?
aDraw.SetLineSize(2);
aDraw.SetLineStyle("solid");
aDraw.BeginPolyline();
for (int i=0; i<aRoute.Size(); i=i+1)
{
aDraw.Vertex(aRoute.Waypoint(i).Location());
}
aDraw.End();
aDraw.SetColor(1.0,0.3,0.3); //pink?
aDraw.SetPointSize(4);
aDraw.BeginPoints();
for (int i=0; i<aRoute.Size(); i=i+1)
{
aDraw.Vertex(aRoute.Waypoint(i).Location());
}
aDraw.End();
}
end_script
behavior planned_route
script_debug_writes off
script_variables
bool mDrawRoute = false;
WsfDraw mDraw = WsfDraw();
double cDEFAULT_SPEED = 450.0 * MATH.MPS_PER_NMPH();
double cDEFAULT_ACCEL = 7.5 * Earth.ACCEL_OF_GRAVITY(); // 7.5 G (m/s^2)
end_script_variables
precondition
writeln_d("precondition planned_route");
return true;
end_precondition
on_new_execute
PLATFORM.Comment("route");
end_on_new_execute
execute
writeln_d(PLATFORM.Name(), " executing planned_route, T=", TIME_NOW);
#only command the platform to do something different if its not currently flying a route
WsfMover aMover = PLATFORM.Mover();
if (aMover.IsValid()) {
if (aMover.IsExtrapolating()) {
WsfGeoPoint pt = PLATFORM.Location();
WsfRoute ro = aMover.DefaultRoute().Copy(); #now we have a modifiable route
if (!ro.IsValid())
return;
writeln_d("flying route, name: ", ro.Name(), ", type: ", ro.Type());
WsfGeoPoint close = ro.LocationAtDistance(ro.DistanceAlongRoute(pt));
if (!close.IsValid()) {
return;
}
close.SetAltitudeAGL(pt.Altitude());
if (mDrawRoute)
{
mDraw.BeginLines();
mDraw.Vertex(pt);
mDraw.Vertex(close);
mDraw.End();
}
double d1 = ro.DistanceFromRoute(pt);
double d2 = pt.GroundRangeTo(close);
double d3 = -1;
Array<double> turnRad = aMover.PropertyDouble("turn_radius");
if (turnRad.Size() > 0) {
d3 = 2*turnRad[0];
}
int i = 0;
for (; i < ro.Size(); i = i+1)
{
WsfWaypoint wpt = ro.Waypoint(i);
WsfGeoPoint rpt = wpt.Location();
//check if we are close to an existing waypoint, if so... break & fly at that one
if (rpt.GroundRangeTo(close) < 926) {
break;
}
double dist = ro.DistanceAlongRoute(rpt);
if (dist > d1) {
if (d2 > d3) {
ro.Insert(i, WsfWaypoint.Create(close, wpt.Speed()));
}
break;
}
}
if (i >= ro.Size()) {
i = ro.Size() - 1;
}
//go at default speed; this gets overwritten if route waypoint has defined a speed
PLATFORM.GoToSpeed(cDEFAULT_SPEED, cDEFAULT_ACCEL, true);
PLATFORM.FollowRoute(ro, i);
}
}
if (mDrawRoute)
{
mDraw.SetDuration(PROCESSOR.UpdateInterval());
mDraw.SetLayer("behavior_planned_route");
DrawRoute(mDraw, PLATFORM.Route());
}
end_execute
end_behavior

View File

@@ -0,0 +1,169 @@
# ****************************************************************************
# 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.
# ****************************************************************************
include_once common_timeline_scripts.txt
include_once air_combat_maneuvers.txt
include_once weapon_defs.txt
behavior timeline_delay
script_debug_writes off
script_variables
WsfQuantumTaskerProcessor processor;
end_script_variables
on_init
if (PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
{
processor = (WsfQuantumTaskerProcessor)PROCESSOR;
}
end_on_init
#returns true (to wait) if no free target can be shot at (current range)
#if target already has weapon on it, it is ignored
script bool WaitToTurnAndShoot(Array<WsfTrack> targets)
#only consider free targets (a target with no weapons on it)
#compare max weapon range to each free target
#calculate time to turn & face for a shot
#return false if any target can be turned on & engaged
foreach(WsfTrack target in targets)
{
WsfLocalTrack masterTarget = GetMasterTrackByName(PLATFORM, target.TargetName());
if (!masterTarget.IsValid())
{
continue;
}
bool checkPeerWeapons = false;
if (FiredOn(PLATFORM, masterTarget, checkPeerWeapons))
{
#LBM TODO - figure out time until weapon terminates
# compare that to turn time too
# dont necessarily skip this target
continue;
}
double weaponMaxRange = 0.0;
WsfWeapon maxRangeWeapon;
for (int i=0; i < PLATFORM.WeaponCount(); i+=1)
{
WsfWeapon weapon = PLATFORM.WeaponEntry(i);
if (WeaponCapableAvailableAgainstThreat(weapon, masterTarget))
{
double weaponRange = MaxRange(PLATFORM,weapon,masterTarget);
if (weaponRange > weaponMaxRange)
{
weaponMaxRange = weaponRange;
maxRangeWeapon = weapon;
}
}
}
if (maxRangeWeapon.IsValid())
{
#valid weapon found with an intelligible max range
double targetRange = PLATFORM.GroundRangeTo(masterTarget.CurrentLocation());
#double weaponMaxRange;
double rangeRemaining = targetRange - weaponMaxRange;
if(rangeRemaining <= 0)
{
return false;
}
#targets speed towards me:
double hisClosingSpeed = masterTarget.Speed() * MATH.Cos(masterTarget.RelativeBearingTo(PLATFORM));
#my avg speed towards him (assuming an immediate turn towards him):
double myMinClosingSpeed = PLATFORM.Speed() * MATH.Cos(PLATFORM.RelativeBearingTo(masterTarget));
double myMaxClosingSpeed = PLATFORM.Speed(); #assumes I turn & face
double myAvgClosingSpeed = (myMinClosingSpeed+myMaxClosingSpeed)/2.0;
#closing speed to use:
double closingSpeed = hisClosingSpeed + myAvgClosingSpeed;
extern double mGeesToTurnWith;
double turnTime = TimeToTurnAndFace(PLATFORM.MakeTrack(), masterTarget.CurrentLocation(), mGeesToTurnWith);
if (turnTime >= rangeRemaining/closingSpeed)
{
return false; #dont wait, time to turn & shoot this guy!!!
}
}
}
return true;
end_script
precondition
writeln_d(PLATFORM.Name(), " precondition timeline_delay, T=", TIME_NOW);
if (!processor.IsValid())
{
return Failure("behavior not attached to a WSF_QUANTUM_TASKER_PROCESSOR");
}
extern bool mRequiredToSupportWeapons;
if (mRequiredToSupportWeapons == true && PLATFORM.WeaponsActiveFor(WsfTrackId()) > 0)
{
return Failure("required to support active weapons - do not delay");
}
WsfTask timeline = GetTimeLineTask(processor);
if (!timeline.IsValid())
{
return Failure("not assigned a TIMELINE task");
}
extern WsfLocalTrack mNearestTimeLineTarget;
if (!mNearestTimeLineTarget.IsValid())
{
return Failure("no TIMELINE target to fly against");
}
#compare risk level with range to nearest target
#only delay if risk MED: past DOR and (want to shoot but out of range or waiting on existing shot)
# risk HIGH: past MAR and (want to shoot but out of range or waiting on existing shot)
string RISK = timeline.AuxDataString("RISK");
if (RISK == "HIGH")
{
double MAR = timeline.AuxDataDouble("MAR"); #range (meters)
double slantRange = PLATFORM.GroundRangeTo(mNearestTimeLineTarget.CurrentLocation());
if (slantRange > MAR)
{
return Failure("HIGH RISK agent outside of MAR, dont bother delaying");
}
Array<WsfTrack> targets = (Array<WsfTrack>)timeline.AuxDataObject("targets");
return WaitToTurnAndShoot(targets);
}
else if (RISK == "MEDIUM")
{
double DOR = timeline.AuxDataDouble("DOR"); #range (meters)
double slantRange = PLATFORM.GroundRangeTo(mNearestTimeLineTarget.CurrentLocation());
if (slantRange > DOR)
{
return Failure("MEDIUM RISK agent outside of DOR, dont bother delaying");
}
Array<WsfTrack> targets = (Array<WsfTrack>)timeline.AuxDataObject("targets");
return WaitToTurnAndShoot(targets);
}
else #if (RISK == "LOW")
{
return Failure("LOW RISK agent does not go past DOR, no need to delay");
}
end_precondition
on_new_execute
PLATFORM.Comment("delay");
end_on_new_execute
execute
#TODO - select beam or posthole or whatever
Beam(PLATFORM, mNearestTimeLineTarget);
end_execute
end_behavior

View File

@@ -0,0 +1,275 @@
# ****************************************************************************
# 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.
# ****************************************************************************
include_once common_timeline_scripts.txt
include_once air_combat_maneuvers.txt
include_once weapon_defs.txt
behavior timeline_engage
script_debug_writes off
script_variables
WsfQuantumTaskerProcessor processor;
bool mWaitForLatestShot = false; #if false: shoots asap once at %rmax
bool mDontShootIfCantSupport = false; #if true: doesn't shoot unless there is time to support the shot
WsfLocalTrack mEngageTarget;
end_script_variables
on_init
if (PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
{
processor = (WsfQuantumTaskerProcessor)PROCESSOR;
}
end_on_init
precondition
writeln_d(PLATFORM.Name(), " precondition timeline_engage, T=", TIME_NOW);
if (!processor.IsValid())
{
return Failure("behavior not attached to a WSF_QUANTUM_TASKER_PROCESSOR");
}
WsfTask timeline = GetTimeLineTask(processor);
if (!timeline.IsValid())
{
return Failure("not assigned a TIMELINE task");
}
string RISK = timeline.AuxDataString("RISK");
extern WsfLocalTrack mNearestTimeLineTarget;
extern bool mConsiderPeersWeapons;
#look for the soonest intercepting target that we don't already have a weapon active or pending against
extern Array<WsfLocalTrack> mTimeLineLocalTargets; #sorted list of local track timeline targets
#check for no active or pending weapons
#pursue something, hopefully a shootable guy
extern Array<WsfLocalTrack> mTimeLineLocalTargetsToShoot;
if (mTimeLineLocalTargetsToShoot.Size() > 0)
{
mEngageTarget = mTimeLineLocalTargetsToShoot.Front();
return true;
}
foreach(WsfLocalTrack target in mTimeLineLocalTargets)
{
if (FiredOn(PLATFORM, target, mConsiderPeersWeapons))
{
continue;
}
extern double mGeesToTurnWith;
extern double mEngageSpeed;
# extern bool mRequiredToSupportWeapons;
# if (mRequiredToSupportWeapons == true && RISK != "HIGH") #can support any shot when risk is high (won't be dragging)
# {
# double gate = timeline.AuxDataDouble("DOR"); #range (meters)
# if (RISK == "MEDIUM")
# {
# gate = timeline.AuxDataDouble("MAR"); #range (meters)
# }
#
# #calculate if we have time to support a shot to this target:
# extern double mOffsetAngle;
# extern double mDelaySpeed;
#
# #figure out weapon range & time of flight
# double weaponMaxRange = 0.0;
# WsfWeapon maxRangeWeapon;
# for (int i=0; i < PLATFORM.WeaponCount(); i+=1)
# {
# WsfWeapon weapon = PLATFORM.WeaponEntry(i);
# if (WeaponCapableAvailableAgainstThreat(weapon, target))
# {
# double weaponRange = MaxRange(PLATFORM,weapon,target);
# if (weaponRange > weaponMaxRange)
# {
# weaponMaxRange = weaponRange;
# maxRangeWeapon = weapon;
# }
# }
# }
# if (!maxRangeWeapon.IsValid())
# {
# continue; #no weapon for this target
# }
# #estimate time before we can shoot
# #hypothetical shot
# #estimate time cranking (based on weapon travel time)
# #does this leave us enough time to turn to drag?
# double range = PLATFORM.GroundRangeTo(mNearestTimeLineTarget.CurrentLocation());
#
# double weaponTOF = TimeToHit(PLATFORM, maxRangeWeapon, target);
# if (weaponTOF <= 0)
# {
# #good weapon data no found
# continue;
# }
# double firingRange = DefaultPercentRangeMax * weaponMaxRange;
# double targetRange = PLATFORM.GroundRangeTo(target.CurrentLocation());
# double rangeToGo = targetRange - firingRange;
# double timeTilShot = 0.0;
# double closingSpeed = 0.0;
# if (rangeToGo > 0)
# {
# #subtract off closing distance to nearest target during the time before shot
# timeTilShot = rangeToGo / PLATFORM.ClosingSpeedOf(target);
# closingSpeed = PLATFORM.Speed() + mNearestTimeLineTarget.Speed();
# range -= (timeTilShot * closingSpeed);
# }
#
# #subtract off closing distance to nearest target during the time of weapon's flight
# closingSpeed = (mDelaySpeed * MATH.Cos(mOffsetAngle)) + mNearestTimeLineTarget.Speed();
# range -= (weaponTOF * closingSpeed);
#
# #calculate time to turn to drag (after cranking)
# Array<double> vals = Array<double>();
# double turnAngle = 180 - mOffsetAngle;
# TurnAngleTimeAndDistance(turnAngle, mEngageSpeed, mGeesToTurnWith, vals);
# double turnTime = vals[0];
# double distSep = vals[1];
# #subtract off closing distance to nearest target during the time of turning to drag
# range += distSep;
# range -= (turnTime*mNearestTimeLineTarget.Speed());
#
# if (range >= gate)
# {
# mEngageTarget = target;
# return true;
# }
# }
# else
{
mEngageTarget = target;
return true;
}
}
return Failure("no timeline targets left to engage");
end_precondition
# script double GetLaunchPercentRangeMaxOnThreat(string weaponName, WsfTrack threat)
# WsfPlatform plat = WsfSimulation.FindPlatform( threat.TargetName() );
# if (plat.IsValid())
# {
# if (WeaponThreatRmaxMap.Exists(weaponName))
# {
# Map<string, double> categoryRangeMap = WeaponThreatRmaxMap.Get(weaponName);
# foreach (string aCategory : double percent in categoryRangeMap)
# {
# if( plat.CategoryMemberOf( aCategory ) )
# {
# return percent;
# }
# }
# }
# }
# return DefaultPercentRangeMax;
# end_script
script bool TryToFireOn(WsfLocalTrack target)
extern double DefaultPercentRangeMax;
extern double DefaultPercentRangeMin;
WsfWeapon weapon;
bool weaponUsable = false;
#first weapon found will be used
for (int i=0; i < PLATFORM.WeaponCount(); i+=1)
{
weapon = PLATFORM.WeaponEntry(i);
writeln_d("checking if weapon ", weapon.Name(), " is usable.");
if (WeaponCapableAvailableAgainstThreat(weapon, target) &&
#InRangeToFire(PLATFORM, weapon, target, GetLaunchPercentRangeMaxOnThreat(weapon.Name(), target), DefaultPercentRangeMin))
InRangeToFire(PLATFORM, weapon, target, DefaultPercentRangeMax, DefaultPercentRangeMin))
{
weaponUsable = true;
break;
}
}
if (weaponUsable == false)
{
writeln_d("no usable weapon found!");
return false;
}
bool launched = false;
if (weapon.IsTurnedOn())
{
writeln_d(" Attempting launch at ", target.TargetName());
launched = weapon.Fire(target);
}
writeln_d(" launched == ", launched, ", weapon: ", weapon.Name());
if(launched == false)
{
writeln_d(" ", PLATFORM.Name(), " could NOT fire at track: ", target.TargetName(), " at time: ", TIME_NOW);
}
return launched;
end_script
on_new_execute
PLATFORM.Comment("engage");
end_on_new_execute
execute
#mEngageTarget;
extern double mDefaultAltitude;
#check if we need to fly offset to get on proper side of engagement separation line
extern double mEngageSpeed;
extern double mGeesToTurnWith;
extern bool mSeparateEngagement;
if (mSeparateEngagement == true)
{
extern string mSeparateSide; # "LEFT" or "RIGHT"
extern double mEngagementTrueBearing;
extern double mOffsetAngle;
double delta = PLATFORM.TrueBearingTo(mEngageTarget) - mEngagementTrueBearing;
if (delta < 0 && mSeparateSide == "LEFT")
{
#we're out of position, crank left
Offset(PLATFORM, mEngageTarget.CurrentLocation(), -mOffsetAngle, mDefaultAltitude, mEngageSpeed, mGeesToTurnWith);
}
else if (delta > 0 && mSeparateSide == "RIGHT")
{
#we're out of position, crank right
Offset(PLATFORM, mEngageTarget.CurrentLocation(), mOffsetAngle, mDefaultAltitude, mEngageSpeed, mGeesToTurnWith);
}
else
{
#engage straight way - intercept the target
Engage(PLATFORM, mEngageTarget, "lead", mEngageSpeed, mDefaultAltitude, mGeesToTurnWith);
}
}
else
{
#engage straight way - intercept the target
Engage(PLATFORM, mEngageTarget, "lead", mEngageSpeed, mDefaultAltitude, mGeesToTurnWith);
}
#TODO - check for shot range, prep for shot if appropriate?
# #if possible, fire a weapon at the target
# TryToFireOn(mEngageTarget);
#try for shots on all shootable targets
foreach (WsfLocalTrack target in mTimeLineLocalTargetsToShoot)
{
TryToFireOn(target);
}
end_execute
end_behavior

View File

@@ -0,0 +1,137 @@
# ****************************************************************************
# 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.
# ****************************************************************************
#REQUIRES defined externally: double mGeesToTurnWith;
include_once common_timeline_scripts.txt
include_once air_combat_maneuvers.txt
behavior timeline_reposition
script_debug_writes off
script_variables
WsfQuantumTaskerProcessor processor;
bool mRepositioning = false;
end_script_variables
on_init
if (PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
{
processor = (WsfQuantumTaskerProcessor)PROCESSOR;
}
end_on_init
on_new_execute
mRepositioning = true;
PLATFORM.Comment("reposition");
end_on_new_execute
on_new_fail
mRepositioning = false;
end_on_new_fail
#TODO - add hysteresis?
# if already repositioning, account for turn time back to engage
# dont reengage if we'll have to turn away before facing target
precondition
#writeln_d(PLATFORM.Name(), " precondition timeline_reposition, T=", TIME_NOW);
if (!processor.IsValid())
{
return Failure("behavior not attached to a WSF_QUANTUM_TASKER_PROCESSOR");
}
WsfTask timeline = GetTimeLineTask(processor);
if (!timeline.IsValid())
{
return Failure("not assigned a TIMELINE task");
}
extern WsfLocalTrack mNearestTimeLineTarget;
if (!mNearestTimeLineTarget.IsValid())
{
return Failure("no local TIMELINE target to fly against");
}
extern double mGeesToTurnWith;
string RISK = timeline.AuxDataString("RISK");
if (RISK == "HIGH")
{
return Failure("HIGH RISK agent does not reposition");
}
extern double mDragSpeed;
extern double mEngageSpeed;
if (mRepositioning == true)
{
writeln_d("testing conditions to CONTINUE dragging");
#already dragging, look at conditions required to stop dragging & re-engage
#check what distance we'd be at after a turn to face the target and another turn away
#in other words: do we have the time to face the target & then turn to stay outside the gate?
#gate determined by risk level (LOW risk gate = DOR, MEDIUM risk gate = MAR)
double gate = timeline.AuxDataDouble("DOR"); #range (meters)
if (RISK == "MEDIUM")
{
gate = timeline.AuxDataDouble("MAR"); #range (meters)
}
Array<double> vals = Array<double>();
WsfGeoPoint targetLoc = mNearestTimeLineTarget.CurrentLocation();
WsfTrack platAsTrack = PLATFORM.MakeTrack();
platAsTrack.SetVelocity(mDragSpeed, platAsTrack.Heading());
TurnTimeAndDistance(platAsTrack, targetLoc, mGeesToTurnWith, vals);
double turnTime = vals[0];
double distClosed = vals[1];
double rangeAfterTurningIn = PLATFORM.GroundRangeTo(targetLoc) - distClosed - mNearestTimeLineTarget.Speed()*turnTime;
double turnAwayTime = TimeToTurn(180, mEngageSpeed, mGeesToTurnWith);
double rangeAfterTurningAway = rangeAfterTurningIn - mNearestTimeLineTarget.Speed()*turnAwayTime;
if (rangeAfterTurningAway >= gate)
{
return Failure("enough time to turn & face before gate");
}
return true;
}
else
{
writeln_d("testing conditions to START dragging");
#not dragging yet, look at conditions required to start:
#look at time required to turn & leave: compare to time until next gate
#gate determined by risk level (LOW risk gate = DOR, MEDIUM risk gate = MAR)
double gate = timeline.AuxDataDouble("DOR"); #range (meters)
if (RISK == "MEDIUM")
{
gate = timeline.AuxDataDouble("MAR"); #range (meters)
}
Array<double> vals = Array<double>();
WsfGeoPoint targetLoc = mNearestTimeLineTarget.CurrentLocation();
double turnAngle = 180 - MATH.Fabs(PLATFORM.RelativeBearingTo(targetLoc));
TurnAngleTimeAndDistance(turnAngle, mEngageSpeed, mGeesToTurnWith, vals);
double turnTime = vals[0];
double distSep = vals[1];
double rangeAfterTurn = distSep + PLATFORM.GroundRangeTo(targetLoc) - mNearestTimeLineTarget.Speed()*turnTime;
if (rangeAfterTurn >= gate)
{
return Failure("agent not close enough to gate");
}
return true;
}
end_precondition
execute
extern double mDragSpeed;
extern double mDragAltitude;
Drag(PLATFORM, mNearestTimeLineTarget, mDragSpeed, mDragAltitude, mGeesToTurnWith); #max speed
end_execute
end_behavior

View File

@@ -0,0 +1,92 @@
# ****************************************************************************
# 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.
# ****************************************************************************
include_once common_timeline_scripts.txt
include_once air_combat_maneuvers.txt
behavior weapon_support
script_debug_writes off
script_variables
WsfQuantumTaskerProcessor processor;
Array<WsfLocalTrack> mEngaged = Array<WsfLocalTrack>();
Array<double> mBearings = Array<double>();
end_script_variables
on_init
if (PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
{
processor = (WsfQuantumTaskerProcessor)PROCESSOR;
}
end_on_init
precondition
writeln_d(PLATFORM.Name(), " precondition weapon_support, T=", TIME_NOW);
if (!processor.IsValid())
{
return Failure("behavior not attached to a WSF_QUANTUM_TASKER_PROCESSOR");
}
extern bool mRequiredToSupportWeapons;
if (mRequiredToSupportWeapons == false)
{
return Failure("not required to support weapons!");
}
WsfTask timeline = GetTimeLineTask(processor);
if (!timeline.IsValid())
{
return Failure("not assigned a TIMELINE task");
}
extern Array<WsfLocalTrack> mTimeLineLocalTargets;
mEngaged.Clear();
mBearings.Clear();
foreach(WsfLocalTrack t in mTimeLineLocalTargets)
{
if (PLATFORM.WeaponsActiveFor(t.TrackId()) > 0)
{
mEngaged.PushBack(t);
mBearings.PushBack(PLATFORM.RelativeBearingTo(t.CurrentLocation()));
}
}
if (mEngaged.Size() <= 0)
{
return Failure("no weapons to support against timeline targets");
}
return true;
end_precondition
on_new_execute
PLATFORM.Comment("crank");
end_on_new_execute
execute
SortLocalTracksByValue(mEngaged, mBearings);
extern string mSeparateSide; # "LEFT" or "RIGHT" or other?
extern double mOffsetAngle;
extern double mDefaultAltitude;
extern double mDelaySpeed;
extern double mGeesToTurnWith;
if (mSeparateSide == "RIGHT")
{
#crank right of left-most engaged target (front of array)
Offset(PLATFORM, mEngaged.Front().CurrentLocation(), mOffsetAngle, mDefaultAltitude, mDelaySpeed, mGeesToTurnWith);
}
else #if (mSeparateSide == "LEFT")
{
#crank left of right-most engaged target (back of array)
Offset(PLATFORM, mEngaged.Back().CurrentLocation(), -mOffsetAngle, mDefaultAltitude, mDelaySpeed, mGeesToTurnWith);
}
end_execute
end_behavior

View File

@@ -0,0 +1,398 @@
# ****************************************************************************
# 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.
# ****************************************************************************
script WsfLocalTrack GetMasterTrackByName(WsfPlatform aPlatform, string trackName)
WsfLocalTrackList trackList = aPlatform.MasterTrackList();
foreach (WsfLocalTrack track in trackList)
{
if (track.TargetName() == trackName)
{
return track;
}
}
WsfLocalTrack x; # since we can't return null
return x;
end_script
# aSpeed in meters per second, current platform speed
# aMaxG in units of gravity (1g equivalent to 9.8 m/s^2)
# adjust to min of 2 G if less is passed in
script double TurnRadius(double aSpeed, double aMaxG)
#RadialAccel = Speed^2/Radius
double RadialAccel = aMaxG * Earth.ACCEL_OF_GRAVITY();
if (aMaxG < 2.0)
{
RadialAccel = 2.0 * Earth.ACCEL_OF_GRAVITY();
}
double turnRadius = aSpeed * aSpeed / RadialAccel;
return turnRadius;
end_script
#maxG in units of gravity (1g equivalent to 9.8 m/s^2)
#adjust to min of 2 G if less is passed in
script double TimeToTurnAndFace(WsfTrack turner, WsfGeoPoint targetPoint, double maxG)
if (!turner.IsValid())
{
return MATH.DOUBLE_MAX();
}
double TurnAzimuth = MATH.Fabs(turner.RelativeBearingTo(targetPoint));
double Speed = turner.Speed();
double Radius = TurnRadius(Speed, maxG);
double TurningCircleCircumference = 2.0 * MATH.PI() * Radius;
double TurningArc = (TurnAzimuth/360)*TurningCircleCircumference;
double TimeToTurn = TurningArc / Speed;
return TimeToTurn;
end_script
script double DistanceClosedDuringTurn(double turnAngle, double turnRadius)
#lock the turn angle into [0,180]
turnAngle = MATH.Fabs(MATH.NormalizeAngleMinus180_180(turnAngle));
#a max turn (from relative bearing 180 back to 0 means we didn't close any distance at all
#plot closing speed as a function of relative angle
#this gives you a cosine curve from 0 to 180 with an amplitude of speed
#use calculus for integrating the cosine curve from the relative angle the turner starts at back to zero
#if solving for total area under curve, you break up an integral whenever it crosses the X axis
#in our case, we don't want total area, we want positive area (positive closing speed)
#so integrating cosine from [0,angle] where angle <= 180, we dont have to break it up into multiple integrals
#integral of cosine is sin
#showing -sin(0) for completeness when integrating
double scale = MATH.Sin(turnAngle) - MATH.Sin(0);
double distanceClosed = scale * turnRadius;
return distanceClosed;
end_script
script bool TurnAngleTimeAndDistance(double angle, double speed, double maxG, Array<double> TimeAndDistance)
if (!TimeAndDistance.IsValid())
{
return false;
}
angle = MATH.Fabs(MATH.NormalizeAngleMinus180_180(angle));
double Radius = TurnRadius(speed, maxG);
double TurningCircleCircumference = 2.0 * MATH.PI() * Radius;
double TurningArc = (angle/360)*TurningCircleCircumference;
double TimeToTurn = TurningArc / speed;
double DistanceClosed = DistanceClosedDuringTurn(angle, Radius);
TimeAndDistance.Clear();
TimeAndDistance.PushBack(TimeToTurn);
TimeAndDistance.PushBack(DistanceClosed);
return true;
end_script
#maxG in units of gravity (1g equivalent to 9.8 m/s^2)
#adjust to min of 2 G if less is passed in
script bool TurnTimeAndDistance(WsfTrack turner, WsfGeoPoint targetPoint, double maxG, Array<double> TimeAndDistance)
if (!turner.IsValid() || !targetPoint.IsValid())
{
return false;
}
double angle = MATH.Fabs(turner.RelativeBearingTo(targetPoint));
return TurnAngleTimeAndDistance(angle, turner.Speed(), maxG, TimeAndDistance);
end_script
script double TimeToTurnFrom(WsfPlatform plat, double speed, WsfTrack threat, double maxG)
double runAwayTurn = 180 - MATH.Fabs(plat.RelativeBearingTo(threat));
double radius = TurnRadius(speed, maxG);
double turnCircle = 2.0 * MATH.PI() * radius;
double turnArc = (runAwayTurn/360)*turnCircle;
double turnTime = turnArc / speed;
return turnTime;
end_script
# Calculate how long it taks a platform to turn the specified angle
# at specified speed (m/s) and max G
script double TimeToTurn(double angle, double speed, double maxG)
double radius = TurnRadius(speed, maxG);
double turnCircle = 2.0 * MATH.PI() * radius;
double turnArc = (angle/360)*turnCircle;
double turnTime = turnArc / speed;
return turnTime;
end_script
#assumes current speed maintained
script double TimeToReachPoint(WsfTrack traveler, WsfGeoPoint targetPoint, double maxG)
if (!traveler.IsValid())
{
return MATH.DOUBLE_MAX();
}
double Speed = traveler.Speed();
if (Speed <= 0.0)
{
return MATH.DOUBLE_MAX();
}
Array<double> vals = Array<double>();
TurnTimeAndDistance(traveler, targetPoint, maxG, vals);
double TurnTime = vals[0];
double DistClosed = vals[1];
double TravelLegDist = traveler.GroundRangeTo(targetPoint) - DistClosed;
double TravelLegTime = TravelLegDist / traveler.Speed();
double TimeToReach = TurnTime + TravelLegTime;
return TimeToReach;
end_script
#bubble sort
script void SortTracksByValue(Array<WsfTrack> tracks, Array<double> values)
if(tracks.Size() != values.Size())
return;
bool swapped = true;
int j = 0;
WsfTrack tempTrack;
double tempValue;
while (swapped)
{
swapped = false;
j += 1;
for (int i=0; i<(values.Size())-j; i+=1)
{
if (values[i] > values[i+1])
{
tempValue = values[i]; tempTrack = tracks[i];
values[i] = values[i+1]; tracks[i] = tracks[i+1];
values[i+1] = tempValue; tracks[i+1] = tempTrack;
swapped = true;
}
}
}
end_script
script void SortLocalTracksByValue(Array<WsfLocalTrack> tracks, Array<double> values)
if(tracks.Size() != values.Size())
return;
bool swapped = true;
int j = 0;
WsfLocalTrack tempTrack;
double tempValue;
while (swapped)
{
swapped = false;
j += 1;
for (int i=0; i<(values.Size())-j; i+=1)
{
if (values[i] > values[i+1])
{
tempValue = values[i]; tempTrack = tracks[i];
values[i] = values[i+1]; tracks[i] = tracks[i+1];
values[i+1] = tempValue; tracks[i+1] = tempTrack;
swapped = true;
}
}
}
end_script
script WsfGeoPoint GetAssetCentroid(Array<WsfAssetPerception> ASSETS)
if (ASSETS.Size() <= 0)
{
return WsfGeoPoint.Construct(0,0,0);
}
double coeff = 1.0/((double)ASSETS.Size());
Vec3 wcs = Vec3.Construct(0,0,0);
foreach (WsfAssetPerception p in ASSETS)
{
Vec3 vec = p.Location().LocationWCS();
vec.Scale(coeff);
wcs = Vec3.Add(wcs, vec);
}
return WsfGeoPoint.ConstructWCS(wcs);
end_script
script WsfTask GetTimeLineTask(WsfQuantumTaskerProcessor proc)
WsfTaskList taskList = proc.TasksReceivedOfType("TIMELINE");
if (taskList.Count() > 0)
{
return taskList.Entry(0);
}
WsfTask retTask; #dummy task
return retTask;
end_script
script double GetTimeLineTaskValueForKey(WsfTask timeline, string aKeyVal)
if (timeline.IsValid())
{
if (timeline.AuxDataExists(aKeyVal))
{
return timeline.AuxDataDouble(aKeyVal);
}
else
{
writeln_d(aKeyVal, " not found in ", timeline.TaskType() );
}
}
return -1.0;
end_script
script double GetTimeLineValueForKey(WsfQuantumTaskerProcessor proc, string aKeyVal)
WsfTask timeline = GetTimeLineTask(proc);
return GetTimeLineTaskValueForKey(timeline, aKeyVal);
end_script
script WsfTrack GetTimeLineTaskTargetTrack(WsfPlatform plat, WsfTask timeline)
WsfTrack t; #stub track
if (timeline.IsValid())
{
if (timeline.AuxDataExists("targets"))
{
Array<WsfTrack> trkList = (Array<WsfTrack>)timeline.AuxDataObject("targets");
foreach (WsfTrack trk in trkList)
{
if (trk.IsValid())
{
#see if the track can be found in local track list, if so return the distance
WsfLocalTrack myTrack = GetMasterTrackByName(plat, trk.TargetName());
if (myTrack.IsValid())
{
return myTrack;
}
#if the task assigned track isn't a track on the current platform, use the information given
else
{
return trk;
}
}
}
}
}
return t;
end_script
script WsfTrack GetTimeLineTargetTrack(WsfPlatform plat, WsfQuantumTaskerProcessor proc)
WsfTask timeline = GetTimeLineTask(proc);
return GetTimeLineTaskTargetTrack(plat, timeline);
end_script
script Array<WsfTrack> GetTimeLineTaskTargets(WsfTask timeline)
Array<WsfTrack> targets;
if (timeline.IsValid() && timeline.AuxDataExists("targets"))
{
targets = (Array<WsfTrack>)timeline.AuxDataObject("targets");
}
else
{
targets = Array<WsfTrack>();
}
return targets;
end_script
script Array<WsfTrack> GetTimeLineTargets(WsfQuantumTaskerProcessor proc)
WsfTask timeline = GetTimeLineTask(proc);
return GetTimeLineTaskTargets(timeline);
end_script
script WsfTrack GetNearestTimeLineTaskTarget(WsfPlatform plat, WsfTask timeline)
WsfTrack nearestTrack; #empty at first
Array<WsfTrack> trkList = GetTimeLineTaskTargets(timeline);
double nearest = MATH.DOUBLE_MAX();
foreach (WsfTrack trk in trkList)
{
if (trk.IsValid())
{
double dist = plat.GroundRangeTo(trk.CurrentLocation());
if (dist < nearest)
{
nearest = dist;
nearestTrack = trk;
}
}
}
return nearestTrack;
end_script
script WsfTrack GetNearestTimeLineTarget(WsfPlatform plat, WsfQuantumTaskerProcessor proc)
WsfTask timeline = GetTimeLineTask(proc);
return GetNearestTimeLineTaskTarget(plat, timeline);
end_script
script WsfLocalTrack GetNearestTimeLineTaskLocalTarget(WsfPlatform plat, WsfTask timeline)
WsfLocalTrack nearestLocalTrack; #empty at first
Array<WsfTrack> trkList = GetTimeLineTaskTargets(timeline);
double nearest = MATH.DOUBLE_MAX();
foreach (WsfTrack trk in trkList)
{
WsfLocalTrack lt = GetMasterTrackByName(plat,trk.TargetName());
if (lt.IsValid())
{
double dist = plat.GroundRangeTo(lt.CurrentLocation());
if (dist < nearest)
{
nearest = dist;
nearestLocalTrack = lt;
}
}
}
return nearestLocalTrack;
end_script
script Array<WsfLocalTrack> GetTimeLineTaskLocalTargets(WsfPlatform plat, WsfTask timeline)
Array<WsfTrack> targets = GetTimeLineTaskTargets(timeline);
Array<WsfLocalTrack> localTargets = Array<WsfLocalTrack>();
foreach(WsfTrack t in targets)
{
WsfLocalTrack lt = GetMasterTrackByName(plat, t.TargetName());
if (lt.IsValid())
{
localTargets.PushBack(lt);
}
}
return localTargets;
end_script
script Array<WsfLocalTrack> GetTimeLineLocalTargets(WsfPlatform plat, WsfQuantumTaskerProcessor proc)
WsfTask timeline = GetTimeLineTask(proc);
return GetTimeLineTaskLocalTargets(plat, timeline);
end_script
script bool FiredOn(WsfPlatform shooter, WsfLocalTrack target, bool checkPeers)
if ((shooter.WeaponsPendingFor(target.TrackId()) + shooter.WeaponsActiveFor(target.TrackId())) > 0)
{
return true;
}
if (checkPeers)
{
foreach(WsfPlatform peer in shooter.Peers())
{
WsfLocalTrack peerTrack = GetMasterTrackByName(peer,target.TargetName());
if (peerTrack.IsValid())
{
if ((peer.WeaponsPendingFor(peerTrack.TrackId()) + peer.WeaponsActiveFor(peerTrack.TrackId())) > 0)
{
return true;
}
}
}
}
return false;
end_script
script double GetTargetDistance(WsfPlatform plat, WsfQuantumTaskerProcessor proc)
WsfTrack targetTrack = GetNearestTimeLineTarget(plat, proc);
if (targetTrack.IsValid())
{
# if (targetTrack.Target().IsValid() && PLATFORM.IsValid())
# {
# draw.SetLineStyle("solid");
# draw.BeginLines();
# draw.SetColor(1.0, 0.0, 1.0);
# draw.Vertex(PLATFORM);
# draw.Vertex(targetTrack.Target());
# draw.End();
# }
double dist = plat.GroundRangeTo(targetTrack.CurrentLocation());
return dist;
}
return -1;
end_script

View File

@@ -0,0 +1,521 @@
# ****************************************************************************
# 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.
# ****************************************************************************
include_once common_timeline_scripts.txt
processor FLIGHT_LEAD_FILTERED_TIMELINE WSF_QUANTUM_TASKER_PROCESSOR
script_debug_writes on
script_variables
#asset evaluation on tasks:
bool mCountWeaponsInEval = false;
#setting up the timeline:
double FSR = 40 * MATH.M_PER_NM();
double DOR = 35 * MATH.M_PER_NM();
double MAR = 20 * MATH.M_PER_NM();
#risk level management
bool mStaticRiskLevel = false;
string RISK = "LOW"; #overwritten dynamically if 'mStaticRiskLevel' is true
#based on range gates to protected location defined here
WsfGeoPoint mProtectedLocation; #can be invalid, will just use lead's position
double mLowRiskRange = 120 * MATH.M_PER_NM();
double mMediumRiskRange = 90 * MATH.M_PER_NM();
double mHighRiskRange = 60 * MATH.M_PER_NM();
#battle stance management
bool mPassiveAllowed = true;
Map<string,bool> mPassivePlayers = Map<string,bool>();
#values for engaging two groups
bool mEnableTwoTimelines = true;
double mMyWeaponRange = 40*1852; #used to figure out if threats are presenting to wide on azimuth for one shooter
double mMaxTargetAzSeparation = 30.0; #degrees, max az separation of targets for one shooter
#########################################################################
# Threat Filtering
# Only consider known threats with soonest intercept time
# 1000 knots closing speed = ~514 meters/sec
# 15 nm range at 514 m/s closing speed = ~54 seconds
#########################################################################
double mDefaultTargetTurningGs = 6.0; # units of gravity (1 gravity = 9.8 m/s^2)
double mInterceptTimeThreshold = 108.0; #our current calculation only accounts for threats speed, not ours
#double mInterceptTimeThreshold = 27.0;
#double mInterceptTimeThreshold = 54.0; # (sec) ignore all threats that will intercept us later than (the soonest threat + this time)
Map<string,int> mKnownTargetTypes = Map<string,int>();
mKnownTargetTypes["BLUE_STRIKER"] = 1;
mKnownTargetTypes["RED_STRIKER"] = 1;
mKnownTargetTypes["STRIKER"] = 1;
#values for support roles
#bool mEnableSupportRoles = false;
#########################################################################
## util - dont touch
double mSoonestIntercept = -1.0;
WsfTrack mBiggestThreat;
WsfClusterManager mClusterManager;
WsfAssetPerception mShooter1;
int mWeapons1 = 0;
WsfAssetPerception mShooter2;
int mWeapons2 = 0;
WsfDraw mDraw = WsfDraw();
end_script_variables
on_initialize
mDraw.SetId("timeline_lead");
mDraw.SetColor(1.0,0.5,0.0); #orange?
mDraw.SetLineStyle("solid");
mDraw.SetLineSize(1);
mDraw.SetDuration(PROCESSOR.UpdateInterval());
mClusterManager = WsfClusterManager.Create();
mClusterManager.SetClusterMethod("H_TREE_MAX");
#if clustering is ever used in this agent, its to create two clusters for two timelines
mClusterManager.SetDistanceFunction("POSITION_ONLY");
mClusterManager.SetNumClustersToCreate(2);
if (!mProtectedLocation.IsValid())
{
mProtectedLocation = PLATFORM.Location();
}
end_on_initialize
state INITIAL
on_entry
PLATFORM.Comment("INITIAL state");
end_on_entry
next_state BATTLE
Map<string, bool> shots = Map<string, bool>();
writeln_d("T=",TIME_NOW,", ", PLATFORM.Name(), " analyzing battle conditions!");
WsfTaskList list = PROCESSOR.TasksAssignedOfType("TIMELINE");
foreach(WsfTask task in list)
{
#bool roundsFired = task.Assignee().RoundsFiredAt(task.TrackId()) > 0;
bool roundsFired = task.Assignee().RoundsCompleteFor(WsfTrackId()) > 0;
writeln_d(" checking assigned task ", task.TrackId().ToString(), " assigned to ", task.AssigneeName(), "fired? ", roundsFired);
shots[task.AssigneeName()] = roundsFired;
}
foreach (string assignee : bool shot in shots)
{
if (shot == false)
{
return false;
}
}
return (shots.Size() > 0);
end_next_state
end_state
state BATTLE
on_entry
PLATFORM.Comment("BATTLE state");
mPassiveAllowed = false;
end_on_entry
end_state
script void UpdateRiskLevel(Array<WsfTrack> knownThreats)
string oldrisk = RISK;
double closest = MATH.DOUBLE_MAX();
foreach(WsfTrack threat in knownThreats)
{
double dist = mProtectedLocation.GroundRangeTo(threat.CurrentLocation());
closest = MATH.Min(closest, dist);
}
if (closest < mHighRiskRange)
{
RISK = "HIGH";
}
else if (closest < mMediumRiskRange)
{
RISK = "MEDIUM";
}
else
{
RISK = "LOW";
}
if (oldrisk != RISK)
{
PLATFORM.Comment(write_str(RISK," RISK"));
}
end_script
script Array<WsfTrack> FilterTracks(Array<WsfLocalTrack> TRACKS, Array<WsfAssetPerception> ASSETS)
Array<WsfTrack> filtered = Array<WsfTrack>();
#filter out threats that wont intercept us as soon as most threatening
#find soonest intercept
Array<double> interceptTimes = Array<double>();
mSoonestIntercept = MATH.DOUBLE_MAX();
for (int i=0; i<TRACKS.Size(); i=i+1)
{
WsfLocalTrack lt = TRACKS[i];
writeln_d("T=", TIME_NOW, ", considering target ", lt.TargetName());
interceptTimes[i] = -1.0;
if (lt.IsValid() && !lt.TargetKilled() && (!lt.SideValid() || lt.Side() != PLATFORM.Side()))
{
if (mKnownTargetTypes.Size() <= 0 || mKnownTargetTypes.Exists(lt.TargetType()))
{
writeln_d("T=", TIME_NOW, ", did not filter target ", lt.TargetName(), " of type ", lt.TargetType());
double time = MATH.DOUBLE_MAX();
foreach (WsfAssetPerception p in ASSETS)
{
double temp = TimeToReachPoint(lt, p.Location(), mDefaultTargetTurningGs);
if (temp < time)
{
time = temp;
}
}
interceptTimes[i] = time;
writeln_d("T=", TIME_NOW,", ", lt.TargetName()," time to reach = ", time);
if (time > 0 && time < mSoonestIntercept)
{
mSoonestIntercept = time;
mBiggestThreat = lt;
}
} else { writeln_d("UNKNOWN TARGET TYPE!"); }
} else { writeln_d("TRACK NOT VALID (or similar error)"); }
}
for (int i=0; i<TRACKS.Size(); i=i+1)
{
if (interceptTimes[i] > 0 && interceptTimes[i] <= (mSoonestIntercept + mInterceptTimeThreshold))
{
filtered.PushBack(TRACKS[i]);
}
# else ignore this threat for now, too far away
}
return filtered;
end_script
script Array<WsfQuantumTask> FlightLeadTaskGeneration (Array<WsfLocalTrack> TRACKS, Array<WsfAssetPerception> ASSETS )
writeln_d("T=", TIME_NOW, ", ", PLATFORM.Name(), " generating tasks!");
Array<WsfQuantumTask> tasks = Array<WsfQuantumTask>();
if (ASSETS.Size() <= 0)
{
writeln_d("NO ASSETS!");
return tasks;
}
if (TRACKS.Size() <= 0)
{
writeln_d("NO TRACKS!");
}
#FILTER TRACKS DOWN TO ONLY TARGETS WE CARE ABOUT RIGHT NOW
#TODO - find fastest/furthest along asset (as likliest to be first engaged by threats)
Array<WsfTrack> filtered = FilterTracks(TRACKS, ASSETS);
if (mStaticRiskLevel == false)
{
UpdateRiskLevel(filtered);
}
# mDraw.SetColor(0.6, 0.6, 0.6); #gray
# mDraw.BeginLines();
# foreach(WsfTrack target in filtered)
# {
# writeln_d("T=", TIME_NOW, " drawing filtered: ", target.TargetName());
# mDraw.Vertex(PLATFORM);
# mDraw.Vertex(target.CurrentLocation());
# }
# mDraw.End();
if (filtered.Size() <= 0)
{
return tasks;
}
#DEBUG - draw the filtered tracks with a purple hull
mClusterManager.Draw(filtered, PROCESSOR.UpdateInterval(), Vec3.Construct(0.2,0.2,1.0), "hull");
# #DEBUG - draw line from asset centroid to filtered threats centroid
# mDraw.SetColor(1.0,1.0,0.0); #yellow?
# mDraw.SetLineStyle("solid");
# mDraw.SetLineSize(1);
# mDraw.BeginLines();
# mDraw.Vertex(AssetCentroid);
# mDraw.Vertex(FilteredCluster.MeanLocation());
# mDraw.End();
#CALCULATE IF THE FLIGHT EXECUTES ONE OR TWO TIMELINES
#first find if we have two+ shooters or not
int totalWeapons = 0;
mWeapons1 = 0;
mWeapons2 = 0;
bool haveTwoShooters = false;
foreach(WsfAssetPerception ASSET in ASSETS)
{
for (int i=0; i<ASSET.SystemCount(); i+=1)
{
#TODO - limit the consideration to only certain [better] weapons
# instead of just first weapon in system list
if (ASSET.SystemKind(i) == "weapon")
{
int weapons = ASSET.SystemQuantityRemaining(i);
totalWeapons += weapons;
if (mPassiveAllowed == true && mPassivePlayers[ASSET.Name()] == true)
{
break; #passive players will not fly "point" on a timeline nor shoot
}
if (weapons > mWeapons1)
{
if (mWeapons1 > mWeapons2)
{
mShooter2 = mShooter1;
mWeapons2 = mWeapons1;
}
mShooter1 = ASSET;
mWeapons1 = weapons;
}
else if (weapons > mWeapons2)
{
mShooter2 = ASSET;
mWeapons2 = weapons;
}
break;
}
}
}
if (filtered.Size() > totalWeapons)
{
writeln_d(PLATFORM.Name(), ", WARNING! More threats than this flight has weapons!");
}
if (filtered.Size() > (mWeapons1+mWeapons2))
{
writeln_d(PLATFORM.Name(), ", WARNING! More threats than two shooters have weapons! Enable all as shooters?");
}
haveTwoShooters = (mWeapons1 > 0 && mWeapons2 > 0);
#TODO - more intelligent calculation of azimuth presentation:
# find likely first shot location (from shooter 1 to biggest threat)
# sort all extrapolatead threats based on relative azimuth at this shot location
# figure out if the threats are presenting on azimuth and will require two timelines
#for now - sort filtered tracks on azimuth
# compare "width" of threats (left most to right most) to "length" of weapon
Array<double> bearings = Array<double>();
foreach(WsfTrack t in filtered)
{
double bearing = ASSETS[0].RelativeBearingTo(t.CurrentLocation());
bearings.PushBack(bearing);
}
SortTracksByValue(filtered, bearings);
#debug: print sorted (by bearing) order to screen
mDraw.SetDuration(PROCESSOR.UpdateInterval());
mDraw.SetColor(1.0,0.5,0.0); #orange?
mDraw.SetLineStyle("solid");
mDraw.SetTextSize(16);
for (int i=0; i<filtered.Size(); i+=1)
{
mDraw.BeginText(write_str(i));
mDraw.Vertex(filtered[i].CurrentLocation());
mDraw.End();
}
#calculate if the threats are presenting across a wide azimuth
bool AzimuthPresentation = true;
WsfTrack left = filtered.Front();
WsfTrack right = filtered.Back();
double width = left.CurrentLocation().GroundRangeTo(right.CurrentLocation());
double length = mMyWeaponRange;
#assume shooter 1 flies towards middle of angle separation
double halfAngle = MATH.ATan2(width/2.0,length);
if ((2.0*halfAngle) < mMaxTargetAzSeparation)
{
AzimuthPresentation = false;
}
if (mEnableTwoTimelines &&
haveTwoShooters && #only create two timelines if we have 2 shooters???
filtered.Size() > 1 && #two timelines only possible if more than 1 threat exists
( mWeapons1 < filtered.Size() || #if MUST use 2 shooters because of weapon counts
AzimuthPresentation) #OR if must use 2 shooters because of threat presentation
)
{
#create two timeline tasks
#first make mShooter1 the left most shooter
WsfGeoPoint p1 = filtered[0].CurrentLocation(); #left target
double d1 = mShooter1.Location().GroundRangeTo(p1);
double d2 = mShooter2.Location().GroundRangeTo(p1);
if (d2 < d1)
{
#swap 1 and 2 so mShooter1 refers to left shooter
int tWeapons; WsfAssetPerception tShooter;
tShooter = mShooter1; tWeapons = mWeapons1;
mShooter1 = mShooter2; mWeapons1 = mWeapons2;
mShooter2 = tShooter; mWeapons2 = tWeapons;
}
#TODO - consider group up threats on azimuth???
#TODO - consider other azimuth-based algorithms to separate the threats
#for now - make two clusters
WsfGeoPoint SeparateA;
WsfGeoPoint SeparateB;
Array<Array<WsfTrack>> clusters = mClusterManager.GetClusters(filtered);
#should be 2 clusters now
if (clusters.Size() != 2)
{
writeln("ERROR: cluster manager did not create two clusters! ABORT task generation!");
return tasks;
}
else
{
#draw separation lines
WsfGeoPoint a = mClusterManager.MeanLocation(clusters[0]);
WsfGeoPoint b = mClusterManager.MeanLocation(clusters[1]);
double az = a.TrueBearingTo(b);
double dist = a.GroundRangeTo(b);
#WsfGeoPoint c = WsfGeoPoint(a);
SeparateA = WsfGeoPoint(a);
SeparateA.OffsetRBE(dist/2.0,az,0.0);
#WsfGeoPoint d = WsfGeoPoint(c);
SeparateB = WsfGeoPoint(SeparateA);
SeparateA.OffsetRBE(DOR,az+90.0,0.0);
SeparateB.OffsetRBE(DOR,az-90.0,0.0);
# mDraw.SetColor(1.0,1.0,1.0); #white
# mDraw.SetLineStyle("solid");
# mDraw.SetLineSize(1);
# mDraw.BeginLines();
# mDraw.Vertex(a);
# mDraw.Vertex(b);
# mDraw.Vertex(SeparateA);
# mDraw.Vertex(SeparateB);
# mDraw.End();
}
for (int i = 0; i < clusters.Size(); i += 1 )
{
string shapeType = "hull";
if(Math.Mod(i,2) == 0)
{
shapeType = "star";
}
#Draw a green shape around the cluster for one update cycle
mClusterManager.Draw(clusters[i], PROCESSOR.UpdateInterval(), Vec3.Construct(0.0,1.0,0.0), shapeType);
WsfQuantumTask task = WsfQuantumTask.Construct(1.0, "TIMELINE");
task.SetTaskType("TIMELINE");
Array<WsfTrack> targets = Array<WsfTrack>();
for (int j=0; j<clusters[i].Size(); j+=1)
{
targets.PushBack(WsfTrack(clusters[i][j]));
}
task.SetAuxData("targets",targets);
task.SetAuxData("FSR",FSR);
task.SetAuxData("DOR",DOR);
task.SetAuxData("MAR",MAR);
task.SetAuxData("RISK",RISK);
task.SetAuxData("point_a",SeparateA);
task.SetAuxData("point_b",SeparateB);
task.SetAuxData("cluster_mean", mClusterManager.MeanLocation(clusters[i]));
task.UniqueId(mClusterManager.UniqueId(targets));
tasks.PushBack(task);
writeln_d("timeline task generated for cluster: ", mClusterManager.UniqueId(clusters[i]));
}
}
else
{
#create just one timeline task
WsfQuantumTask task = WsfQuantumTask.Construct(1.0, "TIMELINE");
task.SetTaskType("TIMELINE");
Array<WsfTrack> targets = Array<WsfTrack>();
foreach(WsfTrack t in filtered)
{
targets.PushBack(WsfTrack(t));
}
task.SetAuxData("targets",targets);
task.SetAuxData("FSR",FSR);
task.SetAuxData("DOR",DOR);
task.SetAuxData("MAR",MAR);
task.SetAuxData("RISK",RISK);
task.UniqueId(PLATFORM.Index()); #only timeline task this lead will generate
tasks.PushBack(task);
writeln_d("timeline task generated for all filtered tracks!");
}
return tasks;
end_script
script double FlightLeadEvaluation ( WsfQuantumTask TASK, WsfAssetPerception ASSET)
writeln_d(PLATFORM.Name(), " evaluating task type ", TASK.TaskType());
#TODO - include weapon capability in evaluation
# for now: just base value on range
if (TASK.TaskType() == "TIMELINE")
{
if (mPassiveAllowed == true && mPassivePlayers[ASSET.Name()] == true)
{
return 0; #passive players do not do weapon tasks for now
}
Array<WsfTrack> targets = (Array<WsfTrack>)TASK.AuxDataObject("targets");
if (targets.Size() > 0)
{
WsfGeoPoint clusterMeanPoint = mClusterManager.MeanLocation(targets);
double range = ASSET.Location().GroundRangeTo(clusterMeanPoint);
double value = 1.0 / (range * range);
if (mCountWeaponsInEval == true)
{
double weaponCount = 0;
for (int i=0; i<ASSET.SystemCount(); i+=1)
{
if (ASSET.SystemKind(i) == "weapon")
{
weaponCount += ASSET.SystemQuantityRemaining(i);
}
}
if (weaponCount >= targets.Size())
{
value *= 2.0;
}
}
return value;
}
}
return 0.0;
end_script
#show_task_messages
#script_debug_writes on
update_interval 10.0 sec
asset_representation platform
reallocation_strategy dynamic
#reallocation_strategy event
generator custom FlightLeadTaskGeneration
evaluator custom FlightLeadEvaluation
#allocator optimal_profit type TIMELINE
#allocator greedy_profit
allocator optimal_profit
allocator_extra_assets greedy_profit
#allocator_extra_tasks optimal_profit
#allocator_extra_assets optimal_profit
end_processor

View File

@@ -0,0 +1,122 @@
# ****************************************************************************
# 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.
# ****************************************************************************
processor FLIGHT_LEAD_DATA WSF_QUANTUM_TASKER_PROCESSOR
script_variables
#########################################################################
## for evaluating weapon tasks
Map<string,int> mKnownTargetTypes = Map<string,int>();
mKnownTargetTypes["STRIKER"] = 1;
mKnownTargetTypes["BLUE_STRIKER"] = 1;
mKnownTargetTypes["RED_STRIKER"] = 1;
#########################################################################
double FSR = 30 * MATH.M_PER_NM();
double DOR = 20 * MATH.M_PER_NM();
double MAR = 10 * MATH.M_PER_NM();
string RISK = "LOW";
bool mStaticRiskLevel = true;
end_script_variables
#on_initialize
#end_on_initialize
script Array<WsfQuantumTask> FlightLeadTaskGeneration (Array<WsfLocalTrack> TRACKS, Array<WsfAssetPerception> ASSETS )
Array<WsfQuantumTask> tasks = Array<WsfQuantumTask>();
extern Map<int, int> mGrinderPairings;
mGrinderPairings.Clear();
#if its us, then create weapon tasks for enemy tracks
for (int i=0; i<TRACKS.Size(); i=i+1)
{
WsfLocalTrack lt = TRACKS.Get(i);
if (lt.IsValid() && (!lt.SideValid() || lt.Side() != PLATFORM.Side()))
{
if (mKnownTargetTypes.Size() <= 0 || mKnownTargetTypes.Exists(lt.TargetType()))
{
#WsfQuantumTask task = WsfQuantumTask.Construct(1.0, "TIMELINE");
#task.SetUniqueId(lt.TargetIndex()); #use this for now, use cluster Id later
#WsfQuantumTask task = WsfQuantumTask.Construct(1.0, "TIMELINE", lt);
Array<WsfTrack> targets = Array<WsfTrack>();
targets.PushBack(WsfTrack(lt));
WsfQuantumTask task = WsfQuantumTask.Construct(1.0, "TIMELINE");
task.UniqueId(lt.TargetIndex()); #use this for now, use cluster Id later
task.SetAuxData("targets",targets);
task.SetAuxData("FSR",FSR);
task.SetAuxData("DOR",DOR);
task.SetAuxData("MAR",MAR);
task.SetAuxData("RISK",RISK);
tasks.PushBack(task);
writeln_d("timeline task generated for: ", lt.TargetName(), ", updated time: ", lt.UpdateTime());
# Generate a second timeline task for support
WsfQuantumTask taskSup = WsfQuantumTask.Construct(1.0, "TIMELINE");
taskSup.UniqueId(lt.TargetIndex() + 123456); //use this for now, use cluster Id later
taskSup.SetAuxData("targets",targets);
taskSup.SetAuxData("FSR",FSR);
taskSup.SetAuxData("DOR",DOR);
taskSup.SetAuxData("MAR",MAR);
taskSup.SetAuxData("RISK",RISK);
tasks.PushBack(taskSup);
writeln_d("timeline support task generated for: ", lt.TargetName(), ", updated time: ", lt.UpdateTime());
# Map task IDs to platforms doing grinder can look each other up
mGrinderPairings.Set(lt.TargetIndex(), lt.TargetIndex() + 123456);
mGrinderPairings.Set(lt.TargetIndex() + 123456, lt.TargetIndex());
}
}
}
return tasks;
end_script
script double FlightLeadEvaluation ( WsfQuantumTask TASK, WsfAssetPerception ASSET)
writeln_d(PLATFORM.Name(), " evaluating task type ", TASK.TaskType());
#TODO - include weapon capability in evaluation
# for now: just base value on range
if (TASK.TaskType() == "TIMELINE")
{
Array<WsfTrack> targets = (Array<WsfTrack>)TASK.AuxDataObject("targets");
if (targets.Size() > 0)
{
WsfTrack target = targets.Get(0);
writeln_d("evaluating timeline task on: ", target.TargetName(), ", updated time: ", target.UpdateTime());
double value = 1.0 / target.GroundRangeTo(ASSET.Location());
return value;
}
}
return 0;
end_script
#show_task_messages
script_debug_writes off
update_interval 10.0 sec
asset_representation platform
#reallocation_strategy event
reallocation_strategy dynamic
generator custom FlightLeadTaskGeneration
evaluator custom FlightLeadEvaluation
# #allocator greedy_isolated type TIMELINE
# #allocator greedy_isolated
# #allocator_extra_tasks optimal_profit
# allocator optimal_profit type TIMELINE
# allocator_extra_assets optimal_profit type TIMELINE
# allocator greedy_isolated
allocator optimal_profit
allocator_extra_assets optimal_profit
end_processor

View File

@@ -0,0 +1,8 @@
# ****************************************************************************
# 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.
# ****************************************************************************

View File

@@ -0,0 +1,197 @@
# ****************************************************************************
# 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.
# ****************************************************************************
#include_once processors/quantum_agents/aiai/behavior_engage_weapon_task_target.txt
include_once processors/quantum_agents/aiai/behavior_escort.txt
include_once processors/quantum_agents/aiai/behavior_evade.txt
include_once processors/quantum_agents/aiai/behavior_go_home.txt
include_once processors/quantum_agents/aiai/behavior_planned_route.txt
#include_once processors/quantum_agents/aiai/behavior_pursue-target_route_finder.txt
include_once processors/quantum_agents/aiai/behavior_pursue_weapon_task_target.txt
include_once behavior_fly_timeline.txt
include_once behavior_engage_on_timeline.txt
include_once air_combat_maneuvers.txt
include_once common_timeline_scripts.txt
processor TIMELINE_FLYER_PROC WSF_QUANTUM_TASKER_PROCESSOR
#show_task_messages
script_debug_writes off
update_interval 5 sec
script_variables
bool stallForSupport = true;
WsfDraw draw = WsfDraw();
end_script_variables
state COMMIT #should this be "SEARCH" or "MISSION"? meh...
on_entry
PLATFORM.Comment("COMMIT");
end_on_entry
behavior_tree
selector
behavior_node evade #YES, evade will likely be in every state
behavior_node planned_route
end_selector
end_behavior_tree
next_state APPROACH
# #NAC+ at this point the fighter has committed to the target and received a timeline?
# double curDist = GetTargetDistance(PLATFORM, PROCESSOR);
# string distStr = (string)curDist;
# #string distStr = (string)tgtDist;
# string timelineStr = "COMMIT " + distStr;
# PLATFORM.Comment(timelineStr);
# double timelineVal = GetTimeLineValueForKey(PROCESSOR,"FSR");
# writeln_d("timelineVal = ", timelineVal);
# if (curDist != -1 && curDist <= GetTimeLineValueForKey(PROCESSOR,"FSR"))
# {
# return true;
# }
return GetTimelineTask(PROCESSOR).IsValid();
end_next_state
end_state
state APPROACH
on_entry
PLATFORM.Comment("APPROACH");
end_on_entry
behavior_tree
selector
behavior_node evade
behavior_node fly_timeline
end_selector
end_behavior_tree
next_state SHOOT_MANEUVER
#Engage(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
double curDist = GetTargetDistance(PLATFORM, PROCESSOR);
# string distStr = (string)curDist;
# string timelineStr = "APPROACH " + distStr;
# PLATFORM.Comment(timelineStr);
if (curDist != -1 && curDist <= GetTimeLineValueForKey(PROCESSOR,"DOR") && !stallForSupport)
{
#Take the shot
WsfTrack targetTrack = GetNearestTimelineTarget(PLATFORM, PROCESSOR);
if (targetTrack.IsValid() && PLATFORM.WeaponCount() > 0)
{
WsfWeapon myWeap = PLATFORM.WeaponEntry(0);
if (myWeap.IsValid())
{
myWeap.Fire(targetTrack);
PLATFORM.Comment("Fired Shot!");
}
}
return true;
}
return false;
end_next_state
#NAC+ : Branch state here to maneuver if outside of OSR and need to slow down engagement or do something else
next_state DELAY_MANEUVER
double curDist = GetTargetDistance(PLATFORM, PROCESSOR);
# string distStr = (string)curDist;
# string timelineStr = "APPROACH delay" + distStr;
# PLATFORM.Comment(timelineStr);
if (curDist != -1 && curDist <= GetTimeLineValueForKey(PROCESSOR,"DOR") && stallForSupport)
{
#perform a delaying maneuver
return true;
}
return false;
end_next_state
end_state
state SHOOT_MANEUVER
on_entry
PLATFORM.Comment("SHOOT_MANEUVER");
end_on_entry
behavior_tree
selector
behavior_node evade
behavior_node fly_timeline
behavior_node planned_route
end_selector
#behavior_node engage_weapon_task_target
end_behavior_tree
next_state FLY_TO_DOR
#Engage(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
#Beam(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
#Crank(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
Drag(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
#PostHole(PLATFORM, "right");
double curDist = GetTargetDistance(PLATFORM, PROCESSOR);
# string distStr = (string)curDist;
# string timelineStr = "Shoot Maneuver " + distStr;
# PLATFORM.Comment(timelineStr);
if (curDist != -1 && curDist <= GetTimeLineValueForKey(PROCESSOR,"MAR"))
{
return true;
}
return false;
end_next_state
end_state
state DELAY_MANEUVER
on_entry
PLATFORM.Comment("DELAY_MANEUVER");
end_on_entry
next_state APPROACH
#Engage(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
#Beam(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
#Crank(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
Drag(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
stallForSupport = false;
#PostHole(PLATFORM, "right");
#PLATFORM.Comment("Stall Maneuver!");
return true;
end_next_state
end_state
state FLY_TO_DOR
on_entry
PLATFORM.Comment("FLY_TO_DOR");
end_on_entry
behavior_tree
#behavior_node pursue_weapon_task_target
behavior_node fly_timeline
end_behavior_tree
next_state GO_HOME
double curDist = GetTargetDistance(PLATFORM, PROCESSOR);
# string distStr = (string)curDist;
# string timelineStr = "FLY_TO_DOR " + distStr;
# PLATFORM.Comment(timelineStr);
if (curDist <= GetTimeLineValueForKey(PROCESSOR,"MAR"))
{
#PLATFORM.Comment("Reached MAR");
return true;
}
return false;
end_next_state
end_state
state GO_HOME
on_entry
PLATFORM.Comment("GO_HOME");
end_on_entry
behavior_tree
behavior_node planned_route
end_behavior_tree
next_state COMMIT
WsfWaypoint waypt = PLATFORM.Mover().Route().Front();
PLATFORM.GoToLocation(waypt);
# PLATFORM.Comment("Flying Home");
return false;
end_next_state
end_state
end_processor

View File

@@ -0,0 +1,328 @@
# ****************************************************************************
# 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.
# ****************************************************************************
include_once behavior_timeline_reposition.txt #drag to restart further back on timeline
include_once behavior_timeline_delay.txt #maneuver to delay enemy's engagement
include_once behavior_timeline_engage.txt #intercept/shoot timeline targets
#include_once behavior_timeline_support.txt #fly support role on a timeline (usually behind lead shooter role)
include_once behavior_weapon_support.txt #crank & support weapon
include_once behavior_planned_route.txt #no timeline tasks, just return to route
include_once behavior_evade.txt #evade iminent threats (missiles)
include_once behavior_go_home.txt #disengage, go back to initial location
#include_once behavior_fly_timeline.txt
processor TIMELINE_PROC WSF_QUANTUM_TASKER_PROCESSOR
update_interval 2 sec
script_variables
double mGeesToTurnWith = 6.0; #update this however intelligently you can (based on risk level?)
#double mOffsetAngle = 45.0; #TODO - calculate half sensor angle?
double mOffsetAngle = 30.0; #TODO - calculate half sensor angle?
double mDelaySpeed = 204; #0.6
double mEngageSpeed = 237; #0.8
double mDragSpeed = 309; #1.2
double mDefaultAltitude = 35000 * MATH.M_PER_FT();
#double mDragAltitude = 30000 * MATH.M_PER_FT();
double mDragAltitude = 35000 * MATH.M_PER_FT(); #testing
bool mConsiderPeersWeapons = true; #do not shoot if peers already are
#bool mRequiredToSupportWeapons = true; #TODO - update dynamically somehow?
bool mRequiredToSupportWeapons = true; #TODO - update dynamically somehow?
double DefaultPercentRangeMax = 0.80; # don't launch unless within this percent of Rmax
double DefaultPercentRangeMin = 1.20; # don't launch unless beyond this percent of Rmin
##weapon + threat specific shooting parameters
##specify an Rmax based on which weapon used and which threat engaged
#Map<string, Map<string, double>> WeaponThreatRmaxMap = Map<string, Map<string, double>>();
#WeaponThreatRmaxMap["base_weapon"] = Map<string, double>();
#WeaponThreatRmaxMap["base_weapon"].Set("fighter", 0.80);
bool mDrawSeparationData = true;
string mSeparateSide = "LEFT"; #this is dynamically updated based on engagement axis
#do not edit these:
bool mSeparateEngagement = false;
double mEngagementTrueBearing = -1.0; #valid if in [0,360], update based on engagement axis
WsfDraw mDraw = WsfDraw();
Array<WsfLocalTrack> mTimeLineLocalTargets;
WsfLocalTrack mNearestTimeLineTarget;
Array<WsfLocalTrack> mTimeLineLocalTargetsToShoot = Array<WsfLocalTrack>();
end_script_variables
on_update
#update these if necessary
mSeparateEngagement = false;
mEngagementTrueBearing = -1.0;
#populate shared variables
WsfTask timeline = GetTimeLineTask(PROCESSOR);
mTimeLineLocalTargets = GetTimeLineTaskLocalTargets(PLATFORM, timeline);
Array<double> times = Array<double>();
foreach(WsfLocalTrack target in mTimeLineLocalTargets)
{
double time = TimeToReachPoint(target, PLATFORM.Location(), mGeesToTurnWith);
times.PushBack(time);
}
SortLocalTracksByValue(mTimeLineLocalTargets, times);
mNearestTimeLineTarget = GetNearestTimeLineTaskLocalTarget(PLATFORM, timeline);
if (timeline.IsValid() &&
timeline.AuxDataExists("point_a") &&
timeline.AuxDataExists("point_b") )
{
WsfGeoPoint SeparateA = (WsfGeoPoint)timeline.AuxDataObject("point_a");
WsfGeoPoint SeparateB = (WsfGeoPoint)timeline.AuxDataObject("point_b");
if (SeparateA.IsValid() && SeparateB.IsValid())
{
WsfGeoPoint near;
WsfGeoPoint far;
if (PLATFORM.GroundRangeTo(SeparateA) < PLATFORM.GroundRangeTo(SeparateB))
{
near = SeparateA;
far = SeparateB;
}
else
{
near = SeparateB;
far = SeparateA;
}
mDraw.SetLayer("timeline_proc");
mDraw.SetId("timeline_proc");
mDraw.SetDuration(PROCESSOR.UpdateInterval());
mDraw.SetLineSize(1);
mDraw.SetLineStyle("solid");
Array<WsfTrack> targets = (Array<WsfTrack>)timeline.AuxDataObject("targets");
#check if platform is on the correct side of separation line
WsfGeoPoint clusterMeanLocation = (WsfGeoPoint)timeline.AuxDataObject("cluster_mean");
double refAz = near.TrueBearingTo(far);
#save this engagement axis true bearing!
mSeparateEngagement = true;
mEngagementTrueBearing = MATH.NormalizeAngle0_360(refAz);
double ownAz = near.TrueBearingTo(PLATFORM.Location()) - refAz;
double tgtAz = near.TrueBearingTo(clusterMeanLocation) - refAz;
ownAz = MATH.NormalizeAngleMinus180_180(ownAz);
tgtAz = MATH.NormalizeAngleMinus180_180(tgtAz);
#save off turn direction
if (tgtAz < 0) {
mSeparateSide = "LEFT";
} else {
mSeparateSide = "RIGHT";
}
bool CorrectSide = ((ownAz * tgtAz) > 0);
#draw lines for separation divide
mDraw.SetColor(0.0, 0.0, 0.0); #black
mDraw.BeginLines();
mDraw.Vertex(near);
mDraw.Vertex(far);
mDraw.End();
if (CorrectSide) {
mDraw.SetColor(0.0, 1.0, 1.0); #aqua
} else {
mDraw.SetColor(1.0, 0.0, 0.0); #red
}
#draw line from me to target center of gravity
mDraw.BeginLines();
mDraw.Vertex(PLATFORM);
#mDraw.Vertex(targetCluster.MeanLocation());
mDraw.Vertex(mTimeLineLocalTargets.Front().CurrentLocation());
mDraw.End();
# mDraw.SetEllipseMode("line");
# mDraw.BeginCircle(0.0, 4.0 * MATH.M_PER_NM());
# mDraw.Vertex(PLATFORM);
# mDraw.End();
}
}
#update the "mTimeLineLocalTargetsToShoot" list
mTimeLineLocalTargetsToShoot.Clear();
if (timeline.IsValid())
{
string RISK = timeline.AuxDataString("RISK");
foreach(WsfLocalTrack target in mTimeLineLocalTargets)
{
writeln_d("T=",TIME_NOW," ", PLATFORM.Name(), " checking if shootable: ", target.TargetName());
if (FiredOn(PLATFORM, target, mConsiderPeersWeapons))
{
writeln_d(" NO - already fired on!");
continue;
}
if (mRequiredToSupportWeapons == true && RISK != "HIGH") #can support any shot when risk is high (won't be dragging)
{
double gate = timeline.AuxDataDouble("DOR"); #range (meters)
if (RISK == "MEDIUM")
{
gate = timeline.AuxDataDouble("MAR"); #range (meters)
}
#calculate if we have time or range to support a shot to this target:
###################################################################
###################################################################
###################################################################
#figure out weapon range & time of flight
double weaponMaxRange = 0.0;
WsfWeapon maxRangeWeapon;
for (int i=0; i < PLATFORM.WeaponCount(); i+=1)
{
WsfWeapon weapon = PLATFORM.WeaponEntry(i);
if (WeaponCapableAvailableAgainstThreat(weapon, target))
{
double weaponRange = MaxRange(PLATFORM,weapon,target);
if (weaponRange > weaponMaxRange)
{
weaponMaxRange = weaponRange;
maxRangeWeapon = weapon;
}
}
}
if (!maxRangeWeapon.IsValid())
{
writeln_d(" NO - do not have a weapon for the target");
continue; #no weapon for this target
}
#estimate time before we can shoot
#hypothetical shot
#estimate time cranking (based on weapon travel time)
#does this leave us enough time to turn to drag?
double weaponTOF = TimeToHit(PLATFORM, maxRangeWeapon, target);
if (weaponTOF <= 0)
{
writeln_d(" NO - could not find TOF data for weapon");
#good weapon data no found
continue;
}
double firingRange = DefaultPercentRangeMax * weaponMaxRange;
writeln_d(" INFO - weapon \"", maxRangeWeapon.Name(), "\" firing range = ", firingRange, " TOF = ", weaponTOF);
double targetRange = PLATFORM.GroundRangeTo(target.CurrentLocation());
double rangeToGo = targetRange - firingRange;
double timeTilShot = 0.0;
double range = PLATFORM.GroundRangeTo(mNearestTimeLineTarget.CurrentLocation());
if (rangeToGo > 0)
{
#assume target continues on its current heading
#assume nearest threat closes towards you
#assume we close towards target
double myRelativeBearingToThreat = MATH.Fabs(MATH.NormalizeAngleMinus180_180(PLATFORM.TrueBearingTo(mNearestTimeLineTarget) - PLATFORM.TrueBearingTo(target)));
double targetRelativeBearingToMe = target.RelativeBearingTo(PLATFORM);
double closingSpeedToTarget = PLATFORM.Speed() + MATH.Cos(targetRelativeBearingToMe) * target.Speed();
double closingSpeedOfThreat = mNearestTimeLineTarget.Speed() + MATH.Cos(myRelativeBearingToThreat) * PLATFORM.Speed();
if (closingSpeedToTarget <= 0)
{
writeln_d(" NO - negative closing speed to target with range remaining to shoot");
continue;
}
writeln_d(" INFO - closing to target = ", closingSpeedToTarget, " closing of threat = ", closingSpeedOfThreat);
#subtract off closing distance to nearest threat during the time before shot
timeTilShot = rangeToGo / closingSpeedToTarget;
double temp = range;
range -= (timeTilShot * closingSpeedOfThreat);
writeln_d(" INFO - starting threat range = ", temp, " range after closing to shot loc = ", range);
}
#subtract off closing distance to nearest target during the time of weapon's flight
double closingSpeedOfThreat = (mDelaySpeed * MATH.Cos(mOffsetAngle)) + mNearestTimeLineTarget.Speed();
double temp = range;
range -= (weaponTOF * closingSpeedOfThreat);
writeln_d(" INFO - range at shot = ", temp, " range after supporting shot = ", range);
# #calculate time to turn to drag (after cranking)
# Array<double> vals = Array<double>();
# double turnAngle = 180 - mOffsetAngle;
# TurnAngleTimeAndDistance(turnAngle, mEngageSpeed, mGeesToTurnWith, vals);
# double turnTime = vals[0];
# double distSep = vals[1];
# #subtract off closing distance to nearest target during the time of turning to drag
# range += distSep;
# range -= (turnTime*mNearestTimeLineTarget.Speed());
if (range >= gate)
{
writeln_d(" YES - have time & range to support the shot.");
mTimeLineLocalTargetsToShoot.PushBack(target);
}
else
{
writeln_d(" NO - do not have time & range to support the shot.");
##TODO - should we break here?
## its highly unlikely futher away targets will be shootable
#break;
}
###################################################################
###################################################################
###################################################################
}
else if (mRequiredToSupportWeapons == false || RISK == "HIGH")
{
writeln_d(" YES - risk is high or not required to support.");
#all targets are for shooting (if not already shot on)
mTimeLineLocalTargetsToShoot.PushBack(target);
}
}
}
end_on_update
behavior_tree
selector
behavior_node evade #
behavior_node go_home #
behavior_node timeline_reposition #
#behavior_node timeline_support #TODO - integrate kyle's grinder behavior support stuff
behavior_node timeline_delay #TODO - integrate nick's delay script
behavior_node timeline_engage #TODO - only shoot if there is time to support it
behavior_node weapon_support #
behavior_node planned_route #
end_selector
end_behavior_tree
end_processor

View File

@@ -0,0 +1,696 @@
# ****************************************************************************
# 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.
# ****************************************************************************
#include_once processors/quantum_agents/aiai/behavior_engage_weapon_task_target.txt
include_once processors/quantum_agents/aiai/behavior_escort.txt
include_once processors/quantum_agents/aiai/behavior_evade.txt
include_once processors/quantum_agents/aiai/behavior_go_home.txt
include_once processors/quantum_agents/aiai/behavior_planned_route.txt
#include_once processors/quantum_agents/aiai/behavior_pursue-target_route_finder.txt
include_once processors/quantum_agents/aiai/behavior_pursue_weapon_task_target.txt
include_once behavior_fly_timeline.txt
include_once behavior_engage_on_timeline.txt
include_once air_combat_maneuvers.txt
include_once common_timeline_scripts.txt
processor TIMELINE_PROC_FSM WSF_QUANTUM_TASKER_PROCESSOR
#show_task_messages
script_debug_writes off
update_interval 5 sec
script_variables
WsfDraw draw = WsfDraw();
int delayForSupport = 0;
double currentManeuverHeading = 0.0;
bool delayManeuverActive = false;
double mGeesToTurnWith = 6.0; #TODO - what to use?
#debug stuff:
WsfDraw mDraw = WsfDraw();
end_script_variables
//return values: 0 = don't delay
// 1 = perform a crank
// 2 = perform a beam
// 3 = perform a drag
// 4 = perform a posthole
script int EvaluateDelay()
double myPos = GetTargetDistance(PLATFORM, PROCESSOR);
WsfTrack myTrack = GetTimeLineTaskTargetTrack(PLATFORM, GetTimeLineTask(PROCESSOR));
//figure out if this platform is the closest to the target track
if (myTrack)
{
bool amLead = true;
Map<WsfPlatform, double> peerDistMap = Map<WsfPlatform, double>();
WsfPlatformList peerList = PLATFORM.Peers();
if (peerList.Count() == 0)
{
//find out if you can delay until the DOR, if so do that
double crankTime = TimeToPerformCrank(PLATFORM, myTrack);
double beamTime = TimeToPerformBeam(PLATFORM, myTrack);
double dragTime = TimeToPerformDrag(PLATFORM, myTrack);
double postHoleTime = 0.0;
crankTime = (crankTime * 2) + TIME_NOW; //multiply by 2 to reflect going back to starting position
beamTime = (beamTime * 2) + TIME_NOW; //multiply by 2 to reflect going back to starting position
dragTime = (dragTime * 2) + TIME_NOW; //multiply by 2 to reflect going back to starting position
postHoleTime = dragTime; //a 180 turn followed by a 180 turn is basically a posthole
Array<double> maneuverTimes = Array<double>();
maneuverTimes.PushBack(crankTime);
maneuverTimes.PushBack(beamTime);
maneuverTimes.PushBack(dragTime);
maneuverTimes.PushBack(postHoleTime);
double DORDist = GetTimeLineValueForKey(PROCESSOR, "DOR");
double FSRDist = GetTimeLineValueForKey(PROCESSOR, "FSR");
double furthestGate;
double closestGate;
if (DORDist > FSRDist)
{
furthestGate = FSRDist;
closestGate = DORDist; //the terminology is tricky here, closest means closest in distance to the PLATFORM but furthest from the target
}
else
{
furthestGate = DORDist;
closestGate = FSRDist;
}
int curSafeIdx = 0;
int curIdx = 1;
foreach(double futureTime in maneuverTimes)
{
WsfGeoPoint myLoc = PLATFORM.FutureLocation(futureTime);
WsfGeoPoint targetLoc = myTrack.Target().FutureLocation(futureTime);
if (myLoc.IsValid() && targetLoc.IsValid())
{
double futureDist = myLoc.SlantRangeTo(targetLoc);
if (futureDist > furthestGate && futureDist < closestGate) //we're inside the DOR
{
curSafeIdx = curIdx;
}
}
curIdx = curIdx + 1;
}
if (curSafeIdx == 0)
{
return 0;
}
else
{
if (curSafeIdx == 3) //don't allow drag maneuver right now
{
curSafeIdx = 2;
}
return curSafeIdx;
}
}
foreach (WsfPlatform curPlat in peerList)
{
double dist = curPlat.SlantRangeTo(myTrack);
peerDistMap[curPlat] = dist;
if (dist < myPos)
{
amLead = false;
}
}
if (amLead)
{
//check to see if the closest follower is at least in FSR (or some other pre-determined distance on the timeline)
double FSRDist = GetTimeLineValueForKey(PROCESSOR, "FSR");
bool foundInFSR = false;
//loop through the map of peers and if all of the peers are outside of the FSRDist, we need to delay
foreach (WsfPlatform peer in peerList)
{
double peerDist = peerDistMap[peer];
if (peerDist <= FSRDist)
{
foundInFSR = true;
break;
}
}
if (foundInFSR)
{
return 0;
}
else
{
//no peer close enough, evaluate which delay tactic to do
double crankTime = TimeToPerformCrank(PLATFORM, myTrack);
double beamTime = TimeToPerformBeam(PLATFORM, myTrack);
double dragTime = TimeToPerformDrag(PLATFORM, myTrack);
double postHoleTime = 0.0;
crankTime = (crankTime * 2) + TIME_NOW; //multiply by 2 to reflect going back to starting position
beamTime = (beamTime * 2) + TIME_NOW; //multiply by 2 to reflect going back to starting position
dragTime = (dragTime * 2) + TIME_NOW; //multiply by 2 to reflect going back to starting position
postHoleTime = dragTime;
//figure out how long it will take for the peer to get into FSRDist
//take into account the heading and speed of the track as well as the heading and speed of the peer
//evaluate each platform in 1 second intervals until a peer is in the FSR
bool peerInFSR = false;
double peerTimeToFSR = MATH.DOUBLE_MAX();
double futureTime = TIME_NOW + 1;
if (peerList.Count())
{
while (!peerInFSR)
{
foreach (WsfPlatform peer in peerList)
{
WsfGeoPoint peerLoc = peer.FutureLocation(futureTime);
WsfGeoPoint targetLoc = myTrack.Target().FutureLocation(futureTime);
if (peerLoc.IsValid() && targetLoc.IsValid())
{
double futureDist = peerLoc.SlantRangeTo(targetLoc);
if (futureDist <= FSRDist)
{
peerTimeToFSR = futureTime - TIME_NOW;
peerInFSR = true;
}
}
}
futureTime = futureTime + 1;
//if futuretime is beyond the length of this sim, stop
if (futureTime > WsfSimulation.EndTime())
{
break;
}
}
}
//peer never reached FSR, return 0?
if (!peerInFSR)
{
return 0;
}
if (peerTimeToFSR >= postHoleTime)
{
return 4; //perform a posthole
}
else if (peerTimeToFSR < postHoleTime && peerTimeToFSR >= dragTime)
{
return 3; //perform a drag
}
else if (peerTimeToFSR < dragTime && peerTimeToFSR >= beamTime)
{
return 2; //perform a beam
}
else
{
return 1; //perform a crank
}
}
}
else
{
return 0;
}
}
return 0;
end_script
script bool ShouldApproach()
#do we have a timeline task?
return GetTimelineTask(PROCESSOR).IsValid();
end_script
script bool ShouldDelay()
#compare risk level with range to nearest target
#only delay if risk MED: past DOR and (want to shoot but out of range or waiting on existing shot)
# risk HIGH: past MAR and (want to shoot but out of range or waiting on existing shot)
WsfTask timeline = GetTimelineTask(PROCESSOR);
if (!timeline.IsValid())
{
return false;
}
WsfTrack target = GetNearestTimelineTarget(PLATFORM, PROCESSOR);
if (!target.IsValid())
{
return false;
}
string RISK = timeline.AuxDataString("RISK");
if (RISK == "HIGH")
{
double MAR = timeline.AuxDataDouble("MAR"); #range (meters)
double slantRange = PLATFORM.SlantRangeTo(target);
return (slantRange <= MAR);
}
else if (RISK == "MEDIUM")
{
double DOR = timeline.AuxDataDouble("DOR"); #range (meters)
double slantRange = PLATFORM.SlantRangeTo(target);
return (slantRange <= DOR);
}
else #if (RISK == "LOW")
{
//check to see if there is enough time to delay before it hits the DOR
delayForSupport = EvaluateDelay();
double DOR = timeline.AuxDataDouble("DOR");
if (delayForSupport > 0 && PLATFORM.SlantRangeTo(target) <= DOR)
{
return true;
}
return false;
}
return false;
end_script
script bool ShouldShoot()
#TODO - more work required here
#look at time required to turn, speed up, & pitch up for shot
#compare that to time until shot range will be reached
#use optimal shot range somehow?
#base this on current weapons &/or best weapon for the threat?
#cover all timeline targets - do any not have weapons on them?
WsfTask timeline = GetTimelineTask(PROCESSOR);
if (!timeline.IsValid())
{
return false;
}
WsfTrack target = GetNearestTimelineTarget(PLATFORM, PROCESSOR);
if (!target.IsValid())
{
return false;
}
double FSR = timeline.AuxDataDouble("FSR");
return (PLATFORM.SlantRangeTo(target) <= FSR);
end_script
script bool ShouldReset()
//NAC+ DEBUG This still needs some work
return false;
//NAC-
#look at time required to turn & leave & compare to time until next gate
#gate determined by risk level (LOW risk gate = DOR, MEDIUM risk gate = MAR)
WsfTask timeline = GetTimelineTask(PROCESSOR);
if (!timeline.IsValid())
{
return false;
}
WsfTrack target = GetNearestTimelineTarget(PLATFORM, PROCESSOR);
if (!target.IsValid())
{
return false;
}
string RISK = timeline.AuxDataString("RISK");
if (RISK == "HIGH")
{
return false;
}
else if (RISK == "MEDIUM")
{
#calculate if its necessary to turn before the MAR now
double turnTime = TimeToTurnFrom(PLATFORM, PLATFORM.Speed(), target, mGeesToTurnWith);
double MAR = timeline.AuxDataDouble("MAR"); #range (meters)
double closingSpeed = PLATFORM.ClosingSpeedOf(target);
double slantRange = PLATFORM.SlantRangeTo(target);
double timeUntilMAR = (slantRange-MAR)/closingSpeed;
return (turnTime >= timeUntilMAR);
}
else #if (RISK == "LOW")
{
#calculate if its necessary to turn before the DOR now
double turnTime = TimeToTurnFrom(PLATFORM, PLATFORM.Speed(), target, mGeesToTurnWith);
double DOR = timeline.AuxDataDouble("DOR"); #range (meters)
double closingSpeed = PLATFORM.ClosingSpeedOf(target);
double slantRange = PLATFORM.SlantRangeTo(target);
double timeUntilDOR = (slantRange-DOR)/closingSpeed;
return (turnTime >= timeUntilDOR);
}
return false;
end_script
script bool ShouldGoHome()
#check weapons, fuel, force ratios, etc?
return false;
end_script
script bool ShouldSupport()
#are there weapons to support & am I allowed to emit to support them
return false;
end_script
script void DrawTimeLine()
WsfTrack target = GetNearestTimeLineTarget(PLATFORM, PROCESSOR);
if (target.IsValid())
{
double FSRDist = GetTimeLineValueForKey(PROCESSOR, "FSR");
double DORDist = GetTimeLineValueForKey(PROCESSOR, "DOR");
double MARDist = GetTimeLineValueForKey(PROCESSOR, "MAR");
WsfGeoPoint platLoc = PLATFORM.Location();
WsfGeoPoint tgtLocFSR = target.Target().Location();
WsfGeoPoint tgtLocDOR = target.Target().Location();
WsfGeoPoint tgtLocMAR = target.Target().Location();
WsfTrack PlatformAsTrack = WsfTrack();
PlatformAsTrack.SetTarget(PLATFORM.Name());
PlatformAsTrack.SetLocation(platLoc);
//double headingFromTargetToPlatform = target.Target().RelativeHeadingOf(PlatformAsTrack);
double headingFromTargetToPlatform = target.Target().Location().TrueBearingTo(PLATFORM.Location());
//headingFromTargetToPlatform = MATH.Fabs(headingFromTargetToPlatform) + target.Target().Heading();
//extrapolate the tgtLoc point a distance of FSRDist along a heading in the direction of the PLATFORM
tgtLocFSR.Extrapolate(headingFromTargetToPlatform, FSRDist);
tgtLocDOR.Extrapolate(headingFromTargetToPlatform, DORDist);
tgtLocMAR.Extrapolate(headingFromTargetToPlatform, MARDist);
mDraw.SetDuration(5.0);
mDraw.SetColor(1.0, 0.557, 0.0314);
mDraw.SetLineSize(3);
mDraw.BeginLines();
mDraw.Vertex(PLATFORM);
mDraw.Vertex(target.Target());
mDraw.End();
//mDraw.SetPointSize(9);
//mDraw.BeginPoints();
//mDraw.SetColor(1.0, 0.0, 1.0);
//mDraw.Vertex(tgtLocFSR);
//mDraw.SetColor(0.0, 1.0, 0.0);
//mDraw.Vertex(tgtLocDOR);
//mDraw.SetColor(1.0, 1.0, 1.0);
//mDraw.Vertex(tgtLocMAR);
//mDraw.End();
WsfGeoPoint FSROne = WsfGeoPoint(tgtLocFSR);
FSROne.Extrapolate(0.0, 10000.0);
WsfGeoPoint FSRTwo = WsfGeoPoint(tgtLocFSR);
FSRTwo.Extrapolate(180.0, 10000.0);
WsfGeoPoint DOROne = WsfGeoPoint(tgtLocDOR);
DOROne.Extrapolate(0.0, 10000.0);
WsfGeoPoint DORTwo = WsfGeoPoint(tgtLocDOR);
DORTwo.Extrapolate(180.0, 10000.0);
WsfGeoPoint MAROne = WsfGeoPoint(tgtLocMAR);
MAROne.Extrapolate(0.0, 10000.0);
WsfGeoPoint MARTwo = WsfGeoPoint(tgtLocMAR);
MARTwo.Extrapolate(180.0, 10000.0);
mDraw.SetLineSize(5);
mDraw.SetColor(0.0, 1.0, 0.0);
mDraw.BeginLines();
mDraw.Vertex(FSROne);
mDraw.Vertex(FSRTwo);
mDraw.End();
mDraw.SetColor(1.0, 1.0, 0.0);
mDraw.BeginLines();
mDraw.Vertex(DOROne);
mDraw.Vertex(DORTwo);
mDraw.End();
mDraw.SetColor(1.0, 0.0, 0.0);
mDraw.BeginLines();
mDraw.Vertex(MAROne);
mDraw.Vertex(MARTwo);
mDraw.End();
//mDraw.SetTextSize(25);
//mDraw.SetColor(1.0, 1.0, 1.0);
//mDraw.SetLineStyle("solid");
//string fsrTxt = "FSR";
//mDraw.BeginText(fsrTxt);
// mDraw.Vertex(PLATFORM.Location());
//mDraw.End();
}
end_script
script bool ShouldSupportWeapon()
#this (current state) contains script to perform in the state
//if a shot has been fired, support it?
WsfTrack target = GetTimeLineTaskTargetTrack(PLATFORM, GetTimeLineTask(PROCESSOR));
if (target.IsValid())
{
if (PLATFORM.WeaponsActiveFor(target.TrackId()) > 0)
{
Crank(PLATFORM, target, 35, PLATFORM.Speed());
return true;
}
else if (PLATFORM.WeaponsPendingFor(target.TrackId()) > 0)
{
Crank(PLATFORM, target, 35, PLATFORM.Speed());
return true;
}
else
{
return false;
}
}
return false;
end_script
#TODO - how to do things like evade behavior from each state?
# have an EVADE state? maybe?
on_update
//DrawTimeLine();
end_on_update
###########################################################
## ##
## BEGIN STATE MACHINE ##
## ##
###########################################################
state COMMIT
#picture building, getting target, tracking target
on_entry
PLATFORM.Comment("COMMIT");
end_on_entry
behavior_tree
selector
behavior_node evade #TODO - move to evade state???
behavior_node planned_route #TODO - move to this state!
end_selector
end_behavior_tree
next_state COMMIT
#this (current state) contains script to perform in the state
#fly planned route or formation
return false;
end_next_state
#best order for checking these?
#current opinion, check: DELAY before APPROACH
# RESET before APPROACH
# SHOOT before DELAY
# RESET before SHOOT?????
next_state RESET return ShouldReset(); end_next_state
next_state SHOOT return ShouldShoot(); end_next_state
next_state DELAY return ShouldDelay(); end_next_state
next_state APPROACH return ShouldApproach(); end_next_state
end_state
state APPROACH
#main state to start in once we have a timeline task (which should contain targets)
#bulk of this state is intercepting nearest timeline target that isn't currently fired upon
#basic times to approach:
# if risk low: only when outside of DOR
# if risk med: only when outside of MAR -> chance to delay when out of weapons range or waiting on existing shot
# if risk high: only all the time -> chance to delay when out of weapons range or waiting on existing shot
#NOTE: switch to SHOOT when in weapons range
on_entry
PLATFORM.Comment("APPROACH");
end_on_entry
behavior_tree
selector
behavior_node evade
behavior_node fly_timeline #TODO - move this script to inside this state?
end_selector
end_behavior_tree
next_state APPROACH
#this (current state) contains script to perform in the state
return false;
end_next_state
next_state RESET return ShouldReset(); end_next_state
next_state DELAY return ShouldDelay(); end_next_state
next_state SHOOT return ShouldShoot(); end_next_state
end_state
state SHOOT
#once entered, will setup & take a shot
#balance this by limiting the shoot behavior:
# add in conditions (speed+orientation) to it's precondition
on_entry
PLATFORM.Comment("SHOOT");
end_on_entry
behavior_tree
selector
behavior_node evade
behavior_node fly_timeline
behavior_node planned_route
end_selector
behavior_node engage_on_timeline #TODO - move this script to inside this state?
end_behavior_tree
next_state SHOOT
return false;
end_next_state
next_state SUPPORT_WEAPON return ShouldSupportWeapon(); end_next_state
next_state RESET return ShouldReset(); end_next_state
next_state DELAY return ShouldDelay(); end_next_state
next_state APPROACH return ShouldApproach(); end_next_state
end_state
state SUPPORT_WEAPON
next_state SUPPORT_WEAPON
return ShouldSupportWeapon();
end_next_state
//next_state SHOOT return ShouldShoot(); end_next_state
//next_state DELAY return ShouldDelay(); end_next_state
next_state APPROACH return ShouldApproach(); end_next_state
end_state
state DELAY
on_entry
PLATFORM.Comment("DELAY");
end_on_entry
#only delay if risk MED: past DOR and (want to shoot but out of range or waiting on existing shot)
# risk HIGH: past MAR and (want to shoot but out of range or waiting on existing shot)
next_state DELAY
if (delayForSupport != 0 && delayManeuverActive)
{
//check current heading vs goal heading
double curHeading = PLATFORM.Heading();
if (curHeading < 0)
{
curHeading = 360 + curHeading;
}
if (MATH.Fabs(curHeading - currentManeuverHeading) <= 1) //give a 1 degree margin of error
{
delayManeuverActive = false;
return false;
}
}
#this (current state) contains script to perform in the state
if (delayForSupport == 1 && !delayManeuverActive)
{
PLATFORM.Comment("DELAY CRANK");
currentManeuverHeading = Crank(PLATFORM, GetNearestTimeLineTarget(PLATFORM, PROCESSOR), 35, PLATFORM.Speed());
delayManeuverActive = true;
return true;
}
else if (delayForSupport == 2 && !delayManeuverActive)
{
PLATFORM.Comment("DELAY BEAM");
currentManeuverHeading = Beam(PLATFORM, GetNearestTimeLineTarget(PLATFORM, PROCESSOR));
delayManeuverActive = true;
return true;
}
else if (delayForSupport == 3 && !delayManeuverActive)
{
PLATFORM.Comment("DELAY DRAG");
currentManeuverHeading = Drag(PLATFORM, GetNearestTimeLineTarget(PLATFORM, PROCESSOR), PLATFORM.Speed(), PLATFORM.Altitude(), 6.0);
delayManeuverActive = true;
return true;
}
else if (delayForSupport == 4 && !delayManeuverActive)
{
PLATFORM.Comment("DELAY POSTHOLE");
currentManeuverHeading = PostHole(PLATFORM, "right");
delayManeuverActive = true;
return true;
}
else #(delayForSupport == 0)
{
if (delayManeuverActive)
{
return true;
}
else
{
PLATFORM.Comment("NO DELAY");
return false;
}
}
return false;
end_next_state
next_state RESET return ShouldReset(); end_next_state
next_state SHOOT return ShouldShoot(); end_next_state
next_state APPROACH return ShouldApproach(); end_next_state
end_state
state SUPPORT
#this is basically a CRANK state for supporting active weapons while risk level allows it
#is this state needed?
on_entry
PLATFORM.Comment("SUPPORT");
end_on_entry
next_state SUPPORT
#this (current state) contains script to perform in the state
Crank(PLATFORM, GetNearestTimelineTarget(PLATFORM,PROCESSOR));
return false;
end_next_state
next_state RESET return ShouldReset(); end_next_state
next_state SHOOT return ShouldShoot(); end_next_state
next_state DELAY return ShouldDelay(); end_next_state
next_state APPROACH return ShouldApproach(); end_next_state
end_state
state RESET
#this is basically a DRAG state for getting back & ready for future shots
on_entry
PLATFORM.Comment("RESET");
end_on_entry
next_state RESET
#this (current state) contains script to perform in the state
Drag(PLATFORM, GetNearestTimelineTarget(PLATFORM,PROCESSOR), PLATFORM.Speed(), 6.0);
return false;
end_next_state
next_state SHOOT return ShouldShoot(); end_next_state
next_state DELAY return ShouldDelay(); end_next_state
next_state APPROACH return ShouldApproach(); end_next_state
end_state
state HOME
#done, go home
on_entry
PLATFORM.Comment("HOME");
end_on_entry
next_state HOME
#no breaking out of this state? meh...
WsfWaypoint waypt = PLATFORM.Mover().Route().Front();
PLATFORM.GoToLocation(waypt);
return true;
end_next_state
end_state
end_processor

View File

@@ -0,0 +1,388 @@
# ****************************************************************************
# 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.
# ****************************************************************************
script_struct WeaponData
script_variables
string type = "";
double rangeMin = 0; #meters
double rangeMax = 0; #meters
bool onlyUseInRange = true;
double averageSpeed = 0; #meters/second
double maxTimeFlight = 0; #seconds, note: should == rangeMax / averageSpeed
int numActiveMax = 0;
bool domainAir = false;
bool domainLand = false;
double maxFiringAngle = 0;
end_script_variables
end_script_struct
script_variables
Map<string, struct> gWeaponDefs = Map<string, struct>();
gWeaponDefs["MEDIUM_RANGE_MISSILE"] = struct.New("WeaponData");
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->type = "MEDIUM_RANGE_MISSILE";
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->rangeMin = 50; # (meters)
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->rangeMax = 111120; # ~60 nm (meters)
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->averageSpeed = 1657.283; #mach 5 (m/s)
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->maxTimeFlight = 67.05; #for 60 nm range (seconds)
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->numActiveMax = 2;
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->domainAir = true;
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->domainLand = false;
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->maxFiringAngle = 45.0;
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"] = struct.New("WeaponData");
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->type = "MEDIUM_RANGE_RADAR_MISSILE";
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->rangeMin = 50; # (meters)
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->rangeMax = 111120; # ~60 nm (meters)
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->averageSpeed = 1657.283; #mach 5 (m/s)
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->maxTimeFlight = 67.05; #for 60 nm range (seconds)
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->numActiveMax = 2;
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->domainAir = true;
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->domainLand = false;
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->maxFiringAngle = 45.0;
end_script_variables
#returns a 'WeaponData' struct
script struct GetWeaponData(string aType)
if (gWeaponDefs.Exists(aType))
{
return gWeaponDefs.Get(aType);
}
else
{
return struct.New("WeaponData");
}
end_script
include_once processors/quantum_agents/common/common_platform_script.txt
script_debug_writes off
script bool WeaponCapableAvailableAgainstThreat(WsfWeapon weapon, WsfTrack track)
writeln_d(" checking weapon ", weapon.Name(), " valid=", weapon.IsValid());
if (weapon.IsNull() || !weapon.IsValid() || track.IsNull() || !track.IsValid())
{
writeln_d("weapon or track is not valid!");
return false;
}
if ((weapon.QuantityRemaining()-weapon.WeaponsPendingFor(WsfTrackId())) <= 0)
{
writeln_d("no unassigned weapons left to fire!");
return false;
}
#check manually input user data first
struct weaponData = GetWeaponData(weapon.Type());
if (weaponData->type == weapon.Type())
{
if ((track.AirDomain() && !weaponData->domainAir) ||
(track.LandDomain() && !weaponData->domainLand) )
{
writeln_d("weapon not capable against target domain!");
return false;
}
}
else
{
writeln_d("could not find weapon type ", weapon.Type() ," in weapon database; query returned type ", weaponData->type);
#check if it has a launch computer of the necessary type
WsfLaunchComputer lcPtr = weapon.LaunchComputer();
if (lcPtr.IsValid())
{
if (track.AirDomain() && lcPtr.IsA_TypeOf("WSF_AIR_TO_AIR_LAUNCH_COMPUTER"))
{
return true;
}
else if (track.LandDomain() && lcPtr.IsA_TypeOf("WSF_ATG_LAUNCH_COMPUTER"))
{
return true;
}
}
writeln_d("nor could an applicable launch computer be found!");
return false; #dont have weapon data
}
return true;
end_script
#could return -1 for an invalid max range
#(from a launch computer or weapon struct not found)
script double MaxRange(WsfPlatform shooter, WsfWeapon weapon, WsfTrack track)
WsfLaunchComputer lcPtr = weapon.LaunchComputer();
if (lcPtr.IsValid() &&
lcPtr.IsA_TypeOf("WSF_AIR_TO_AIR_LAUNCH_COMPUTER"))
{
double rmax = lcPtr.LookupResult(track)[0];
return rmax;
}
else
{
struct weaponData = GetWeaponData(weapon.Type());
if (weaponData->type == weapon.Type())
{
return weaponData->rangeMax;
}
else
{
return -1.0;
}
}
end_script
#if beyond max range: returns time to hit if launched at max range
#if before min range: returns time to hit if launched at min range
#could return -1 for an invalid max range
#(from a launch computer or weapon struct not found)
script double TimeToHit(WsfPlatform shooter, WsfWeapon weapon, WsfTrack track)
double range = shooter.SlantRangeTo(track);
WsfLaunchComputer lcPtr = weapon.LaunchComputer();
if (lcPtr.IsValid() &&
lcPtr.IsA_TypeOf("WSF_AIR_TO_AIR_LAUNCH_COMPUTER"))
{
Array<double> arr = lcPtr.LookupResult(track);
double rmax = arr[0];
double rmaxTOF = arr[1];
double rmin = arr[4];
double rminTOF = arr[5];
if (range >= rmax)
{
return rmaxTOF;
}
else if (range<=rmin)
{
return rminTOF;
}
else
{
# in between, interpolate
double scale = (range-rmin)/(rmax-rmin);
double tof = scale*(rmaxTOF-rminTOF) + rminTOF;
return tof;
}
}
else
{
struct weaponData = GetWeaponData(weapon.Type());
if (weaponData->type == weapon.Type())
{
if (range > weaponData->rangeMax)
{
range = weaponData->rangeMax;
}
else if (range < weaponData->rangeMin)
{
range = weaponData->rangeMin;
}
double tof = range / weaponData->averageSpeed;
return tof;
}
else
{
return -1.0;
}
}
end_script
# Calculate time for the weapon to fly a given range
#if beyond max range: returns time to hit if launched at max range
#if before min range: returns time to hit if launched at min range
#could return -1 for an invalid max range
#(from a launch computer or weapon struct not found)
script double TimeToRange(double range, WsfWeapon weapon, WsfTrack track)
WsfLaunchComputer lcPtr = weapon.LaunchComputer();
if (lcPtr.IsValid() &&
lcPtr.IsA_TypeOf("WSF_AIR_TO_AIR_LAUNCH_COMPUTER"))
{
Array<double> arr = lcPtr.LookupResult(track);
double rmax = arr[0];
double rmaxTOF = arr[1];
double rmin = arr[4];
double rminTOF = arr[5];
if (range >= rmax)
{
return rmaxTOF;
}
else if (range<=rmin)
{
return rminTOF;
}
else
{
# in between, interpolate
double scale = (range-rmin)/(rmax-rmin);
double tof = scale*(rmaxTOF-rminTOF) + rminTOF;
return tof;
}
}
else
{
struct weaponData = GetWeaponData(weapon.Type());
if (weaponData->type == weapon.Type())
{
if (range > weaponData->rangeMax)
{
range = weaponData->rangeMax;
}
else if (range < weaponData->rangeMin)
{
range = weaponData->rangeMin;
}
double tof = range / weaponData->averageSpeed;
return tof;
}
else
{
return -1.0;
}
}
end_script
script bool InRangeToFire(WsfPlatform shooter, WsfWeapon weapon, WsfTrack track, double percentRangeMax, double percentRangeMin)
##do not check this again
#if ( ! WeaponCapableAvailableAgainstThreat(weapon, track) )
#{
# return false;
#}
WsfLaunchComputer lcPtr = weapon.LaunchComputer();
if (lcPtr.IsValid() &&
lcPtr.IsA_TypeOf("WSF_AIR_TO_AIR_LAUNCH_COMPUTER"))
{
writeln_d(" using air-to-air launch computer");
# The returned array contains: Rmax, RmaxTOF, Rne, RneTOF, Rmin, RminTOF
# in that order. -1.0 means "not valid".
Array<double> returnedValues = lcPtr.LookupResult(track);
# Now have to consider whether we have enough information to continue with a weapon shot:
double theRmax = returnedValues[0]; #"Rmax";
double theRmaxTOF = returnedValues[1]; #"RmaxTOF";
double theRne = returnedValues[2]; #"Rne";
double theRneTOF = returnedValues[3]; #"RneTOF";
double theRmin = returnedValues[4]; #"Rmin";
double theRminTOF = returnedValues[5]; #"RminTOF";
double range = shooter.GroundRangeTo(track.CurrentLocation());
# Check for track range less than Rmin * scaleFactor, if not, return.
# But do not check for min range constraint at all unless we are likely to be needing it.
if (range < 5000)
{
if (theRmin == -1.0)
{
writeln_d(" Engagement did not shoot since Rmin was not valid.");
return false;
}
double RminConstraint = theRmin * percentRangeMin;
if (range < RminConstraint)
{
writeln_d(" Engagement did not shoot since inside the k * Rmin constraint distance.");
writeln_d(" Range versus Rmin constraint = ", range, ", ", RminConstraint);
return false;
}
}
# Check for track range less than Rne, if so, FORCE a weapon fire.
bool forceWeaponFire = false;
if (range < theRne)
{
writeln_d(" Engagement is forcing a weapon fire due to inside Rne.");
writeln_d(" Range versus Rne constraint = ", range, ", ", theRne);
return true;
}
if (forceWeaponFire == false)
{
######################################TRY THIS######################################
WsfPlatform plat = WsfSimulation.FindPlatform( track.TargetName() );
if (plat.IsValid() && plat.CategoryMemberOf("fighter"))
{
#theRmax = theRne;
theRmax = (theRmax + theRne)/2.0; #for highly maneuverable fighter targets
}
####################################END TRY THIS####################################
# Check for track range less than k * Rmax, if not, return.
if (theRmax == -1.0)
{
writeln_d(" Engagement did not shoot since Rmax was not valid.");
return false;
}
#double RmaxConstraint = theRmax * DefaultPercentRangeMax;
if (range > (theRmax * percentRangeMax))
{
writeln_d(" Engagement did not shoot since outside the k * Rmax constraint distance.");
writeln_d(" Range versus Rmax constraint = ", range, ", ", (theRmax * percentRangeMax));
return false;
}
}
writeln_d(" Engagement meets constraints for firing a weapon (continue).");
}
else if (lcPtr.IsValid() &&
lcPtr.IsA_TypeOf("WSF_ATG_LAUNCH_COMPUTER"))
{
writeln_d(" using air-to-ground launch computer");
if (lcPtr.CanIntercept(track))
{
#intercept works, this weapon is a candidate
}
else
{
return false;
}
}
else
{
struct weaponData = GetWeaponData(weapon.Type());
writeln_d(" using input WeaponData struct values");
#check our own ranges & angles --> hacky!!!
#extern double EffectiveRange (WsfPlatform, WsfTrack);
double effectiveRange = EffectiveRange(shooter, track);
double absRelativeBearing = MATH.Fabs(shooter.RelativeBearingTo( track ));
if ((weaponData->rangeMin * percentRangeMin) > effectiveRange)
{
writeln_d(" target too close");
return false;
}
if (absRelativeBearing > weaponData->maxFiringAngle)
{
writeln_d(" target firing angle too large");
return false;
}
if (weaponData->rangeMax * percentRangeMax < effectiveRange)
{
writeln_d(" target too far away");
return false;
}
double range = shooter.SlantRangeTo(track);
#double closingSpeed = PLATFORM.ClosingSpeedOf(track);
double relBearing = track.RelativeBearingTo(shooter);
if (relBearing > 90.0)
{
if (track.Speed() > weaponData->averageSpeed)
{
return false;
}
double speedDiff = weaponData->averageSpeed - track.Speed();
if ((range/speedDiff) > weaponData->maxTimeFlight)
{
return false;
}
}
}
return true;
end_script