I wrote this code to allow scenario designers to build a flexible model of a hierarchical IADS that would include the possibility of clipping the kill chain by striking various echelon command posts. IADS elements will move periodically, following their emission period.
This first part is intended to be executed on scenario load. It defines the IADS hierarchy and initializes the state table for its components.
-- initialization
asvRadarGuid = 'GWKW9V-0HME4FG8F0I72'
bgdCPguid = 'GWKW9V-0HME4FGAIAD73'
sa10rgtCPguid = 'GWKW9V-0HME4FG8F0HPC'
sa10bnCPguid = 'GWKW9V-0HME4FG95CU8I'
sa10bmRadarGuid = 'GWKW9V-0HME4FG8F0HPJ'
sa10Bn1Guid='GWKW9V-0HME4FG8F0I36'
sa10Bn2Guid='GWKW9V-0HME4FG8F0I0B'
sa10Bn3Guid='GWKW9V-0HME4FG8F0HPO'
sa21rgtCPguid = 'GWKW9V-0HME4FGAI6D4S'
sa21bnCPguid = 'GWKW9V-0HME4FGAHSGSH'
sa21bmRadarGuid = 'GWKW9V-0HME4FGAHTDR6'
sa21Bn1Guid= 'GWKW9V-0HME4FGAHRUG7'
sa21Bn2Guid= 'GWKW9V-0HME4FGAHS03V'
sa21Bn3Guid= 'GWKW9V-0HME4FGAHS15G'
pantsirCpyCP = 'GWKW9V-0HME4FGBBM6A5'
pantsirCpyBMRadr = 'GWKW9V-0HME4FGBBPDRI'
pantsirPlt1 = 'GWKW9V-0HME4FGBBM05M'
pantsirPlt2 = 'GWKW9V-0HME4FGBBM1RT'
pantsirPlt3 = 'GWKW9V-0HME4FGBBM2I5'
pantsirBMMaxRange = 135 -- Max launch range of a FLATFACE-E Radar
pantsirRange = 10 -- Max launch range of an SA-22
sa10BMRMaxRange = 325 -- Max range for a BIG BIRD B
sa10Range = 40 -- Max launch range of an SA-10
sa21BMRMaxRange = 325 -- Max range for a BIG BIRD D
sa21Range = 215 -- Max launch range of an SA-21
-- weapons control states
wcsFree = 0
wcsTight = 1
wcsHold = 2
sa10AirDefenseRgt = {
commandPost=sa10rgtCPguid,
equipment={sa10bmRadarGuid},
rdrRng=0.5*sa10BMRMaxRange,
emitTime = 8,
tearDownTime = 2,
repositionTime = 2,
setUpTime = 2,
standByTime = 2,
subordinates={ sa10AirDefenseBns }}
sa10AirDefenseBns = {
commandPost=sa10bnCPguid,
equipment={sa10Bn1Guid,
sa10Bn2Guid, sa10Bn3Guid},
rdrRng=0.8*sa10Range,
emitTime = 8,
tearDownTime = 2,
repositionTime = 2,
setUpTime = 2,
standByTime = 2,
subordinates={}}
sa21AirDefenseRgt = {
commandPost=sa21rgtCPguid,
equipment={sa21bmRadarGuid },
rdrRng=0.75*sa21BMRMaxRange,
emitTime = 8,
tearDownTime = 2,
repositionTime = 2,
setUpTime = 2,
standByTime = 2,
subordinates={ sa21AirDefenseBns }}
sa21AirDefenseBns = {
commandPost=sa21bnCPguid,
equipment={sa21Bn1Guid, sa21Bn2Guid, sa21Bn3Guid},
rdrRng=0.8*sa21Range,
emitTime = 8,
tearDownTime = 2,
repositionTime = 2,
setUpTime = 2,
standByTime = 2,
subordinates={}}
pansirAirDefenseCpy = {
commandPost=pantsirCpyCP,
equipment={pantsirCpyBMRadr},
rdrRng=0.8*pantsirBMMaxRange,
emitTime = 8,
tearDownTime = 2,
repositionTime = 2,
setUpTime = 2,
standByTime = 2,
subordinates={ pantsirAirDefensePlts }}
pantsirAirDefensePlts = {
commandPost=pantsirCpyCP,
equipment={pantsirPlt1, pantsirPlt2, pantsirPlt3},
rdrRng=0.8*pantsirRange,
emitTime = 8,
tearDownTime = 2,
repositionTime = 2,
setUpTime = 2,
standByTime = 2,
subordinates={}}
airDefenseBrigade = {
commandPost=bgdCPguid ,
equipment={},
rdrRng=0,
emitTime = 8,
tearDownTime = 2,
repositionTime = 2,
setUpTime = 2,
standByTime = 2,
subordinates={ sa21AirDefenseRgt, sa10AirDefenseRgt }}
unitStates = {}
function initializeUnitStates(unit, thisSide)
initialized = false
if(ScenEdit_GetUnit({side=mySide, guid=unit.commandPost}) ~= nil) then
if(unit.subordinates ~= nil) then
for s, sub in ipairs(unit.subordinates) do
initializeUnitStates(sub, thisSide)
end
end
if(unit.equipment ~= {}) then
for e, eqp in ipairs(unit.equipment) do
timeToCompleteEvolution = unit.emitTime + unit.tearDownTime + unit.repositionTime + unit.setUpTime + unit.standByTime
timeToCeaseEmission = unit.emitTime
timeToCompleteTearDown = timeToCeaseEmission + unit.tearDownTime
timeToCompleteRelocation = timeToCompleteTearDown + unit.repositionTime
timeToCompleteSetUp = timeToCompleteRelocation + unit.setUpTime
timeToStandBy = timeToCompleteSetUp + unit.standByTime
time = math.random(0, timeToCompleteEvolution )
if time < timeToCeaseEmission then
unitStates[eqp] = { state="canEmit", minutesInState = time }
elseif ( (time >= timeToCeaseEmission) and ( time < timeToCompleteTearDown )) then
unitStates[eqp] = { state="tearingDown", minutesInState = (time - timeToCeaseEmission) }
elseif ( ( time >= timeToCompleteTearDown ) and (time < timeToCompleteRelocation )) then
unitStates[eqp] = { state="repositioning", minutesInState = (time - timeToCompleteTearDown) }
elseif ( (time >= timeToCompleteRelocation ) and ( time < timeToCompleteSetUp )) then
unitStates[eqp] = { state="settingUp", minutesInState = (time - timeToCompleteRelocation) }
else
unitStates[eqp] = { state="standingBy", minutesInState = (time - timeToCompleteSetUp ) }
end
end
end
return true
else
return false -- return false if unit is improperly defined
end
end
initializeUnitStates(airDefenseBrigade)
The next bit of code is intended to executed every minute. It manages whether whether the IADS components are setting up, tearing down, moving, or whether they could emit should a worthwhile target come close enough to make it worth lighting up one's organic radars.
-- execute every minute
function randomWaypoint(currentPosition, dist)
math.randomseed( os.time() ) -- removes correlations (not)
r = math.random(0, 359)
print(r)
newpos=World_GetPointFromBearing( {latitude=tostring(currentPosition.latitude), longitude=tostring(currentPosition.longitude), distance = dist, bearing = r} )
print(newpos)
--wpt = {latitude = newpos.latitude, longitude = newpos.longitude}
wpt = {TypeOf = 'ManualPlottedCourseWaypoint', latitude = newpos.latitude, longitude = newpos.longitude}
return wpt
end
function manageUnitEMCONandMobilityStates(unit, thisSide)
if(ScenEdit_GetUnit({side=thisSide, guid=unit.commandPost}) ~= nil) then
if(unit.subordinates ~= nil) then
for s, sub in ipairs(unit.subordinates) do
manageUnitEMCONandMobilityStates(sub, thisSide)
end
end
if(next(unit.equipment) ~= nil) then
timeToCompleteEvolution = unit.emitTime + unit.tearDownTime + unit.repositionTime + unit.setUpTime + unit.standByTime
for e, eqp in ipairs(unit.equipment) do
if( unitStates[eqp] ~= nil) then
unitStates[eqp].minutesInState = unitStates[eqp].minutesInState + 1
print(eqp..": state = "..unitStates[eqp].state..", minutesinstate = "..unitStates[eqp].minutesInState)
if unitStates[eqp].state == 'canEmit' then
ScenEdit_SetUnit({side=thisSide, guid=eqp, course={}, speed = 0, holdposition=true })
if unitStates[eqp].minutesInState >= unit.emitTime then
unitStates[eqp].state = 'tearingDown'
unitStates[eqp].minutesInState = 0
break
end
elseif unitStates[eqp].state == 'tearingDown' then
ScenEdit_SetUnit({side=thisSide, guid=eqp, course={}, speed = 0, holdposition=true })
if unitStates[eqp].minutesInState >= unit.tearDownTime then
unitStates[eqp].state = 'repositioning'
unitStates[eqp].minutesInState = 0
break
end
elseif unitStates[eqp].state == 'repositioning' then
repositionSpeed = 35 -- NM/hr
u = ScenEdit_GetUnit({side=thisside, guid=eqp})
if ( unitStates[eqp].minutesInState < unit.repositionTime) then
newPosition = randomWaypoint( {latitude=u.latitude, longitude=u.longitude}, (repositionSpeed * unit.repositionTime / 60) )
--print(u.name..": latitude= "..newPosition.latitude..", longitude= "..newPosition.longitude)
ScenEdit_SetUnit({side=thisSide, guid=eqp, course={newPosition}, speed = repositionSpeed, holdposition=false })
print(u.course)
else
if unitStates[eqp].minutesInState >= unit.repositionTime then
unitStates[eqp].state = 'settingUp'
unitStates[eqp].minutesInState = 0
break
end
end
elseif unitStates[eqp].state == 'settingUp' then
ScenEdit_SetUnit({side=thisSide, guid=eqp, course={}, speed = 0, holdposition=true })
if unitStates[eqp].minutesInState >= unit.setUpTime then
unitStates[eqp].state = 'standingBy'
unitStates[eqp].minutesInState = 0
break
end
elseif unitStates[eqp].state == 'standingBy' then
ScenEdit_SetUnit({side=thisSide, guid=eqp, course={}, speed = 0, holdposition=true })
if unitStates[eqp].minutesInState >= timeToCompleteEvolution then
unitStates[eqp].state = 'canEmit'
unitStates[eqp].minutesInState = 0
break
end
end
end
end
end
end
return true
end
manageUnitEMCONandMobilityStates( airDefenseBrigade )
This third part is intended to execute every 15 seconds. It recursively descends the hierarchy tree and determines if a unit component is in a state where it could emit, then adjusts its WCS and EMCON appropriately.
-- execute every 15 seconds
function equipmentCanEmit(elementGuid)
canEmit = false
if(unitStates[elementGuid] ~= nil) then
if( unitStates[elementGuid].state == "canEmit" ) then
canEmit = true
end
end
return canEmit
end
function atLeastOneContactIsInRangeOfEquipment(list, unitGuid, range)
cntctInRng = false
for c, cntc in ipairs(contactList) do
trk = ScenEdit_GetContact({side=mySide, guid=cntc.guid})
trkRng = Tool_Range(unitGuid, {latitude=trk.latitude, longitude=trk.longitude})
print(trkRng..", "..range)
if (trkRng <= range) then
cntctInRng = true;
end
end
return cntctInRng
end
function manageIADSEchelonEmconAndWCS(unit, thisSide)
if(ScenEdit_GetUnit({side=mySide, guid=unit.commandPost}) ~= nil) then
if(unit.subordinates ~= nil) then
for s, sub in ipairs(unit.subordinates) do
manageIADSEchelonEmconAndWCS(sub, thisSide)
end
end
contactList = ScenEdit_GetContacts(thisSide)
if(unit.equipment ~= {}) then
for e, eqp in ipairs(unit.equipment) do
if( ScenEdit_GetUnit({side=thisSide, guid=eqp}) ~= nil ) then
u=ScenEdit_GetUnit({side=thisSide, guid=eqp})
if( atLeastOneContactIsInRangeOfEquipment(contactList, eqp, unit.rdrRng) and equipmentCanEmit(eqp) ) then
print("Turning on radar.")
ScenEdit_SetEMCON('Unit', eqp, "Radar=Active;Sonar=Passive;OECM=Passive")
ScenEdit_SetDoctrine({side = thisSide, unitname=u.name }, { weapon_control_status_air = wcsTight })
else
print("Turning off radar.")
ScenEdit_SetEMCON('Unit', eqp, "Radar=Passive;Sonar=Passive;OECM=Passive")
ScenEdit_SetDoctrine({side = thisSide, unitname=u.name }, { weapon_control_status_air = wcsHold })
end
end
end
end
return true
else
-- execute alternative emcon/wcs management plan (currently none)
return false -- return false if echelon command post is destroyed
end
end
manageIADSEchelonEmconAndWCS(airDefenseBrigade, "RUS")
Merry Christmas!