diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index bd7560952..c97068865 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -32,11 +32,12 @@ -- @field #string EWR_Templates_Prefix Prefix to build the #GROUP_SET for EWR group -- @field @{Core.Set#GROUP_SET} EWR_Group The EWR #GROUP_SET -- @field @{Core.Set#GROUP_SET} Adv_EWR_Group The EWR #GROUP_SET used for advanced mode --- @field #string SEAD_Template_CC The ME name of the HQ object --- @field @{Wrapper.Group#GROUP} SEAD_CC The #GROUP object of the HQ +-- @field #string HQ_Template_CC The ME name of the HQ object +-- @field @{Wrapper.Group#GROUP} HQ_CC The #GROUP object of the HQ -- @field #table SAM_Table Table of SAM sites -- @field #string lid Prefix for logging --- @field @{Functional.Detection#DETECTION_AREAS} Detection The #DETECTION_AREAS object +-- @field @{Functional.Detection#DETECTION_AREAS} Detection The #DETECTION_AREAS object for EWR +-- @field @{Functional.Detection#DETECTION_AREAS} AWACS_Detection The #DETECTION_AREAS object for AWACS -- @field #boolean debug Switch on extra messages -- @field #boolean verbose Switch on extra logging -- @field #number checkradius Radius of the SAM sites @@ -48,6 +49,8 @@ -- @field #boolean advanced Use advanced mode, will decrease reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an HQ object -- @field #number adv_ratio Percentage to use for advanced mode, defaults to 100% -- @field #number adv_state Advanced mode state tracker +-- @field #boolean advAwacs Boolean switch to use Awacs as a separate detection stream +-- @field #number awacsrange Detection range of an optional Awacs unit -- @extends @{Core.Base#BASE} @@ -65,7 +68,33 @@ -- Set up your EWR system in the mission editor. Name the groups with common prefix like "Red EWR". Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc. -- [optional] Set up your HQ. Can be any group, e.g. a command vehicle. -- --- # 1. Start up your MANTIS +-- # 1. Basic tactical considerations when setting up your SAM sites +-- +-- ## 1.1 Radar systems and AWACS +-- +-- Typically, your setup should consist of EWR (early warning) radars to detect and track targets, accompanied by AWACS if your scenario forsees that. Ensure that your EWR radars have a good coverage of the area you want to track. +-- **Location** is of highest importantance here. Whilst AWACS in DCS has almost the "all seeing eye", EWR don't have that. Choose your location wisely, against a mountain backdrop or inside a valley even the best EWR system +-- doesn't work well. Prefer higher-up locations with a good view; use F7 in-game to check where you actually placed your EWR and have a look around. Apart from the obvious choice, do also consider other radar units +-- for this role, most have "SR" (search radar) or "STR" (search and track radar) in their names, use the encyclopedia to see what they actually do. +-- +-- ## 1.2 SAM sites +-- +-- Typically your SAM should cover all attack ranges. The closer the enemy gets, the more systems you will need to deploy to defend your location. Use a combination of long-range systems like the SA-10/11, midrange like SA-6 and short-range like +-- SA-2 for defense (Patriot, Hawk, Gepard, Blindfire for the blue side). For close-up defense and defense against HARMs or low-flying aircraft, helicopters it is also advisable to deploy SA-15 TOR systems, Shilka, Strela and Tunguska units, as well as manpads (Think Gepard, Avenger, Chaparral, +-- Linebacker, Roland systems for the blue side). If possible, overlap ranges for mutual coverage. +-- +-- ## 1.3 Typical problems +-- +-- Often times, people complain because the detection cannot "see" oncoming targets and/or Mantis switches on too late. Three typial problems here are +-- +-- * bad placement of radar units, +-- * overestimation how far units can "see" and +-- * not taking into account that a SAM site will take (e.g for a SA-6) 30-40 seconds between switching to RED, acquiring the target and firing. +-- +-- An attacker doing 350knots will cover ca 180meters/second or thus more than 6km until the SA-6 fires. Use triggers zones and the ruler in the missione editor to understand distances and zones. Take into account that the ranges given by the circles +-- in the mission editor are absolute maximum ranges; in-game this is rather 50-75% of that depending on the system. Fiddle with placement and options to see what works best for your scenario, and remember **everything in here is in meters**. +-- +-- # 2. Start up your MANTIS with a basic setting -- -- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)` -- @@ -80,13 +109,17 @@ -- to fine-tune your setup. -- -- `myredmantis:Start()` +-- +-- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup: +-- +-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")` -- --- # 2. Default settings +-- # 3. Default settings -- -- By default, the following settings are active: -- -- * SAM_Templates_Prefix = "Red SAM" - SAM site group names in the mission editor begin with "Red SAM" --- * EWR_Templates_Prefix = "Red EWR" - EWR group names in the mission editor begin with "Red EWR" - can also be AWACS +-- * EWR_Templates_Prefix = "Red EWR" - EWR group names in the mission editor begin with "Red EWR" - can also be combined with an AWACS unit -- * checkradius = 25000 (meters) - SAMs will engage enemy flights, if they are within a 25km around each SAM site - `MANTIS:SetSAMRadius(radius)` -- * grouping = 5000 (meters) - Detection (EWR) will group enemy flights to areas of 5km for tracking - `MANTIS:SetEWRGrouping(radius)` -- * acceptrange = 80000 (meters) - Detection (EWR) will on consider flights inside a 80km radius - `MANTIS:SetEWRRange(radius)` @@ -96,6 +129,11 @@ -- * autorelocate = false - HQ and (mobile) EWR system will not relocate in random intervals between 30mins and 1 hour - `MANTIS:SetAutoRelocate(hq, ewr)` -- * debug = false - Debugging reports on screen are set to off - `MANTIS:Debug(onoff)` -- +-- # 4. Advanced Mode +-- +-- Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Awacs is counted as one EWR unit. It will set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option. +-- E.g. `mymantis:SetAdvancedMode( true, 90 )` +-- Use this option if you want to make use of or allow advanced SEAD tactics. -- -- @field #MANTIS MANTIS = { @@ -106,11 +144,12 @@ MANTIS = { EWR_Templates_Prefix = "", EWR_Group = nil, Adv_EWR_Group = nil, - SEAD_Template_CC = "", - SEAD_CC = nil, + HQ_Template_CC = "", + HQ_CC = nil, SAM_Table = {}, lid = "", Detection = nil, + AWACS_Detection = nil, debug = false, checkradius = 25000, grouping = 5000, @@ -121,7 +160,10 @@ MANTIS = { advanced = false, adv_ratio = 100, adv_state = 0, - verbose = false + AWACS_Prefix = "", + advAwacs = false, + verbose = false, + awacsrange = 250000 } ----------------------------------------------------------------------- @@ -133,22 +175,24 @@ do --@param #MANTIS self --@param #string name Name of this MANTIS for reporting --@param #string samprefix Prefixes for the SAM groups from the ME, e.g. all groups starting with "Red Sam..." - --@param #string ewrprefix Prefixes for the EWR and AWACS groups from the ME, e.g. all groups starting with "Red EWR..." + --@param #string ewrprefix Prefixes for the EWR groups from the ME, e.g. all groups starting with "Red EWR..." --@param #string hq Group name of your HQ (optional) --@param #string coaltion Coalition side of your setup, e.g. "blue", "red" or "neutral" - --@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) + --@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) (optional) + --@param #string awacs Group name of your Awacs (optional) --@return #MANTIS self - function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic) + function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs) -- DONE: Create some user functions for these -- DONE: Make HQ useful -- DONE: Set SAMs to auto if EWR dies - -- TODO: Refresh SAM table in dynamic mode + -- DONE: Refresh SAM table in dynamic mode + -- DONE: Treat Awacs separately, since they might be >80km off site self.name = name or "mymantis" self.SAM_Templates_Prefix = samprefix or "Red SAM" self.EWR_Templates_Prefix = ewrprefix or "Red EWR" - self.SEAD_Template_CC = hq or nil + self.HQ_Template_CC = hq or nil self.Coalition = coaltion or "red" self.SAM_Table = {} self.dynamic = dynamic or false @@ -164,9 +208,16 @@ do self.adv_state = 0 self.verbose = false self.Adv_EWR_Group = nil + self.AWACS_Prefix = awacs or nil + self.awacsrange = 250000 --TODO: 250km, User Function to change + if type(awacs) == "string" then + self.advAwacs = true + else + self.advAwacs = false + end -- @field #string version - self.version="0.3.0" + self.version="0.3.5" env.info(string.format("***** Starting MANTIS Version %s *****", self.version)) -- Set the string id for output to DCS.log file. @@ -193,9 +244,8 @@ do end -- set up CC - if self.SEAD_Template_CC then - self.SEAD_CC = GROUP:FindByName(self.SEAD_Template_CC) - --self.SEAD_CC = COMMANDCENTER:New(GROUP:FindByName(self.SEAD_Template_CC),self.SEAD_Template_CC) + if self.HQ_Template_CC then + self.HQ_CC = GROUP:FindByName(self.HQ_Template_CC) end -- Inherit everything from BASE class. local self = BASE:Inherit(self, BASE:New()) -- #MANTIS @@ -269,13 +319,25 @@ do -- @param #MANTIS self -- @return Wrapper.GROUP#GROUP The HQ #GROUP object or *nil* if it doesn't exist function MANTIS:GetCommandCenter() - if self.SEAD_CC then - return self.SEAD_CC + if self.HQ_CC then + return self.HQ_CC else return nil end end + --- Function to set separate AWACS detection instance + -- @param #MANTIS self + -- @param #string prefix Name of the AWACS group in the mission editor + function MANTIS:SetAwacs(prefix) + if prefix ~= nil then + if type(prefix) == "string" then + self.AWACS_Prefix = prefix + self.advAwacs = true + end + end + end + --- Function to set the HQ object for further use -- @param #MANTIS self -- @param Wrapper.GROUP#GROUP The HQ #GROUP object to be set as HQ @@ -283,11 +345,11 @@ do local group = group or nil if group ~= nil then if type(group) == "string" then - self.SEAD_CC = GROUP:FindByName(group) - self.SEAD_Template_CC = group + self.HQ_CC = GROUP:FindByName(group) + self.HQ_Template_CC = group else - self.SEAD_CC = group - self.SEAD_Template_CC = group:GetName() + self.HQ_CC = group + self.HQ_Template_CC = group:GetName() end end end @@ -304,13 +366,13 @@ do -- @param #MANTIS self -- @param #boolean onoff If true, will activate Advanced Mode -- @param #number ratio [optional] Percentage to use for advanced mode, defaults to 100% - -- @usage Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an **HQ** object + -- @usage Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option. -- E.g. `mymantis:SetAdvancedMode(true, 90)` function MANTIS:SetAdvancedMode(onoff, ratio) self:F({onoff, ratio}) local onoff = onoff or false local ratio = ratio or 100 - if (type(self.SEAD_Template_CC) == "string") and onoff and self.dynamic then + if (type(self.HQ_Template_CC) == "string") and onoff and self.dynamic then self.adv_ratio = ratio self.advanced = true self.adv_state = 0 @@ -333,7 +395,7 @@ do if self.verbose then env.info(text) end -- start check if self.advanced then - local hq = self.SEAD_Template_CC + local hq = self.HQ_Template_CC local hqgrp = GROUP:FindByName(hq) if hqgrp then if hqgrp:IsAlive() then -- ok we're on, hq exists and as alive @@ -360,6 +422,14 @@ do local EWR_Group = self.Adv_EWR_Group --local EWR_Set = EWR_Group.Set local nalive = EWR_Group:CountAlive() + if self.advAwacs then + local awacs = GROUP:FindByName(self.AWACS_Prefix) + if awacs ~= nil then + if awacs:IsAlive() then + nalive = nalive+1 + end + end + end env.info(self.lid..string.format(" No of EWR alive is %d", nalive)) if nalive > 0 then return true @@ -426,8 +496,8 @@ do if self.verbose then env.info(text) end if self.autorelocate then -- relocate HQ - if self.autorelocateunits.HQ and self.SEAD_CC then --only relocate if HQ exists - local _hqgrp = self.SEAD_CC + if self.autorelocateunits.HQ and self.HQ_CC then --only relocate if HQ exists + local _hqgrp = self.HQ_CC self:T(self.lid.." Relocating HQ") local text = self.lid.." Relocating HQ" local m= MESSAGE:New(text,10,"MANTIS"):ToAll() @@ -510,6 +580,38 @@ do return _MANTISdetection end + --- Function to start the detection via AWACS if defined as separate + -- @param #MANTIS self + -- @return Functional.Detection #DETECTION_AREAS The running detection set + function MANTIS:StartAwacsDetection() + self:F(self.lid.."Starting Awacs Detection") + + -- start detection + local group = self.AWACS_Prefix + local groupset = SET_GROUP:New():FilterPrefixes(group):FilterCoalitions(self.Coalition):FilterStart() + local grouping = self.grouping or 5000 + --local acceptrange = self.acceptrange or 80000 + local interval = self.detectinterval or 60 + + --@param Functional.Detection #DETECTION_AREAS _MANTISdetection [internal] The MANTIS detection object + _MANTISAwacs = DETECTION_AREAS:New( groupset, grouping ) --[internal] Grouping detected objects to 5000m zones + _MANTISAwacs:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER }) + _MANTISAwacs:SetAcceptRange(self.awacsrange) --250km + _MANTISAwacs:SetRefreshTimeInterval(interval) + _MANTISAwacs:Start() + + function _MANTISAwacs:OnAfterDetectedItem(From,Event,To,DetectedItem) + --BASE:I( { From, Event, To, DetectedItem }) + local debug = false + if DetectedItem.IsDetected and debug then + local Coordinate = DetectedItem.Coordinate -- Core.Point#COORDINATE + local text = "Awacs Detection at "..Coordinate:ToStringLLDMS() + local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) + end + end + return _MANTISAwacs + end + --- Function to set the SAM start state -- @param #MANTIS self -- @return #MANTIS self @@ -524,24 +626,53 @@ do local engagerange = self.engagerange -- firing range in % of max --cycle through groups and set alarm state etc for _i,_group in pairs (SAM_Grps) do - --for i=1,#SAM_Grps do local group = _group - group:OptionAlarmStateGreen() - --group:OptionROEHoldFire() - --group:SetAIOn() + group:OptionAlarmStateGreen() -- AI off + group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --default engagement will be 75% of firing range + if group:IsGround() then + local grpname = group:GetName() + local grpcoord = group:GetCoordinate() + table.insert( SAM_Tbl, {grpname, grpcoord}) + table.insert( SEAD_Grps, grpname ) + end + end + self.SAM_Table = SAM_Tbl + -- make SAMs evasive + local mysead = SEAD:New( SEAD_Grps ) + mysead:SetEngagementRange(engagerange) + self.mysead = mysead + return self + end + + --- (Internal) Function to update SAM table and SEAD state + -- @param #MANTIS self + -- @return #MANTIS self + function MANTIS:_RefreshSAMTable() + self:F(self.lid.."Setting SAM Start States") + -- Requires SEAD 0.2.2 or better + -- get SAM Group + local SAM_SET = self.SAM_Group + local SAM_Grps = SAM_SET.Set --table of objects + local SAM_Tbl = {} -- table of SAM defense zones + local SEAD_Grps = {} -- table of SAM names to make evasive + local engagerange = self.engagerange -- firing range in % of max + --cycle through groups and set alarm state etc + for _i,_group in pairs (SAM_Grps) do + local group = _group group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --engagement will be 75% of firing range if group:IsGround() then local grpname = group:GetName() local grpcoord = group:GetCoordinate() - --local grpzone = ZONE_UNIT:New(grpname,group:GetUnit(1),5000) -- defense zone around each SAM site 5000 meters - --table.insert( SAM_Tbl, {grpname, grpcoord, grpzone}) table.insert( SAM_Tbl, {grpname, grpcoord}) -- make the table lighter, as I don't really use the zone here table.insert( SEAD_Grps, grpname ) end end self.SAM_Table = SAM_Tbl -- make SAMs evasive - SEAD:New( SEAD_Grps ) + if self.mysead ~= nil then + local mysead = self.mysead + mysead:UpdateSet( SEAD_Grps ) + end return self end @@ -556,11 +687,19 @@ do self:F(self.lid.."Starting MANTIS") self:SetSAMStartState() self.Detection = self:StartDetection() - + if self.advAwacs then + self.AWACS_Detection = self:StartAwacsDetection() + end + -- detection function local function check(detection) --get detected set local detset = detection:GetDetectedItemCoordinates() self:F("Check:", {detset}) + -- randomly update SAM Table + local rand = math.random(1,100) + if rand > 65 then -- 1/3 of cases + self:_RefreshSAMTable() + end -- switch SAMs on/off if (n)one of the detected groups is inside their reach local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates for _,_data in pairs (samset) do @@ -596,7 +735,6 @@ do end -- check advanced state local function checkadvstate() - if self.advanced then local interval, oldstate = self:_CheckAdvState() local newstate = self.adv_state if newstate ~= oldstate then @@ -606,6 +744,10 @@ do if self.MantisTimer.isrunning then self.MantisTimer:Stop() self.MantisTimer.isrunning = false + end -- stop Awacs timer + if self.MantisATimer.isrunning then + self.MantisATimer:Stop() + self.MantisATimer.isrunning = false end -- stop timer local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates for _,_data in pairs (samset) do @@ -621,25 +763,43 @@ do self.MantisTimer:Stop() self.MantisTimer.isrunning = false end + if self.MantisATimer.isrunning then + self.MantisATimer:Stop() + self.MantisATimer.isrunning = false + end self.MantisTimer = TIMER:New(check,self.Detection) self.MantisTimer:Start(5,interval,nil) self.MantisTimer.isrunning = true + if self.advAwacs then + self.MantisATimer = TIMER:New(check,self.AWACS_Detection) + self.MantisATimer:Start(15,interval,nil) + self.MantisATimer.isrunning = true + end end end -- end newstate vs oldstate - end -- end advanced end -- timers to run the system local interval = self.detectinterval self.MantisTimer = TIMER:New(check,self.Detection) self.MantisTimer:Start(5,interval,nil) self.MantisTimer.isrunning = true + -- Awacs timer + if self.advAwacs then + self.MantisATimer = TIMER:New(check,self.AWACS_Detection) + self.MantisATimer:Start(15,interval,nil) + self.MantisATimer.isrunning = true + end -- timer to relocate HQ and EWR - local relointerval = math.random(1800,3600) -- random between 30 and 60 mins - self.MantisReloTimer = TIMER:New(relocate) - self.MantisReloTimer:Start(relointerval,relointerval,nil) + if self.autorelocate then + local relointerval = math.random(1800,3600) -- random between 30 and 60 mins + self.MantisReloTimer = TIMER:New(relocate) + self.MantisReloTimer:Start(relointerval,relointerval,nil) + end -- timer for advanced state check - self.MantisAdvTimer = TIMER:New(checkadvstate) - self.MantisAdvTimer:Start(30,interval*5,nil) + if self.advanced then + self.MantisAdvTimer = TIMER:New(checkadvstate) + self.MantisAdvTimer:Start(30,interval*5,nil) + end return self end @@ -650,6 +810,9 @@ do if self.MantisTimer.isrunning then self.MantisTimer:Stop() end + if self.MantisATimer.isrunning then + self.MantisATimer:Stop() + end if self.autorelocate then self.MantisReloTimer:Stop() end