How It Works¶
Activation Range vs Engagement Zone¶
Every SAM has two range values:
- Activation Range (actRange): When the EW feeds contact data in integrated mode, the SAM goes ALERT and starts tracking at this range. This gives the DCS AI time to build a fire solution before the target reaches missile range.
- Engagement Zone (WEZ/NEZ): The actual missile engagement envelope. In EMCON mode, the SAM only breaks cover when a target is inside this range.
For example, the SA-2 has actRange=30 NM and WEZ=24 NM. In integrated mode it starts tracking at 30 NM, giving the AI 6 NM of lead time (~45s at 480 kts). In EMCON mode it stays hidden until 24 NM.
The activation range automatically tracks zone overrides. Each system has a built-in margin (actRange minus WEZ) representing the DCS AI lead time. When a NEZ or WEZ override changes the engagement zone, the activation range adjusts to preserve that margin. For example, an SA-6 (wez=18, actRange=22, margin=4) with a NEZ override (nez=6) activates at 10 NM (6+4), not the default 22 NM. The ACT suffix always overrides this calculation.
State Machine¶
Every SAM site is in one of seven states:
| State | Emissions | ROE | Description |
|---|---|---|---|
| DARK | OFF | HOLD | No threat. Invisible to RWR. |
| AWARE | OFF | HOLD | EW has contacts but none in this SAM's activation range. |
| ALERT | ON | WEAPONS FREE | Contact in activation range. Fully hot, engaging. |
| EMCON_ON | OFF | HOLD | EMCON active. Emissions silent. Cycling. |
| EMCON_OFF | ON | HOLD | EMCON lifted. Radar sweeping, not yet engaging. |
| EMCON_ENGAGED | ON | WEAPONS FREE | Was EMCON, found target in WEZ, weapons free. |
| DESTROYED | -- | -- | Dead. Terminal state. |
Integrated Operations (EW Alive)¶
Contact enters theater
-> EW detects contact
-> SAMs in sector go AWARE (still dark, no emissions)
-> Contact enters a SAM's activation range
-> That SAM goes ALERT (emissions on, weapons free)
-> Contact leaves activation range
-> SAM returns to AWARE (next poll cycle)
-> Contact leaves theater
-> SAM returns to DARK (after alertTimeout)
From the cockpit: RWR is clean until you push into the activation range.
Alert Frustration¶
A SAM in ALERT checks whether any EW-fed contact is actually inside its WEZ (not just the activation range). If the target loiters in the activation zone without ever entering the WEZ:
- After 30-60s (randomized) of ALERT with no WEZ contacts, the crew gets antsy
- 10% chance they stay hot (re-rolls the timeout), 90% chance they power down to AWARE
- After powering down, the crew enters a frustration cooldown (another 30-60s randomized). During cooldown, the same contact in actRange won't re-alert the SAM — the crew is ignoring it
- A WEZ contact breaks cooldown immediately — that's a real threat and the crew snaps back to ALERT
- Once cooldown expires, the next poll cycle can re-alert the SAM if contacts are still in actRange (with a fresh frustration timeout)
This uses the same contacts the EW poll already gathered — zero additional API calls.
Degraded Operations (EW Dead)¶
When a SAM loses EW coverage (or was never networked), it enters EMCON cycling:
EMCON_ON (silent, 30-120s)
-> EMCON_OFF (sweep, 15-45s)
-> Target in WEZ? EMCON_ENGAGED (weapons free)
-> No target? Back to EMCON_ON
HARM Reaction¶
AEGIS hooks S_EVENT_SHOT and identifies anti-radiation missiles by weapon descriptor (missileCategory=6, guidance=5).
TOO/SP Mode (HARM locked on a SAM)¶
getTarget() returns the targeted unit. AEGIS maps it to a tracked SAM, then the crew processes the warning (8-12s randomized delay — detection + classification + crew action) before reacting:
| Condition | Reaction | Behavior |
|---|---|---|
| Multi-HARM saturation (2+ in 15s) | GO_DARK | Even capable crews bail under saturation |
| Nat 20 bravery roll (5% chance) | DEFIANT | Any crew might decide to fight back |
| selfProtect=true (SA-10, SA-11, PATRIOT, SA-15, TOR) | STAY_HOT | Engages ARM with own missiles (30s window) |
| selfProtect=true but crew panics (15% chance) | GO_DARK | Not every crew is brave |
| Has live PD child | LAST_DITCH | PD defends 8-12s, then parent goes dark |
| Neither | GO_DARK | Classic HARM dodge with jittered cooldown (45-90s) |
The bravery roll is checked after multi-HARM saturation (which always forces GO_DARK) but before the normal selfProtect/PD/GO_DARK tree. When it hits, the SAM executes as STAY_HOT — 30s engagement window, same as selfProtect. Even a non-selfProtect SA-2 crew might decide today's the day they earn a medal. F10 marker shows DEFIANT (crew fighting back!).
selfProtect values are in SYSTEM_DB and can be hand-edited by mission makers.
During any HARM reaction, a cooldown prevents the EW poll from overriding the SAM's state. The cooldown starts at detection time (not after the crew delay) — the SAM's state is frozen the moment the crew begins processing the threat. This prevents alert frustration or other poll-driven transitions from overriding a pending HARM reaction. The actual reaction (STAY_HOT, LAST_DITCH, GO_DARK) overwrites the cooldown with its own duration. If the HARM is still in flight when cooldown expires, it auto-extends (checked via weapon:isExist()). Hard cap of 180s prevents infinite extension.
HARM Targeting a PD¶
When a HARM targets a point defense unit, AEGIS redirects the reaction to the parent SAM. A HARM aimed at a co-located PD is functionally a HARM aimed at the entire site. The PD stays hot -- engaging HARMs is its job.
PB Mode (HARM aimed at coordinates, no lock)¶
getTarget() returns nil. AEGIS stores the weapon reference and checks trajectory 2 seconds later (velocity is {0,0,0} at launch but populates within ~0.5s). A single position + velocity sample projects a ray forward. SAMs within 5 NM (configurable) of the projected path are affected.
Two independent detection paths:
-
harmInboundflag — Set on ALL SAMs in trajectory, regardless of EW coverage. If the SAM emits while the flag is active (from any cause — EW activation, EMCON sweep, weasel bait), the SAM detects the HARM on its own tracking radar and enters the HARM reaction tree (8-12s crew delay). Flag expires atETA + 30s margin. If the SAM never emits, the flag expires silently. -
EW network warning — If the SAM has a live EW in its sector, the EW detects the HARM via radar sweeps using a score-per-sweep model. Detection delay is based on range from the HARM to the EW — 12s at close range, 60s+ at 40 NM, effectively never at 70+ NM. Multiple EWs sum scores each sweep. After detection, a 3-5s unit reaction delay, then the SAM gets warned. Warning only sent if total delay < HARM ETA.
EW Network Gate
EW warnings require a live EW close enough to detect a small RCS target. An autonomous SAM (EW dead) only has the harmInbound path — it must emit to detect the HARM.
| Situation | Detection Path | Action |
|---|---|---|
| SAM emitting, harmInbound active | Own-radar detection | HARM reaction tree (8-12s delay) |
| SAM dark, close EW (< 30 NM) | EW network warning (12-30s) | Emitting: HARM reaction. Dark: EMCON suppressed + PD activation |
| SAM dark, distant EW (> 40 NM) | EW too slow, harmInbound only | Flag waits. SAM baited up → own-radar detection |
| SAM dark, no EW | harmInbound only | Flag waits. SAM stays dark → flag expires, SAM survives |
| PB HARM + weasel baits SAM | harmInbound triggers | SAM detects HARM on own radar, reacts |
Point Defense¶
Point defense SAMs (SA-15, Tor, etc.) slave to their nearest parent SAM or EW:
- Parent goes ALERT -> PD goes ALERT
- Parent goes DARK -> PD goes DARK
- PD units don't use their own WEZ for activation
- During HARM events: PD activates immediately to defend parent (bypasses poll cycle)
- PD stays ALERT through full HARM cooldown -- even after parent goes dark, PD remains hot to engage inbound HARMs
- PB HARM warning: PD goes ALERT to defend a dark parent when network detects inbound HARM
Place PD groups within 5 NM of their parent and name them PD-TYPE-SECTOR-ID.
Orphan Promotion¶
When a parent SAM is destroyed, its PD children don't just go dark forever — they promote to autonomous SAMs:
- The PD creates a full SAM entry using its own system data (SA-15 with wez=8, actRange=10, selfProtect=true)
- Starts EMCON cycling independently in the parent's sector
- Responds to HARMs through the normal reaction tree (SA-15 has selfProtect)
- Shows up in status reports and F10 markers as a SAM, not a PD
An orphaned SA-15 is still a capable short-range SAM. Kill a parent SA-10 and the defending SA-15 doesn't give up — it starts hunting on its own.
Decoy Effectiveness¶
Decoys (TALDs, ADM-141A) appear as real radar contacts to the IADS. This means:
- EWRs detect them and feed them to the contact list
- SAMs check activation range and go ALERT
- DCS AI locks and engages the decoy with missiles
SEAD packages with TALDs ahead of strike aircraft will force SAM emissions and waste missiles -- exactly as designed in real life. A 10-TALD swarm is a perfectly valid tactic.
Contact Filtering¶
DCS radar detection returns everything in the air. AEGIS filters out weapons that shouldn't trigger SAM activation:
| Object | Triggers SAM? | Why |
|---|---|---|
| Aircraft / helicopters | Yes | Real threats |
| TALDs (ADM-141A) | Yes | Decoys designed to fool radar |
| Mk-82 / dumb bombs | No | Ordnance, not a trackable threat |
| AGM-88 HARM | No | Would give away HARM presence to IADS |
| AIM-120 / AAMs | No | Air-to-air, not relevant |
| SAM missiles | No | Friendly fire prevention |
EMCON Jitter¶
EMCON cycling is not a metronome. Each cycle varies based on:
- Startup jitter: 0-60s random delay before first cycle. SAMs entering EMCON at the same time desynchronize immediately.
- Threat memory: SAM detected something last sweep? Next silent phase is shorter (crew is anxious). Three empty sweeps in a row? Silent phase gets longer (crew relaxes).
- Quick peek: 20% chance of a very short 3-6 second sweep instead of the full window. Brief flash on RWR then gone.
- Double-sweep: 15% chance of a brief pause then sweeping again if a contact was seen outside the WEZ. Crew wants another look.
- Neighbor spook (optional): A nearby SAM gets killed? Neighboring EMCON SAMs go extra silent for 2 minutes. They know SEAD is active.
- Reengage jitter: After engaging, SAM goes dark after 10-30s (random) once target leaves the WEZ.
Zone and Activation Overrides¶
Override a SAM's engagement zone and activation range directly in its ME group name. Suffixes are order-independent:
SAM-SA10-NORTH-1 Default WEZ (39 NM), default ACT (50 NM)
SAM-SA10-NORTH-1-NEZ NEZ with database default (20 NM), ACT auto-derives (24 NM)
SAM-SA10-NORTH-1-NEZ25 NEZ at 25 NM, ACT auto-derives (25 + 11 = 36 NM)
SAM-SA10-NORTH-1-WEZ30 WEZ override at 30 NM, ACT auto-derives (30 + 11 = 41 NM)
SAM-SA10-NORTH-1-ACT60 Default WEZ, activation forced to 60 NM
SAM-SA2-NORTH-1-NEZ-ACT30 NEZ + activation forced to 30 NM (ACT overrides auto-derive)
SAM-SA6-SOUTH-ACT25-NEZ Same as NEZ-ACT25 (order doesn't matter)
When a NEZ or WEZ suffix changes the engagement zone, the activation range automatically adjusts to preserve the system's built-in margin (actRange - WEZ from the database). An explicit ACT suffix always takes priority over the auto-derived value.
Parsed once at mission load. Zero runtime cost.
EW Detection Range Override¶
Limit an EW's effective detection range directly in its ME group name with the DET{range} suffix:
EW-NORTH No limit (DCS radar model determines range)
EW-NORTH-DET120 120 NM detection cap
EW-NORTH-2-DET80 EW #2 in NORTH, 80 NM cap
Contacts beyond the detection range are dropped before feeding to the sector. Models degraded radars, terrain masking, or doctrinal placement — a hilltop 1L13 and a mobile P-19 can have different effective ranges without touching DCS unit properties.
A global default can be set via ewDetectionRange in the config table (0 = no limit). The per-EW DET suffix always takes priority. Parsed once at init, zero runtime cost — the range check is a single squared-distance comparison per contact, short-circuited when no cap is set.
Mission Kill vs Full Kill¶
AEGIS distinguishes between two types of SAM death:
Mission Kill — The tracking radar is destroyed but support units survive. Applies to SAM types with a dedicated tracking radar (SA-2, SA-3, SA-5, SA-6, SA-10, HAWK, PATRIOT). The SAM is combat-ineffective — it can't track or engage — even though DCS considers the group alive. AEGIS marks it DESTROYED and silences all surviving units (emissions off, weapons hold). Orphan PD promotion fires normally.
Full Kill — The entire DCS group is destroyed (getSize() == 0). Applies to all SAM types, including self-contained systems (SA-8, SA-11, SA-15, TOR, etc.) that have no single critical unit.
At init, AEGIS scans each SAM group for the tracking radar unit by DCS type name (e.g., S-300PS 40B6M tr for SA-10). The init log shows (TR: unitname) @(x, z) for SAMs with critical unit tracking — the coordinates enable cross-mission position comparison by diffing topology logs. On each death event, AEGIS checks the tracking radar first (Tier 1), then falls back to the group check (Tier 2). Self-contained systems skip Tier 1 entirely — zero overhead.
Kill SA-10's Flap Lid radar (1 of 18 units)
-> *** SAM MISSION KILL: SAM-SA10-SOUTH-1 (tracking radar destroyed)
-> Surviving 17 units silenced (emissions off, weapons hold)
-> Orphan PD-SA15 promotes to autonomous SAM
-> F10 marker removed (same as full kill)
Power Sources¶
Power is per-node, not per-sector. Every SAM is self-powered by default. To add a power dependency:
PWR-SA5-SOUTH-1 -> auto-links to SAM-SA5-SOUTH-1
PWR-SA2-NORTH-1 -> auto-links to SAM-SA2-NORTH-1
PWR-EW-NORTH -> auto-links to EW-NORTH
Kill the power group, that specific node goes permanently DARK. For EW radars, the power kill also silences the radar in DCS so it drops off RWR.
Autonomous SAMs¶
SAMs in a sector with no EW and no other infrastructure start EMCON cycling from mission start. No special configuration needed -- just name your SAM and it handles itself.
ECM Jamming¶
ECM aircraft (ECM- prefix on opposing coalition) trigger jammed EMCON cycling on emitting SAMs within their effect range. The jammer is reactive — it only affects nodes that are actively radiating.
Jammed EMCON Flow¶
SAM emitting (ALERT, EMCON_OFF, or EMCON_ENGAGED)
-> Jammer in range detects emission (1-3s delay)
-> Crew recognizes jamming -> shuts down (DARK)
-> Enters jammed EMCON cycling:
-> Off-phase (45-120s): hiding from jammer
-> On-phase (8-15s): brief radar peek
-> During jam detection window (1-3s): targets inside burn-through range engageable
-> Burn-through contact found -> SAM stays ALERT (monitored every 5s)
-> Contacts leave burn-through range -> back to off-phase
-> No burn-through -> jammer catches emission -> crew goes dark again
-> Jammer leaves/dies -> next on-cycle, no jam detected -> exit jammed EMCON
-> Poll restores normal state (EW alive -> ALERT, EW dead -> normal EMCON)
Burn-Through¶
Jamming isn't absolute. During the 1-3s detection delay at the start of each on-cycle, the SAM's radar is briefly unjammed. Any target inside burn-through range during that window produces a strong enough return to track — not just the jammer, any contact.
Once a burn-through contact is detected, the SAM stays ALERT and continues engaging as long as targets remain inside burn-through range. A monitoring loop checks every 5 seconds. When all burn-through contacts leave (or the jammer dies), the SAM returns to the jammed EMCON off-phase.
Priority¶
1. HARM cooldown (harmCooldownUntil) — highest, never overridden
2. Jammed EMCON (jammedEmconActive) — jammer in range
3. Normal state machine — poll evaluation
Jammed EMCON uses its own generation counter (jammedEmconGen), independent of normal EMCON (emconGen). _StopEMCON() is called before starting jammed EMCON to prevent timer conflicts.
Performance¶
| Scenario | Computation |
|---|---|
| Idle (no contacts) | O(EW_count) -- typically 4-6 radars |
| Active contacts | O(EW_count) + O(contacts x SAMs_in_sector) for range checks |
| EMCON cycling | 2 getDetectedTargets() calls per SAM per cycle (staggered) |
| HARM detection | Event-driven only (S_EVENT_SHOT), zero polling cost |
| PB HARM trajectory | One-shot: 1 weapon poll + 1 ray per SAM site, fires once per PB HARM |
| Quiet sectors | Zero -- skipped entirely |
Range checks use squared distance (no sqrt). All values precomputed. Nothing runs every frame.
Round-Robin Polling (v0.8.0+)¶
AEGIS spreads EW polling across time for high-density missions (70+ aircraft). Instead of processing all sectors in one burst every ewPollInterval, it processes one sector per sub-cycle:
Pre-0.8: |--- all 7 sectors ---| |--- all 7 sectors ---|
t=0 t=10 t=20
v0.8.0+: |N| |S| |E| |W| |C| |NW| |SE| |N| |S| |E| ...
t=0 1.4 2.8 4.3 5.7 7.1 8.6 10.0 11.4 12.8
Each sector is still polled once per ewPollInterval (same frequency), but the DCS API calls are spread evenly. With debug = true, per-sector timing appears in the log: PERF: NORTH poll 2.3ms (12 contacts).