628 lines
25 KiB
Plaintext
628 lines
25 KiB
Plaintext
# ****************************************************************************
|
|
# 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 pursue-target
|
|
|
|
script_debug_writes off
|
|
|
|
script_variables
|
|
|
|
//**********************************************************************//
|
|
//** debugging parameters **//
|
|
//**********************************************************************//
|
|
bool mDrawSteering = false;
|
|
|
|
//**********************************************************************//
|
|
//** bidding parameters (pursue-target jobs) **//
|
|
//** slant range and closing speed are the most important **//
|
|
//** bids are now in units of distance (meters) **//
|
|
//** all other bid contributions are converted with their weight **//
|
|
//** range will dominate the bids **//
|
|
//**********************************************************************//
|
|
double cMIN_JOB_BID = -MATH.DOUBLE_MAX();
|
|
|
|
double cMAX_SLANT_RANGE = 1000000.0; #over 539 nm away, target ranges beyond this are considered unfavorable
|
|
double cWEIGHT_SLANT_RANGE_TO = 1.0;
|
|
|
|
double cMIN_CLOSING_SPEED = -1050.0; #threats running away (negative closing) faster are considered unfavorable
|
|
double cWEIGHT_CLOSING_SPEED = 1.0; #scale for how closing speed translates to distance
|
|
|
|
double cWEIGHT_FUEL = 0.0; #try a value of 2.0 if you care about fuel
|
|
double cWEIGHT_MY_WEAPONS_ACTIVE = 0.0; #changes bid if your own weapons are active on target
|
|
double cWEIGHT_PEERS_WEAPONS_ACTIVE = 0.0; #changes bid if your peers weapons are active on target
|
|
double cWEIGHT_THREAT_WEAPONS_ENVELOPE = 0.0; #uses the global "mWeaponsEnvelope" array, try value of 10000 if you care about that
|
|
//careful with these two parameters, they are stateful
|
|
double cWEIGHT_CURRENT_TARGET = 0.0; #changes bid if you are currently targeting the threat
|
|
double cWEIGHT_OTHERS_TARGETING = 0.0; #changes bid if peers are currently targeting the threat
|
|
|
|
|
|
//**********************************************************************//
|
|
//** control / mode of operation parameters **//
|
|
//**********************************************************************//
|
|
bool mFilterJobsOnWeapons = true; # ignore pursue-target jobs that we cant shoot a weapon at
|
|
bool mFilterJobsOnCategory = false; # ignore pursue-target jobs that are for platform of unknown category
|
|
bool mFilterJobsOnFuel = false; # ignore pursue-target jobs that we dont have the fuel to reach
|
|
|
|
bool mFilterJobsOnZone = false; # ignore pursue-target jobs that are for platforms outsize of zone "mZoneName"
|
|
string mZoneName = "";
|
|
WsfZone mFezZone;
|
|
|
|
bool mCheckOwnJobBoardToo = false; # can this agent bid on & win a job off his own board
|
|
|
|
|
|
bool mUseMoverDuringPursuit = true;
|
|
|
|
//**********************************************************************//
|
|
//** flying parameters, for intercept or approach **//
|
|
//**********************************************************************//
|
|
//target point to fly at
|
|
WsfGeoPoint mTargetPoint = WsfGeoPoint();
|
|
double mTargetSpeed = 0; # will be overwritten
|
|
// larger values, suggested for air to air fighters & jets
|
|
double mMatchSpeedDistanceMin = 1 * 1852; # 1 mile
|
|
double mMatchSpeedDistanceMax = 20 * 1852; # 20 miles
|
|
#double mWaitSpeed = 500 * MATH.MPS_PER_NMPH();
|
|
#double mInterceptSpeed = 800 * MATH.MPS_PER_NMPH();
|
|
#double mInterceptSpeed = 293.941; //mach 0.95 at 25200 ft altitude
|
|
double mWaitSpeed = 293.941; //mach 0.95 at 25200 ft altitude
|
|
double mInterceptSpeed = 600 * MATH.MPS_PER_NMPH();
|
|
|
|
double mDefaultAccel = 7.5 * Earth.ACCEL_OF_GRAVITY(); # m/s^2 ~7.5 Gs
|
|
// smaller values, suggest for a UAV intercepting or following ground forces
|
|
#double mMatchSpeedDistanceMin = 185.2; # one tenth of a mile
|
|
#double mMatchSpeedDistanceMax = 1852.0; # a mile
|
|
#double mWaitSpeed = 22; # m/s (~50 mph)
|
|
#double mInterceptSpeed = 52; # m/s (~100 knots)
|
|
double mMinAltitude = 4572; # ~15000 feet
|
|
|
|
//switch for matching threat's altitude during pursuit
|
|
bool DefaultMatchThreatAltitude = false;
|
|
Map<string, bool> mThreatTypeMatchAltitude = Map<string, bool>();
|
|
//mThreatTypeMatchAltitude["missile_fast"] = true;
|
|
//mThreatTypeMatchAltitude["awacs"] = true;
|
|
//mThreatTypeMatchAltitude["bomber"] = true;
|
|
//mThreatTypeMatchAltitude["fighter"] = true;
|
|
mThreatTypeMatchAltitude["unknown"] = false;
|
|
mThreatTypeMatchAltitude["uav"] = false;
|
|
mThreatTypeMatchAltitude["sam"] = false;
|
|
mThreatTypeMatchAltitude["ship"] = false;
|
|
mThreatTypeMatchAltitude["jammer"] = false;
|
|
mThreatTypeMatchAltitude["missile"] = false;
|
|
|
|
//specify offset angle to fly at, during f-pole pursuit
|
|
double DefaultOffsetDistance = 1852*50; //50 nm
|
|
double DefaultOffsetAngle = 30.0; // should this be radar-specific?
|
|
Map<string, double> ThreatTypeOffsetAngle = Map<string, double>();
|
|
//ThreatTypeOffsetAngle["awacs"] = 15.0;
|
|
//ThreatTypeOffsetAngle["unknown"] = 20.0;
|
|
//ThreatTypeOffsetAngle["sam"] = 50.0;
|
|
|
|
//**********************************************************************//
|
|
//********* VARIABLES BELOW THIS LINE ARE NOT FOR USER EDITING *********//
|
|
//**********************************************************************//
|
|
WsfRIPRJob mCurrentJob;
|
|
WsfDraw mDraw = WsfDraw();
|
|
string mOldTargetStr = "no target";
|
|
double mLastTime = 0.0;
|
|
|
|
end_script_variables
|
|
|
|
|
|
script bool HaveWeaponsForThreat(WsfTrack track)
|
|
extern bool IsWeaponDomainCapable(WsfTrack, Map<string, Object>);
|
|
extern Array<Map<string, Object>> mWeaponArray;
|
|
foreach (Map<string, Object> curWeapon in mWeaponArray)
|
|
{
|
|
WsfWeapon weapon = (WsfWeapon)curWeapon["weapon"];
|
|
if (weapon.QuantityRemaining() > 0 &&
|
|
IsWeaponDomainCapable(track,curWeapon))
|
|
{
|
|
if (curWeapon.Exists("onlyUseInRange") &&
|
|
(int)curWeapon["onlyUseInRange"] == 1 &&
|
|
PLATFORM.SlantRangeTo(track) > (double)curWeapon["rangeMax"] )
|
|
{
|
|
continue;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
end_script
|
|
|
|
#
|
|
# query_bid_type pursue-target
|
|
#
|
|
# extern Map<string, double> ThreatTypePriority;
|
|
#
|
|
# extern WsfTrack GetTrackByName (WsfPlatform, string);
|
|
# extern bool HasEnoughFuelToTravel (WsfPlatform,double);
|
|
# extern string DetermineTrackCategory (WsfTrack);
|
|
# extern bool TestTrackCategory (WsfTrack, string);
|
|
# extern double GetWeaponsEnvelope (WsfPlatform);
|
|
# extern double GetWeaponRangeMax(WsfPlatform aPlatform, Array<Map<string, Object>> aWeaponArray);
|
|
# extern Array<Map<string, Object>> mWeaponArray;
|
|
#
|
|
# if (!JOB.IsValid())
|
|
# {
|
|
# writeln_d("query_bid_type pursue-target: JOB not valid");
|
|
# return cMIN_JOB_BID;
|
|
# }
|
|
#
|
|
# string targetTrackName = (string)JOB.GetData("targetTrackName");
|
|
# WsfTrack targetTrack = GetTrackByName(PLATFORM, targetTrackName);
|
|
# if (!targetTrack.IsValid())
|
|
# {
|
|
# writeln_d("!!! No track for JOB: ", JOB.Name(), ", ", JOB.GetDescription(), ", ", targetTrackName);
|
|
# return cMIN_JOB_BID;
|
|
# }
|
|
#
|
|
# double slantRangeTo = PLATFORM.SlantRangeTo(targetTrack);
|
|
# double closingSpeed = PLATFORM.ClosingSpeedOf(targetTrack);
|
|
# double maxWeaponRange = GetWeaponRangeMax(PLATFORM, mWeaponArray);
|
|
#
|
|
# if (mFilterJobsOnFuel == true)
|
|
# {
|
|
# if (!HasEnoughFuelToTravel(PLATFORM,slantRangeTo-maxWeaponRange))
|
|
# {
|
|
# writeln_d("!!! Not enough fuel for JOB: ", JOB.Name(), ", ", JOB.GetDescription(), ", ", targetTrackName);
|
|
# return cMIN_JOB_BID;
|
|
# }
|
|
# }
|
|
#
|
|
# if (mFilterJobsOnCategory == true)
|
|
# {
|
|
# if (TestTrackCategory(targetTrack, "unknown"))
|
|
# {
|
|
# writeln_d("!!! Target type unknown for current job. ");
|
|
# return cMIN_JOB_BID;
|
|
# }
|
|
# }
|
|
#
|
|
# if (mFilterJobsOnWeapons == true)
|
|
# {
|
|
# //check here if we have any weapons remaining that are capable against target domain
|
|
# if (!HaveWeaponsForThreat(targetTrack) &&
|
|
# PROCESSOR.WeaponsActive(targetTrack) <= 0)
|
|
# {
|
|
# writeln_d("!!! No domain capable weapons left for target!");
|
|
# return cMIN_JOB_BID;
|
|
# }
|
|
# }
|
|
#
|
|
# if (mFilterJobsOnZone == true)
|
|
# {
|
|
# if (mZoneName != "")
|
|
# {
|
|
# mFezZone = PLATFORM.Zone(mZoneName);
|
|
# if (!mFezZone.IsValid() || !mFezZone.PointIsInside(PLATFORM.Location()))
|
|
# {
|
|
# writeln_d("!!! Target outside of given FEZ!");
|
|
# return cMIN_JOB_BID;
|
|
# }
|
|
# }
|
|
# }
|
|
#
|
|
# double weight_closing_speed = cWEIGHT_CLOSING_SPEED;
|
|
# if (targetTrack.LandDomain())
|
|
# {
|
|
# weight_closing_speed = 2.0 * cWEIGHT_CLOSING_SPEED;
|
|
# }
|
|
#
|
|
# /////////////////////////////////////////////////////////////////////////
|
|
# // calculate bulk of the bid HERE
|
|
# double bid = 0.0;
|
|
# bid += cWEIGHT_SLANT_RANGE_TO * (cMAX_SLANT_RANGE - slantRangeTo);
|
|
# bid += weight_closing_speed * (-cMIN_CLOSING_SPEED + closingSpeed);
|
|
# // the bid has its major contributers now
|
|
# /////////////////////////////////////////////////////////////////////////
|
|
#
|
|
# //calculate other optional bid contributions here
|
|
# WsfFuel fuelObj = PLATFORM.Fuel();
|
|
# if (fuelObj.IsValid())
|
|
# {
|
|
# bid = bid + cWEIGHT_FUEL * fuelObj.QuantityRemaining();
|
|
# }
|
|
# //contribution if I have an active weapon on the target
|
|
# bid = bid + cWEIGHT_MY_WEAPONS_ACTIVE * PROCESSOR.WeaponsActive(targetTrack);
|
|
# //contribution if peers have an active weapon on the target
|
|
# bid = bid + cWEIGHT_PEERS_WEAPONS_ACTIVE * PROCESSOR.PeersWeaponsActive(targetTrack);
|
|
# //contribution if I am currently targeting the target
|
|
# if (PROCESSOR.GetTargetName() == targetTrackName)
|
|
# {
|
|
# bid = bid + cWEIGHT_CURRENT_TARGET;
|
|
# }
|
|
# //contribution if any peers are targeting the target
|
|
# WsfRIPRProcessor commander = PROCESSOR.GetRIPRCommanderProcessor();
|
|
# if (commander.IsValid())
|
|
# {
|
|
# bid = bid + cWEIGHT_OTHERS_TARGETING * commander.SubsTargeting(targetTrack, PLATFORM);
|
|
# }
|
|
# //contribution bonus if you care about weapon envelopes
|
|
# if (slantRangeTo < GetWeaponsEnvelope(targetTrack.Target()))
|
|
# {
|
|
# bid = bid + cWEIGHT_THREAT_WEAPONS_ENVELOPE;
|
|
# }
|
|
# //contribution bonus from threat type (category)
|
|
# string targetType = DetermineTrackCategory(targetTrack);
|
|
# if( ThreatTypePriority.Exists( targetType ) )
|
|
# {
|
|
# bid = bid + ThreatTypePriority.Get( targetType );
|
|
# }
|
|
#
|
|
# writeln_d(PLATFORM.Name(), " bid on target ", targetTrackName, ": ", bid);
|
|
# return bid;
|
|
# end_query_bid_type
|
|
|
|
|
|
script double GetOffsetAngleOnThreat(WsfTrack threat)
|
|
WsfPlatform plat = WsfSimulation.FindPlatform( threat.TargetName() );
|
|
if (plat.IsValid())
|
|
{
|
|
foreach( string aCategory : double angle in ThreatTypeOffsetAngle )
|
|
{
|
|
if( plat.CategoryMemberOf( aCategory ) )
|
|
{
|
|
writeln_d("offset angle for type ", aCategory, " = ", angle);
|
|
return angle;
|
|
}
|
|
}
|
|
}
|
|
return DefaultOffsetAngle;
|
|
end_script
|
|
|
|
|
|
script bool MatchAltitudeForThreat(WsfTrack track)
|
|
WsfPlatform plat = WsfSimulation.FindPlatform( track.TargetName() );
|
|
if (plat.IsValid())
|
|
{
|
|
foreach (string aCategory : bool match in mThreatTypeMatchAltitude)
|
|
{
|
|
if (plat.CategoryMemberOf(aCategory))
|
|
{
|
|
return match;
|
|
}
|
|
}
|
|
}
|
|
return DefaultMatchThreatAltitude;
|
|
end_script
|
|
|
|
|
|
precondition
|
|
if (!PROCESSOR.IsA_TypeOf("WSF_RIPR_PROCESSOR"))
|
|
{
|
|
return Failure("behavior not attached to a RIPR processor!");
|
|
}
|
|
writeln_d(PLATFORM.Name(), " precondition pursue-target, T=", TIME_NOW);
|
|
((WsfRIPRProcessor)PROCESSOR).ClearTarget();
|
|
|
|
double duration = TIME_NOW - mLastTime;
|
|
mLastTime = TIME_NOW;
|
|
if (duration > (1.5*((WsfRIPRProcessor)PROCESSOR).UpdateInterval()))
|
|
{
|
|
mOldTargetStr = "no target";
|
|
}
|
|
string anOldTargetStr = mOldTargetStr;
|
|
mOldTargetStr = "no target";
|
|
|
|
WsfRIPRProcessor commander = ((WsfRIPRProcessor)PROCESSOR).GetRIPRCommanderProcessor();
|
|
if (commander.IsValid())
|
|
{
|
|
if (commander.IsJobWindowOpen())
|
|
{
|
|
writeln_d("pursue-target: commander.GetJobFor()");
|
|
mCurrentJob = ((WsfRIPRProcessor)PROCESSOR).GetRIPRCommanderProcessor().GetJobFor(TIME_NOW, ((WsfRIPRProcessor)PROCESSOR));
|
|
}
|
|
else
|
|
{
|
|
writeln_d("pursue-target: commander's job window closed, keeping current job!");
|
|
}
|
|
}
|
|
if (mCheckOwnJobBoardToo &&
|
|
(mCurrentJob.IsNull() || !mCurrentJob.IsValid()))
|
|
{
|
|
writeln_d("pursue-target: myself.GetJobFor()");
|
|
mCurrentJob = ((WsfRIPRProcessor)PROCESSOR).GetJobFor(TIME_NOW, ((WsfRIPRProcessor)PROCESSOR));
|
|
}
|
|
|
|
if (mCurrentJob.IsNull() || !mCurrentJob.IsValid() )
|
|
{
|
|
writeln_d("pursue-target ClearTarget -> job not a valid pursue-target job");
|
|
return Failure("job is not a valid pursue-target job");
|
|
}
|
|
if (mCurrentJob.Name() != "pursue-target")
|
|
{
|
|
string msg = write_str("job is a ", mCurrentJob.Name(), " instead of a pursue-target job");
|
|
writeln_d(msg);
|
|
return Failure("job is not a pursue-target job");
|
|
}
|
|
|
|
extern WsfTrack GetTrackByName (WsfPlatform, string);
|
|
string targetName = (string)mCurrentJob.GetData("targetTrackName");
|
|
WsfTrack targetTrack = GetTrackByName(PLATFORM, targetName);
|
|
|
|
if (!targetTrack.IsValid())
|
|
{
|
|
writeln_d(" No valid target track found for target named ", targetName);
|
|
return Failure("target track not found");
|
|
}
|
|
|
|
########################################################################
|
|
### print output / comments for any target change
|
|
#######################################################################
|
|
mOldTargetStr = "Job: " + mCurrentJob.Name() + ", " + mCurrentJob.GetDescription();
|
|
writeln_d(" - ", mOldTargetStr);
|
|
if (mOldTargetStr != anOldTargetStr)
|
|
{
|
|
PLATFORM.Comment(mOldTargetStr);
|
|
}
|
|
((WsfRIPRProcessor)PROCESSOR).SetTarget(targetTrack);
|
|
return true;
|
|
end_precondition
|
|
|
|
|
|
execute
|
|
string comment = write_str(PLATFORM.Name(), " executing pursue-target, T=", TIME_NOW);
|
|
writeln_d(comment);
|
|
#PLATFORM.Comment(comment);
|
|
|
|
extern string CalculatePositioning (WsfPlatform, WsfTrack, double);
|
|
extern double GetWeaponRangeMax(WsfPlatform aPlatform, Array<Map<string, Object>> aWeaponArray);
|
|
extern Array<Map<string, Object>> mWeaponArray;
|
|
|
|
//get target from ripr processor, to be sure
|
|
WsfTrack targetTrack = ((WsfRIPRProcessor)PROCESSOR).GetTarget();
|
|
|
|
if (targetTrack.IsNull() ||
|
|
!targetTrack.IsValid())
|
|
{
|
|
writeln_d(" UpdateInterceptLocation, targetTrack is null or not valid");
|
|
return;
|
|
}
|
|
|
|
if (mUseMoverDuringPursuit)
|
|
{
|
|
double ownSpeed = PLATFORM.Speed();
|
|
double targetSpeed = targetTrack.Speed();
|
|
double slantRangeTo = PLATFORM.SlantRangeTo(targetTrack);
|
|
double closingSpeed = PLATFORM.ClosingSpeedOf(targetTrack);
|
|
string positioning = CalculatePositioning(PLATFORM, targetTrack, 10.0);
|
|
int weaponsActive = ((WsfRIPRProcessor)PROCESSOR).WeaponsActive(targetTrack);
|
|
|
|
double engageRangeMax = 185200.0; //100 miles
|
|
double engageRangeMin = 1852.0; // 1 mile
|
|
|
|
# // determine the fuel needed to engage
|
|
# double fuelRequired = 0;
|
|
# double bingoQuantity = -1;
|
|
# if (mConsiderFuel)
|
|
# {
|
|
# WsfFuel fuelObj = PLATFORM.Fuel();
|
|
# if (fuelObj)
|
|
# {
|
|
# double maxWeaponRange = GetWeaponRangeMax(PLATFORM, mWeaponArray);
|
|
# double distToWeaponRange = slantRangeTo - maxWeaponRange;
|
|
# double timeToTarget = distToWeaponRange / closingSpeed;
|
|
# double distToTravel = timeToTarget * ownSpeed;
|
|
#
|
|
# fuelRequired = fuelObj.QuantityRequired(distToTravel);
|
|
# bingoQuantity = fuelObj.BingoQuantity();
|
|
# }
|
|
# }
|
|
|
|
string PursuitMode = "pure";
|
|
|
|
if (weaponsActive > 0)
|
|
{
|
|
PursuitMode = "f-pole";
|
|
}
|
|
else if (targetTrack.AirDomain())
|
|
{
|
|
if (slantRangeTo >= engageRangeMax &&
|
|
positioning != "head-to-head" &&
|
|
positioning != "head-to-tail" &&
|
|
targetSpeed >= ownSpeed)
|
|
{
|
|
PursuitMode = "lead";
|
|
}
|
|
else if (slantRangeTo <= engageRangeMax &&
|
|
positioning != "head-to-head" &&
|
|
positioning != "head-to-tail")
|
|
{
|
|
PursuitMode = "lag";
|
|
}
|
|
//else if (slantRangeTo > engageRangeMax ||
|
|
// (slantRangeTo <= engageRangeMax &&
|
|
// (positioning == "head-to-head" ||
|
|
// positioning == "head-to-tail")))
|
|
//{
|
|
// PursuitMode = "pure";
|
|
//}
|
|
}
|
|
|
|
writeln_d(" PursuitMode = ", PursuitMode);
|
|
// Our track quality (or target range) may not be good enough yet, so keep moving towards the target.
|
|
|
|
// If we got the altitude from the TRACK, match it
|
|
double interceptHeading = PLATFORM.Heading();
|
|
double distanceToTarget = PLATFORM.SlantRangeTo(targetTrack);
|
|
|
|
extern double cDEFAULT_ALTITUDE;
|
|
double interceptAltitude = cDEFAULT_ALTITUDE;
|
|
|
|
//check for targets altitude, and whether or not we should match it
|
|
if (targetTrack.ElevationValid() ||
|
|
targetTrack.LocationValid())
|
|
{
|
|
if (targetTrack.Altitude() > interceptAltitude) //always climb up to target
|
|
{
|
|
interceptAltitude = targetTrack.Altitude();
|
|
}
|
|
else if (MatchAltitudeForThreat(targetTrack) == true)
|
|
{
|
|
interceptAltitude = targetTrack.Altitude();
|
|
}
|
|
}
|
|
|
|
//always bound the altitude by the min & max restrictions (in case mover is not setup to do it)
|
|
if (interceptAltitude < mMinAltitude)
|
|
{
|
|
interceptAltitude = mMinAltitude;
|
|
}
|
|
writeln_d("desired intercept altitude: ", interceptAltitude);
|
|
|
|
mTargetSpeed = mInterceptSpeed;
|
|
if (targetTrack.VelocityValid())
|
|
{
|
|
if (targetTrack.AirDomain())
|
|
{
|
|
extern double EffectiveRange(WsfPlatform, WsfTrack);
|
|
|
|
double speedOfTarget = targetTrack.Speed();
|
|
double effRange = EffectiveRange(PLATFORM, targetTrack);
|
|
double distanceWindow = mMatchSpeedDistanceMax - mMatchSpeedDistanceMin;
|
|
double speedWindow = mInterceptSpeed - speedOfTarget;
|
|
|
|
if(effRange < mMatchSpeedDistanceMax && effRange > mMatchSpeedDistanceMin)
|
|
{
|
|
double rangeScale = (effRange - mMatchSpeedDistanceMin) / distanceWindow;
|
|
mTargetSpeed = speedOfTarget + (speedWindow * rangeScale);
|
|
writeln_d(PLATFORM.Name(), " pursue-target, speed scaled down in matching window!");
|
|
}
|
|
else if (effRange <= mMatchSpeedDistanceMin)
|
|
{
|
|
mTargetSpeed = speedOfTarget * 0.99;
|
|
writeln_d(PLATFORM.Name(), " pursue-target, speed set to match target!");
|
|
}
|
|
|
|
if (mTargetSpeed < mWaitSpeed)
|
|
{
|
|
mTargetSpeed = mWaitSpeed;
|
|
writeln_d(PLATFORM.Name(), " pursue-target, speed was lower than wait speed, adjust!");
|
|
}
|
|
}
|
|
else if (targetTrack.LandDomain())
|
|
{
|
|
writeln_d(PLATFORM.Name(), " pursue-target, target is land domain, adjust speed!");
|
|
double speedOfTarget = targetTrack.Speed();
|
|
double range = PLATFORM.GroundRangeTo(targetTrack);
|
|
double distanceWindow = mMatchSpeedDistanceMax - mMatchSpeedDistanceMin;
|
|
double speedWindow = mInterceptSpeed - speedOfTarget;
|
|
|
|
if(range < mMatchSpeedDistanceMax && range > mMatchSpeedDistanceMin)
|
|
{
|
|
double rangeScale = (range - mMatchSpeedDistanceMin) / distanceWindow;
|
|
mTargetSpeed = speedOfTarget + (speedWindow * rangeScale);
|
|
}
|
|
else if (range <= mMatchSpeedDistanceMin)
|
|
{
|
|
mTargetSpeed = speedOfTarget * 0.99;
|
|
}
|
|
|
|
if (mTargetSpeed < mWaitSpeed)
|
|
{
|
|
mTargetSpeed = mWaitSpeed;
|
|
}
|
|
}
|
|
}
|
|
|
|
double leadOrLagTime = 15.0; //seconds
|
|
if (PursuitMode == "lead")
|
|
{
|
|
WsfWaypoint wpt = WsfWaypoint();
|
|
double tti = PLATFORM.InterceptLocation3D(targetTrack, wpt);
|
|
if (tti > 0.0)
|
|
{
|
|
mTargetPoint = wpt.Location();
|
|
}
|
|
else
|
|
{
|
|
mTargetPoint = targetTrack.LocationAtTime(TIME_NOW + leadOrLagTime);
|
|
}
|
|
}
|
|
else if(PursuitMode == "lag")
|
|
{
|
|
double usedLagDelay = (slantRangeTo/engageRangeMax) * leadOrLagTime;
|
|
|
|
double maxLagDist = 0.35 * PLATFORM.SlantRangeTo(targetTrack);
|
|
double maxLagTime = maxLagDist / targetTrack.Speed();
|
|
if (usedLagDelay > maxLagTime)
|
|
{
|
|
usedLagDelay = maxLagTime;
|
|
}
|
|
mTargetPoint = targetTrack.LocationAtTime(TIME_NOW - usedLagDelay);
|
|
}
|
|
else if (PursuitMode == "f-pole")
|
|
{
|
|
extern double MaximizeFPole(WsfPlatform, WsfTrack, double);
|
|
interceptHeading = MaximizeFPole(PLATFORM, targetTrack, GetOffsetAngleOnThreat(targetTrack));
|
|
mTargetPoint = PLATFORM.Location();
|
|
mTargetPoint.Extrapolate(interceptHeading, DefaultOffsetDistance);
|
|
}
|
|
else
|
|
{
|
|
//PursuitMode == pure
|
|
mTargetPoint = targetTrack.LocationAtTime(TIME_NOW);
|
|
}
|
|
|
|
if (!mTargetPoint.IsValid())
|
|
{
|
|
mTargetPoint = targetTrack.CurrentLocation();
|
|
}
|
|
|
|
mTargetPoint.Set(mTargetPoint.Latitude(), mTargetPoint.Longitude(), interceptAltitude);
|
|
|
|
if (mDrawSteering == true)
|
|
{
|
|
mDraw.SetLayer("behavior_pursue_target");
|
|
mDraw.SetDuration(((WsfRIPRProcessor)PROCESSOR).UpdateInterval());
|
|
mDraw.SetColor(1.0, 0.5, 0.0);
|
|
mDraw.SetLineSize(1);
|
|
mDraw.BeginLines();
|
|
mDraw.Vertex(PLATFORM.Location());
|
|
mDraw.Vertex(mTargetPoint);
|
|
mDraw.End();
|
|
}
|
|
|
|
string msg = write_str("pursue-target: ", targetTrack.TargetName(), " at speed ", (string)mTargetSpeed);
|
|
//PLATFORM.Comment(msg);
|
|
writeln_d(" T=", TIME_NOW, " ", PLATFORM.Name(), " ", msg);
|
|
extern bool FlyTarget (WsfPlatform, WsfGeoPoint, double);
|
|
FlyTarget( PLATFORM, mTargetPoint, mTargetSpeed);
|
|
}
|
|
else
|
|
{
|
|
//go at default speed; this gets overwritten if route waypoint has defined a speed
|
|
PLATFORM.GoToSpeed(mInterceptSpeed, mDefaultAccel, true);
|
|
//return to route, at the last target route point as re-entry
|
|
extern bool ReEnterRoute(WsfPlatform);
|
|
ReEnterRoute(PLATFORM);
|
|
|
|
if (mDrawSteering == true)
|
|
{
|
|
WsfRoute currRoute = PLATFORM.Route();
|
|
if (currRoute.IsValid())
|
|
{
|
|
mDraw.SetLayer("behavior_pursue-target");
|
|
mDraw.Erase(PLATFORM.Name());
|
|
mDraw.SetId(PLATFORM.Name());
|
|
mDraw.SetColor(0,1,1);
|
|
mDraw.SetLineSize(1);
|
|
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_execute
|
|
|
|
end_behavior
|