# **************************************************************************** # 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/weapon_defs.txt processor FL_QUANTUM_TASKER WSF_QUANTUM_TASKER_PROCESSOR script_variables bool mSelfCreateTasks = false; //if false, simply interpret received tasks ######################################################################### ## for evaluating weapon tasks ######################################################################### //bool mFilterOnWeapons = true; //double mPercentRangeMaxFire = 0.70; double cWEIGHT_SLANT_RANGE_TO = 1.0; double cWEIGHT_CLOSING_SPEED = 1.0; #double cMAX_SLANT_RANGE = 20037500.0; #halfway around the earth (half circumference) double cMAX_SLANT_RANGE = 10018750.0; #quarter way around the earth (quarter circumference) double cMIN_CLOSING_SPEED = -1050.0; ######################################################################### ## for creating pincer tasks ######################################################################### bool mCreatePincerTasks = false; //for debugging bool mDrawClusters = false; //parameters used to determine if a pincer is appropriate bool mRequireActiveWeapons = false; //if true, weapons must be in flight before pincer performed double mMaxSeparationAngle = 120.0; //degrees (pincer done if we've flanked by this much) int mNumSubsInvolved = 2; //not necessary to be an even number double mThresholdDistance = 160 * 1852; //160nm //clustering is performed to make sure the group of threats we are pincering against are grouped tightly together //if more than one group exist, the 2nd, 3rd, etc... groups have to be far enough away so that they aren't a //concern for the pincer maneuver (i.e. we wont head into them by separating around the target group) string mClusterMethod = "HTREEMAX"; //also valid: K_MEANS, H_TREE_MIN double mClusterDistanceLimit = 20*1852; //20nm - how close the group members have to be together double mMinDistanceRatio = 1.15; //other groups have to be 15% farther away than target group // parameters useful for those performing the pincer bool mCross = false; string mCapZoneName = ""; //specify the name of a zone in which we ignore the threat's orientation, (when they are capping in the zone) // script variables, used by methods below, do not change, not for user edit Array mTrackArray = Array(); Array mTargetNames = Array(); WsfDraw mDraw = WsfDraw(); double mMeanBearing = 0; bool mMeanBearingValid = false; WsfClusterManager mClusterManager; //WsfQuantumTask mPincerTask; WsfGeoPoint mMeanPoint; WsfGeoPoint mSubPoint; end_script_variables on_initialize mClusterManager = WsfClusterManager.Create(); // creates a cluster manager owned by this script mClusterManager.SetClusterMethod(mClusterMethod); // default is: "K_MEANS" mClusterManager.SetDistanceFunction("POSITION_VELOCITY"); // default is: "POSITION_ONLY" mClusterManager.SetDistanceLimit(mClusterDistanceLimit) ; mDraw.SetDuration(PROCESSOR.UpdateInterval()); end_on_initialize ## utility method used to draw lines around tracks in a cluster ## lines are drawn according to the convex hull of the cluster members script void draw_cluster_hull(WsfCluster cluster) Array pts = cluster.ConvexHull(); if (pts.Size() <= 0) { return; } WsfGeoPoint first = pts.Get(0); mDraw.SetColor(1.0,0.5,0.0); //orange? mDraw.SetLineStyle("solid"); mDraw.SetLineSize(2); mDraw.BeginPolyline(); for (int j = 0; j < pts.Size(); j = j + 1 ) { WsfGeoPoint pt = pts.Get(j); mDraw.Vertex(pt); } mDraw.Vertex(first); mDraw.End(); if (cluster.BearingValid()) { double bearing = cluster.Bearing(); WsfGeoPoint pt = cluster.MeanLocation(); mDraw.SetColor(1.0,1.0,1.0); //white? mDraw.BeginLines(); mDraw.Vertex(pt); pt.Extrapolate(cluster.Bearing(), 92600); //50 nautical miles mDraw.Vertex(pt); mDraw.End(); } end_script script WsfGeoPoint MeanLocation(Array assets) Vec3 mean = Vec3.Construct(0, 0, 0); if (assets.Size() > 0) { double scale = 1.0/((double)assets.Size()); foreach(WsfAssetPerception asset in assets) { Vec3 temp = asset.Location().LocationWCS(); temp.Scale(scale); mean = Vec3.Add(mean, temp); } } return WsfGeoPoint.ConstructWCS(mean); end_script script Array FlightLeadTaskGeneration (Array TRACKS, Array ASSETS ) Array tasks = Array(); //check if we are creating tasks or if we have a commander for that if (mSelfCreateTasks == true) { writeln_d(PLATFORM.Name(), ", T=", TIME_NOW, ", creating tasks for:"); //if its us, then create weapon tasks for enemy tracks for (int i=0; i perceivedThreats = PLATFORM.PerceivedThreats(); Array clusterMembers = (Array)t.AuxDataObject("ClusterMemberNames"); bool foundOne = false; foreach(string member in clusterMembers) { foreach(WsfTrack threat in perceivedThreats) { if (member == threat.TargetName()) { if (threat.Target().IsValid()) { //create a task for this target, we have perception of it WsfQuantumTask task = WsfQuantumTask.Construct(1.0, "weapon", threat); task.SetTaskType("WEAPON"); tasks.PushBack(task); foundOne = true; } break; } } } if (foundOne == false) { //cluster task complete, all target members gone PROCESSOR.SetTaskComplete(t,"SUCCESSFUL"); } } } ######################################################################### ## section for creating pincer tasks ######################################################################### if (mCreatePincerTasks == false) { return tasks; } Array assets = PLATFORM.PerceivedAssets(); if (assets.Size() < mNumSubsInvolved) { writeln_d("not enough assets for a pincer maneuver, need ", mNumSubsInvolved); return tasks; } if (mRequireActiveWeapons) { int weaponCount = 0; foreach(WsfAssetPerception perc in assets) { WsfPlatform plat = WsfSimulation.FindPlatform(perc.Index()); weaponCount += plat.WeaponsActiveFor(WsfTrackId()); } if (weaponCount <= 0) { writeln_d("no active weapons, no pincer yet"); return tasks; } } mSubPoint = MeanLocation(assets); #extern bool TestTrackCategory(WsfTrack, string); mTrackArray.Clear(); mTargetNames.Clear(); Array threats = PLATFORM.PerceivedThreats(); foreach(WsfTrack track in threats) { if ((track.IFF_Friend()) || (track.SideValid() && track.Side() == PLATFORM.Side()) || (!track. LocationValid()) || (track.SlantRangeTo(mSubPoint) > mThresholdDistance) || (TestTrackCategory(track, "unknown")) ) { continue; } track.SetBearing( track.Heading() ); #useful for the cluster processing mTrackArray.PushBack(track); mTargetNames.PushBack(track.TargetName()); } if (mTrackArray.Size() > 0) { mClusterManager.UpdateClusters(TIME_NOW,mTrackArray); if (mDrawClusters == true) { for (int i = 0; i < mClusterManager.Count(); i = i + 1 ) { draw_cluster_hull(mClusterManager.Entry(i)); } } if (mClusterManager.Count() > 1) { //check distances double near = MATH.DOUBLE_MAX(); double near2 = MATH.DOUBLE_MAX(); for (int i = 0; i < mClusterManager.Count(); i = i + 1 ) { double dist = mClusterManager.Entry(i).MeanLocation().SlantRangeTo(mSubPoint); if (dist < near) { if (near < near2) { near2 = near; } near = dist; } else if (dist < near2) { near2 = dist; } } double ratio = near2/near; if (ratio < mMinDistanceRatio) { writeln_d("threats too spread out! no pincer tasks"); return tasks; } } WsfCluster cluster = mClusterManager.Entry(0); mMeanPoint = cluster.MeanLocation(); mMeanBearingValid = cluster.BearingValid(); mMeanBearing = cluster.Bearing(); } else //no threats to pincer against, exit out { writeln_d("no threats to pincer against yet!"); return tasks; } //check separation of all subordinates //only relavent if not currently in phase 2 (dragging & chasing) int dragging = 0; # if(mPincerTask.IsValid()) # { # dragging = mPincerTask.AuxDataInt("left_drag") + mPincerTask.AuxDataInt("right_drag"); # } if (dragging == 0) { double standard = mMeanPoint.TrueBearingTo(mSubPoint); double min = 361; double max = -361; WsfPlatformList subs = PLATFORM.Subordinates(); foreach(WsfPlatform p in subs) { double b = MATH.NormalizeAngleMinus180_180(mMeanPoint.TrueBearingTo(p.Location()) - standard); min = MATH.Min(min, b); max = MATH.Max(max, b); } double diff = max - min; if (diff >= mMaxSeparationAngle) { writeln_d("pincer complete, targets flanked! no more pincer task!"); return tasks; } } writeln_d("creating pincers tasks! T=", TIME_NOW); //first find the group of target(s) we are going to perform the pincer against //generate a list of their names and get their mean location int N = assets.Size(); for(int i=0; i= 1) { double value = 1.0 / track.SlantRangeTo(ASSET.Location()); struct weaponData = GetWeaponData(ASSET.SystemType(i)); if (weaponData->type == ASSET.SystemType(i)) { if ((weaponData->domainAir && track.AirDomain()) || (weaponData->domainLand && track.LandDomain()) ) { return value; } } else { //check launch computers WsfPlatform shooter = WsfSimulation.FindPlatform(ASSET.Index()); if (shooter.IsValid()) { WsfWeapon weapon = shooter.Weapon(ASSET.SystemName(i)); if (weapon.IsValid()) { WsfLaunchComputer lcPtr = weapon.LaunchComputer(); if (lcPtr.IsValid()) { if (track.AirDomain() && lcPtr.IsA_TypeOf("WSF_AIR_TO_AIR_LAUNCH_COMPUTER")) { return value; } else if (track.LandDomain() && lcPtr.IsA_TypeOf("WSF_ATG_LAUNCH_COMPUTER")) { return value; } } } } } } } return 0; } else if (TASK.TaskType() == "PINCER") { WsfGeoPoint pt = (WsfGeoPoint)TASK.AuxDataObject("ZonePoint"); double dist = ASSET.Location().GroundRangeTo(pt); return 1.0/dist; } else { return 0; } end_script #show_task_messages script_debug_writes off update_interval 10.0 sec asset_representation platform reallocation_strategy dynamic generator custom FlightLeadTaskGeneration evaluator custom FlightLeadEvaluation #generator simple_weapon #evaluator distance allocator optimal_profit #allocator_extra_tasks optimal_profit allocator_extra_assets optimal_profit end_processor