# **************************************************************************** # 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 manage-pincer script_debug_writes off script_variables //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 = ""; //useful to specify the name of zone in which we ignore the threat's //orientation, because they are capping while 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; WsfRIPRJob mPincerJob; WsfGeoPoint mMeanPoint; WsfGeoPoint mSubPoint; end_script_variables on_init 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) ; end_on_init ## 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 plats) Vec3 mean = Vec3.Construct(0, 0, 0); if (plats.Size() > 0) { double scale = 1.0/((double)plats.Size()); foreach(WsfPlatform plat in plats) { Vec3 temp = plat.LocationWCS(); temp.Scale(scale); mean = Vec3.Add(mean, temp); } } return WsfGeoPoint.ConstructWCS(mean); end_script script WsfGeoPoint SubordinatesMeanLocation() return MeanLocation(((WsfRIPRProcessor)PROCESSOR).SubordinatePlatforms()); end_script script void RemovePincerJob() if(mPincerJob.IsValid()) { //remove job from board WsfRIPRJob fake; writeln_d(PLATFORM.Name(), " job change, REMOVE: ", mPincerJob.GetDescription()); PLATFORM.Comment(write_str("REMOVE job: ", mPincerJob.GetDescription())); ((WsfRIPRProcessor)PROCESSOR).RemoveJob(mPincerJob); mPincerJob = fake; } end_script precondition #writeln_d("precondition manage-pincer"); if (!PROCESSOR.IsA_TypeOf("WSF_RIPR_PROCESSOR")) { return Failure("behavior not attached to a RIPR processor!"); } //((WsfRIPRProcessor)PROCESSOR) if (((WsfRIPRProcessor)PROCESSOR).SubordinateProcessors().Size() < mNumSubsInvolved) { RemovePincerJob(); writeln_d("not enough subordinates"); return Failure("not enough subordinates"); } if (mRequireActiveWeapons) { int weaponCount = 0; Array subs = ((WsfRIPRProcessor)PROCESSOR).SubordinateProcessors(); foreach(WsfRIPRProcessor proc in subs) { weaponCount = weaponCount + proc.WeaponsActive(); } if (weaponCount <= 0) { RemovePincerJob(); writeln_d("no active weapons, no pincer yet"); return Failure("no active weapons, no pincer yet"); } } mSubPoint = SubordinatesMeanLocation(); #extern bool TestTrackCategory(WsfTrack, string); mTrackArray.Clear(); mTargetNames.Clear(); foreach (WsfLocalTrack track in PLATFORM.MasterTrackList()) { 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) { RemovePincerJob(); writeln_d("threats too spread out!"); return Failure("threats too spread out!"); } } WsfCluster cluster = mClusterManager.Entry(0); mMeanPoint = cluster.MeanLocation(); mMeanBearingValid = cluster.BearingValid(); mMeanBearing = cluster.Bearing(); } else //no threats to pincer against, exit out { RemovePincerJob(); writeln_d("no threats to pincer against yet!"); return Failure("no threats to pincer against yet!"); } //check separation of all subordinates //only relavent if not currently in phase 2 (dragging & chasing) int dragging = 0; if(mPincerJob.IsValid()) { dragging = (int)mPincerJob.GetData("left_drag") + (int)mPincerJob.GetData("right_drag"); } if (dragging == 0) { Array subs = ((WsfRIPRProcessor)PROCESSOR).SubordinatePlatforms(); WsfGeoPoint subMean = MeanLocation(subs); double standard = mMeanPoint.TrueBearingTo(subMean); double min = 361; double max = -361; 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) { RemovePincerJob(); writeln_d("pincer complete, targets flanked!"); return Failure("pincer complete, targets flanked!"); } } mDraw.SetDuration(PROCESSOR.UpdateInterval()); return true; end_precondition execute writeln_d("executing manage-pincer, 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 Array subs = ((WsfRIPRProcessor)PROCESSOR).SubordinateProcessors(); int N = subs.Size(); if (mPincerJob.IsValid()) { //pincer job already created! just update it mPincerJob.SetData("ZoneThreatNameArray", mTargetNames); mPincerJob.SetData("ZonePoint", mMeanPoint); mPincerJob.SetData("ZoneBearingValid", mMeanBearingValid); mPincerJob.SetData("ZoneBearing", mMeanBearing); mPincerJob.SetData("all_on_cap", 0); if (mCapZoneName != "" && mPincerJob.IsValid()) { bool allOnCap = true; foreach(WsfTrack t in mTrackArray) { if ( ! t.ReportedLocation().WithinZone(mCapZoneName)) { allOnCap = false; break; } } if (allOnCap) { mPincerJob.SetData("all_on_cap", 1); } } } else { //create a pincer job for this flight group string desc = write_str("pincer-", mNumSubsInvolved); extern double cPincerPriority; mPincerJob = WsfRIPRJob.Create( ((WsfRIPRProcessor)PROCESSOR), //creator "pincer", //name desc, //description cPincerPriority * mNumSubsInvolved, //priority (must be enough to break even with "mNumSubsInvolved" jobs of another type mNumSubsInvolved); //max winnners mPincerJob.SetData("ZoneThreatNameArray", mTargetNames); mPincerJob.SetData("ZonePoint", mMeanPoint); mPincerJob.SetData("ZoneBearingValid", mMeanBearingValid); mPincerJob.SetData("ZoneBearing", mMeanBearing); mPincerJob.SetData("left_drag", 0); mPincerJob.SetData("rightdrag", 0); mPincerJob.SetData("all_on_cap", 0); mPincerJob.SetWinnersMin(mNumSubsInvolved); //setup the left & right flyers double avgBearing = mMeanPoint.TrueBearingTo(mSubPoint); foreach(WsfRIPRProcessor proc in subs) { //Vec3 temp = proc.Platform().LocationWCS(); double bearing = mMeanPoint.TrueBearingTo(proc.Platform().Location()); double relAngle = MATH.NormalizeAngleMinus180_180(bearing-avgBearing); if (mCross) { relAngle = -1 * relAngle; } if (relAngle < 0) { mPincerJob.SetData(proc.Platform().Name(), "right"); } else { mPincerJob.SetData(proc.Platform().Name(), "left"); } } ((WsfRIPRProcessor)PROCESSOR).AddJob(mPincerJob); writeln_d("--- ", PLATFORM.Name(), " job change, ADD: ", mPincerJob.GetDescription() ); PLATFORM.Comment(write_str("ADD job: ", mPincerJob.GetDescription())); # foreach(string tgt in mTargetNames) # { # PLATFORM.Comment(tgt); # } } end_execute end_behavior