mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Add basic implementation for APP-6 SIDCs.
These will be used in conjunction with https://github.com/spatialillusions/milsymbol to simplify the specification of unit type to the front-end.
This commit is contained in:
parent
05f3f3636b
commit
41158543cf
276
game/sidc.py
Normal file
276
game/sidc.py
Normal file
@ -0,0 +1,276 @@
|
||||
"""Implements Symbol Identification Codes (SIDCs) as defined by NATO APP-6(D).
|
||||
|
||||
This implementation only covers assembly of the identifier strings. The front-end is
|
||||
responsible for drawing the icons.
|
||||
|
||||
The third ten digits (used for national modifications and additions not covered by
|
||||
APP-6) are not implemented. The third set of ten digits are optional and will be omitted
|
||||
from the output.
|
||||
|
||||
https://nso.nato.int/nso/nsdd/main/standards/ap-details/1912/EN
|
||||
https://www.spatialillusions.com/milsymbol/docs/milsymbol-2525d.html
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import IntEnum, unique
|
||||
|
||||
# Version field defined by A.5.
|
||||
VERSION = 10
|
||||
|
||||
|
||||
@unique
|
||||
class Context(IntEnum):
|
||||
"""Context field defined by A.6.."""
|
||||
|
||||
REALITY = 0
|
||||
EXERCISE = 1
|
||||
SIMULATION = 2
|
||||
# 3-9 are reserved for future use.
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
||||
|
||||
|
||||
@unique
|
||||
class StandardIdentity(IntEnum):
|
||||
"""Standard identity field defined by A.6."""
|
||||
|
||||
PENDING = 0
|
||||
UNKNOWN = 1
|
||||
ASSUMED_FRIEND = 2
|
||||
FRIEND = 3
|
||||
NEUTRAL = 4
|
||||
SUSPECT_JOKER = 5
|
||||
HOSTILE_FAKER = 6
|
||||
# 7-9 are reserved for future use.
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
||||
|
||||
|
||||
@unique
|
||||
class SymbolSet(IntEnum):
|
||||
"""Symbol set field defined by A.7."""
|
||||
|
||||
UNKNOWN = 0
|
||||
AIR = 1
|
||||
AIR_MISSILE = 2
|
||||
SPACE = 5
|
||||
SPACE_MISSILE = 6
|
||||
LAND_UNIT = 10
|
||||
LAND_CIVILIAN_UNIT_ORGANIZATION = 11
|
||||
LAND_EQUIPMENT = 15
|
||||
LAND_INSTALLATIONS = 20
|
||||
CONTROL_MEASURE = 25
|
||||
DISMOUNTED_INDIVIDUAL = 27
|
||||
SEA_SURFACE = 30
|
||||
SEA_SUBSURFACE = 35
|
||||
MINE_WARFARE = 36
|
||||
ACTIVITY_EVENT = 40
|
||||
ATMOSPHERIC = 45
|
||||
OCEANOGRAPHIC = 46
|
||||
METEOROLOGICAL_SPACE = 47
|
||||
SIGNALS_INTELLIGENCE_SPACE = 50
|
||||
SIGNALS_INTELLIGENCE_AIR = 51
|
||||
SIGNALS_INTELLIGENCE_LAND = 52
|
||||
SIGNALS_INTELLIGENCE_SURFACE = 53
|
||||
SIGNALS_INTELLIGENCE_SUBSURFACE = 54
|
||||
CYBERSPACE_SPACE = 60
|
||||
CYBERSPACE_AIR = 61
|
||||
CYBERSPACE_LAND = 62
|
||||
CYBERSPACE_SURFACE = 63
|
||||
CYBERSPACE_SUBSURFACE = 64
|
||||
VERSION_EXTENSION_FLAG = 99
|
||||
# All other values reserved for future use.
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.value:02}"
|
||||
|
||||
|
||||
@unique
|
||||
class Status(IntEnum):
|
||||
"""Status field defined by A.8 Status."""
|
||||
|
||||
PRESENT = 0
|
||||
PLANNED_ANTICIPATED_SUSPECT = 1
|
||||
PRESENT_FULLY_CAPABLE = 2
|
||||
PRESENT_DAMAGED = 3
|
||||
PRESENT_DESTROYED = 4
|
||||
PRESENT_FULL_TO_CAPACITY = 5
|
||||
# 6-8 reserved for future use.
|
||||
VERSION_EXTENSION_FLAG = 9
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
||||
|
||||
|
||||
@unique
|
||||
class HeadquartersTaskForceDummy(IntEnum):
|
||||
"""Headquarters/Task Force/Dummy field defined by A.9."""
|
||||
|
||||
NOT_APPLICABLE = 0
|
||||
FEINT_DUMMY = 1
|
||||
HEADQUARTERS = 2
|
||||
FEINT_DUMMY_HEADQUARTERS = 3
|
||||
TASK_FORCE = 4
|
||||
FEINT_DUMMY_TASK_FORCE = 5
|
||||
TASK_FORCE_HEADQUARTERS = 6
|
||||
FEINT_DUMMY_TASK_FORCE_HEADQUARTERS = 7
|
||||
# 8 reserved for future use.
|
||||
VERSION_EXTENSION_FLAG = 9
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
||||
|
||||
|
||||
@unique
|
||||
class Amplifier(IntEnum):
|
||||
"""Unit Echelon/Equipment Mobility/Naval Towed Array Amplifier defined by A.10"""
|
||||
|
||||
UNKNOWN = 0
|
||||
|
||||
# Echelon at brigade and below
|
||||
TEAM_CREW = 11
|
||||
SQUAD = 12
|
||||
SECTION = 13
|
||||
PLATOON_DETACHMENT = 14
|
||||
COMPANY_BATTERY_TROOP = 15
|
||||
BATTALION_SQUADRON = 16
|
||||
REGIMENT_GROUP = 17
|
||||
BRIGADE = 18
|
||||
VERSION_EXTENSION_FLAG = 19
|
||||
|
||||
# Echelon at brigade and above
|
||||
DIVISION = 21
|
||||
CORP_MARINE_EXPEDITIONARY_FORCE = 22
|
||||
ARMY = 23
|
||||
ARMY_GROUP_FRONT = 24
|
||||
REGION_THEATRE = 25
|
||||
COMMAND = 26
|
||||
# 27-28 reserved for future use.
|
||||
VERSION_EXTENSION_FLAG2 = 29
|
||||
|
||||
# Equipment mobility on land
|
||||
WHEELED_LIMITED_CROSS_COUNTRY = 31
|
||||
WHEELED_CROSS_COUNTRY = 32
|
||||
TRACKED = 33
|
||||
WHEELED_AND_TRAKCED_COMBINATION = 34
|
||||
TOWED = 35
|
||||
RAIL = 36
|
||||
PACK_ANIMALS = 37
|
||||
# 38 reserved for future use.
|
||||
VERSION_EXTENSION_FLAG3 = 39
|
||||
|
||||
# Equipment mobility on snow
|
||||
OVER_SNOW = 41
|
||||
SLED = 42
|
||||
# 3-8 reserved for future use.
|
||||
VERSION_EXTENSION_FLAG4 = 49
|
||||
|
||||
# Equipment mobility on water
|
||||
BARGE = 51
|
||||
AMPHIBIOUS = 52
|
||||
# 3-8 reserved for future use.
|
||||
VERSION_EXTENSION_FLAG5 = 59
|
||||
|
||||
# Naval towed array
|
||||
SHORT_TOWED_ARRAY = 61
|
||||
LONG_TOWED_ARRAY = 62
|
||||
# 3-8 reserved for future use.
|
||||
VERSION_EXTENSION_FLAG6 = 69
|
||||
|
||||
# Leadership indicator
|
||||
LEADER_INDIVIDUAL = 71
|
||||
DEPUTY_INDIVIDUAL = 72
|
||||
# 3-8 reserved for future use.
|
||||
VERSION_EXTENSION_FLAG7 = 79
|
||||
|
||||
# 80-89 reserved for future use.
|
||||
# 90-99 version extension flag.
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.value:02}"
|
||||
|
||||
|
||||
class Entity(IntEnum):
|
||||
def __str__(self) -> str:
|
||||
return f"{self.value:06}"
|
||||
|
||||
|
||||
# Entity types (the second set of ten digits are implemented as-needed. These are
|
||||
# defined by section A.13. Entity/Entity Type/Entity Subtype and Sector 1 and Sector 2
|
||||
# Modifiers. The specific entity enum used by the SIDC depends on the symbol set used.
|
||||
@unique
|
||||
class AirEntity(Entity):
|
||||
"""Air Entity/Entity Type/Entity Subtype defined by table A-10."""
|
||||
|
||||
UNSPECIFIED = 0
|
||||
|
||||
|
||||
@unique
|
||||
class LandInstallationEntity(Entity):
|
||||
"""Land Installation Entity/Entity Type/Entity Subtype defined by table A-27."""
|
||||
|
||||
UNSPECIFIED = 0
|
||||
|
||||
MILITARY_BASE = 120802
|
||||
AIPORT_AIR_BASE = 121301
|
||||
|
||||
|
||||
@unique
|
||||
class SeaSurfaceEntity(Entity):
|
||||
"""Sea Surface Entity/Entity Type/Entity Subtype defined by table A-34."""
|
||||
|
||||
UNSPECIFIED = 0
|
||||
|
||||
CARRIER = 120100
|
||||
AMPHIBIOUS_ASSAULT_SHIP_GENERAL = 120303
|
||||
|
||||
|
||||
@unique
|
||||
class UnknownEntity(Entity):
|
||||
"""Fallback entity type used when the symbol set is not known."""
|
||||
|
||||
UNSPECIFIED = 0
|
||||
|
||||
|
||||
class Modifier(IntEnum):
|
||||
"""Fallback modifier used when the symbol set is not known."""
|
||||
|
||||
UNSPECIFIED = 0
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.value:02}"
|
||||
|
||||
|
||||
@dataclass
|
||||
class SymbolIdentificationCode:
|
||||
version = VERSION
|
||||
context: Context = Context.REALITY
|
||||
standard_identity: StandardIdentity = StandardIdentity.UNKNOWN
|
||||
symbol_set: SymbolSet = SymbolSet.UNKNOWN
|
||||
status: Status = Status.PRESENT
|
||||
headquarters_task_force_dummy: HeadquartersTaskForceDummy = (
|
||||
HeadquartersTaskForceDummy.NOT_APPLICABLE
|
||||
)
|
||||
amplifier: Amplifier = Amplifier.UNKNOWN
|
||||
entity: Entity = UnknownEntity.UNSPECIFIED
|
||||
sector_one_modifier = Modifier.UNSPECIFIED
|
||||
sector_two_modifier = Modifier.UNSPECIFIED
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "".join(
|
||||
[
|
||||
f"{self.version:02}",
|
||||
str(self.context),
|
||||
str(self.standard_identity),
|
||||
str(self.symbol_set),
|
||||
str(self.status),
|
||||
str(self.headquarters_task_force_dummy),
|
||||
str(self.amplifier),
|
||||
str(self.entity),
|
||||
str(self.sector_one_modifier),
|
||||
str(self.sector_two_modifier),
|
||||
]
|
||||
)
|
||||
17
tests/test_sidc.py
Normal file
17
tests/test_sidc.py
Normal file
@ -0,0 +1,17 @@
|
||||
from game.sidc import (
|
||||
LandInstallationEntity,
|
||||
StandardIdentity,
|
||||
Status,
|
||||
SymbolIdentificationCode,
|
||||
SymbolSet,
|
||||
)
|
||||
|
||||
|
||||
def test_sidc() -> None:
|
||||
sidc = SymbolIdentificationCode(
|
||||
standard_identity=StandardIdentity.FRIEND,
|
||||
symbol_set=SymbolSet.LAND_INSTALLATIONS,
|
||||
status=Status.PRESENT_DAMAGED,
|
||||
entity=LandInstallationEntity.AIPORT_AIR_BASE,
|
||||
)
|
||||
assert str(sidc) == "10032030001213010000"
|
||||
Loading…
x
Reference in New Issue
Block a user