Spaces:
Runtime error
Runtime error
Commit ·
12bc208
1
Parent(s): cd92e0e
Docker + FastAPI backend + v11 HUD for HF Spaces deploy
Browse files- .dockerignore +33 -0
- .gitattributes +1 -0
- Dockerfile +11 -0
- app/__pycache__/main.cpython-311.pyc +0 -0
- app/__pycache__/main.cpython-313.pyc +0 -0
- app/__pycache__/test_pipeline.cpython-311.pyc +0 -0
- app/__pycache__/test_pipeline.cpython-313.pyc +0 -0
- app/main.py +160 -0
- app/test_pipeline.py +94 -0
- data/aircraft_metadata.csv +103 -0
- frontend/atas_hud_v11.html +776 -0
- release_models/aircraft_classifier/atas_final_fine_tuned_aircraft_classifier_model.keras +3 -0
- release_models/eta/atas_final_eta_regressor_model.joblib +3 -0
- release_models/hit/atas_final_hit_classifier_model.joblib +3 -0
- requirements.txt +13 -0
- src/__pycache__/classifier.cpython-311.pyc +0 -0
- src/__pycache__/classifier.cpython-313.pyc +0 -0
- src/__pycache__/decision.cpython-311.pyc +0 -0
- src/__pycache__/metadata.cpython-311.pyc +0 -0
- src/__pycache__/models.cpython-311.pyc +0 -0
- src/__pycache__/physics_generator.cpython-311.pyc +0 -0
- src/__pycache__/schemas.cpython-311.pyc +0 -0
- src/__pycache__/schemas.cpython-313.pyc +0 -0
- src/analysis_for_physics_generator.ipynb +2770 -0
- src/classifier.py +190 -0
- src/decision.py +118 -0
- src/metadata.py +40 -0
- src/models.py +71 -0
- src/physics_generator.py +461 -0
- src/schemas.py +49 -0
.dockerignore
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.git/
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.pyc
|
| 4 |
+
.ipynb_checkpoints/
|
| 5 |
+
notebooks/
|
| 6 |
+
|
| 7 |
+
# Project notes and personal vaults
|
| 8 |
+
ATAS_Notes/
|
| 9 |
+
|
| 10 |
+
# Local python environment
|
| 11 |
+
env/
|
| 12 |
+
|
| 13 |
+
# Large data folder (keep metadata CSV tracked)
|
| 14 |
+
data/
|
| 15 |
+
!data/aircraft_metadata.csv
|
| 16 |
+
|
| 17 |
+
# Logs, artifacts and model binaries
|
| 18 |
+
logs/
|
| 19 |
+
models/
|
| 20 |
+
release_models/
|
| 21 |
+
!release_models/aircraft_classifier/
|
| 22 |
+
!release_models/aircraft_classifier/atas_final_fine_tuned_aircraft_classifier_model.keras
|
| 23 |
+
!release_models/eta/
|
| 24 |
+
!release_models/eta/atas_final_eta_regressor_model.joblib
|
| 25 |
+
!release_models/hit/
|
| 26 |
+
!release_models/hit/atas_final_hit_classifier_model.joblib
|
| 27 |
+
|
| 28 |
+
# Prediction outputs and training history
|
| 29 |
+
predictions/
|
| 30 |
+
model_history/
|
| 31 |
+
|
| 32 |
+
# VS Code workspace config
|
| 33 |
+
.vscode/
|
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
*.keras filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
COPY . .
|
| 6 |
+
|
| 7 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 8 |
+
|
| 9 |
+
EXPOSE 7860
|
| 10 |
+
|
| 11 |
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
|
app/__pycache__/main.cpython-311.pyc
ADDED
|
Binary file (4.72 kB). View file
|
|
|
app/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (3.91 kB). View file
|
|
|
app/__pycache__/test_pipeline.cpython-311.pyc
ADDED
|
Binary file (1.99 kB). View file
|
|
|
app/__pycache__/test_pipeline.cpython-313.pyc
ADDED
|
Binary file (1.89 kB). View file
|
|
|
app/main.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
main.py
|
| 3 |
+
-----------------------------------------------------
|
| 4 |
+
# WHAT: FastAPI application entry point for the ATAS backend.
|
| 5 |
+
# Exposes a single endpoint - POST /analyze - that accepts an aircraft
|
| 6 |
+
# image and engagement parameters, runs the full pipeline, and returns
|
| 7 |
+
# a JSON response.
|
| 8 |
+
#
|
| 9 |
+
# WHY: All five src/ modules are isolated units. main.py is the glue layer
|
| 10 |
+
# that wires them together in the correct order and exposes the result
|
| 11 |
+
# to the outside world via HTTP.
|
| 12 |
+
#
|
| 13 |
+
# USE: Called by the HUD frontend via fetch(). Also used directly during
|
| 14 |
+
# testing via curl or a Python test script to verify end-to-end pipeline
|
| 15 |
+
# behavior before any UI work begins.
|
| 16 |
+
|
| 17 |
+
# User controls / HUD inputs: + 1 Image
|
| 18 |
+
# your_speed, your_altitude, enemy_altitude, countermeasure_deployed,
|
| 19 |
+
# launch_distance, remaining_distance, azimuth, elevation
|
| 20 |
+
|
| 21 |
+
# Comes from aircraft_metadata.csv:
|
| 22 |
+
# missile_speed, missile_range, enemy_generation, maneuverability
|
| 23 |
+
|
| 24 |
+
# Derived by build_feature_array() internally:
|
| 25 |
+
# closure_rate, missile_phase
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
# Importing the necessary libraries
|
| 31 |
+
from fastapi import FastAPI
|
| 32 |
+
from fastapi import UploadFile, File, Form
|
| 33 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 34 |
+
import tempfile
|
| 35 |
+
import os
|
| 36 |
+
from src.classifier import predict_aircraft
|
| 37 |
+
from src.metadata import get_aircraft_metadata
|
| 38 |
+
from src.physics_generator import build_feature_array
|
| 39 |
+
from src.models import make_predictions
|
| 40 |
+
from src.decision import get_recommendation
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
# Creating a server
|
| 44 |
+
app = FastAPI()
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
# Allow the browser to send requests to this server
|
| 48 |
+
# Without this, the HUD's fetch() call gets blocked by the browser's security rules
|
| 49 |
+
# CORS (Cross-Origin Resource Sharing) is the mechanism where your server tells the browser: "yes, I allow requests from other origins."
|
| 50 |
+
# allow_origins=["*"] → accept requests from any origin (local file, any URL)
|
| 51 |
+
# allow_methods=["*"] → accept any HTTP method (POST, GET, etc.)
|
| 52 |
+
# allow_headers=["*"] → accept any headers the browser sends with the request
|
| 53 |
+
app.add_middleware(
|
| 54 |
+
CORSMiddleware,
|
| 55 |
+
allow_origins=["*"],
|
| 56 |
+
allow_methods=["*"],
|
| 57 |
+
allow_headers=["*"],
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
# Function to take the inputs
|
| 62 |
+
# image -> as temp file
|
| 63 |
+
# 8 features -> form object
|
| 64 |
+
@app.post("/analyze")
|
| 65 |
+
async def analyze(
|
| 66 |
+
image: UploadFile = File(...), # The ... just means required. No default value.
|
| 67 |
+
your_speed: float = Form(...), # name : type = where_it_comes_from(required_or_default)
|
| 68 |
+
your_altitude: float = Form(...),
|
| 69 |
+
enemy_altitude: float = Form(...),
|
| 70 |
+
countermeasure_deployed: int = Form(...),
|
| 71 |
+
launch_distance: float = Form(...),
|
| 72 |
+
remaining_distance: float = Form(...),
|
| 73 |
+
azimuth: float = Form(...),
|
| 74 |
+
elevation: float = Form(...),
|
| 75 |
+
friendly_aircraft: str = Form(...)
|
| 76 |
+
):
|
| 77 |
+
|
| 78 |
+
# Browser sends image as raw bytes, not a file path
|
| 79 |
+
# predict_aircraft() needs a real file path on disk
|
| 80 |
+
# So we write the bytes to a temp file and grab its path
|
| 81 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as tmp:
|
| 82 |
+
tmp.write(await image.read()) # dump bytes from memory to disk
|
| 83 |
+
tmp_path = tmp.name # grab the path e.g. /tmp/abc123.jpg
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
# 1. Calling the function to predict the aircraft
|
| 87 |
+
aircraft_name = predict_aircraft(image_path=tmp_path)
|
| 88 |
+
|
| 89 |
+
# 2. Get the metadata of predicted aircraft
|
| 90 |
+
metadata_dict = get_aircraft_metadata(aircraft_name=aircraft_name)
|
| 91 |
+
friendly_metadata = get_aircraft_metadata(aircraft_name=friendly_aircraft)
|
| 92 |
+
|
| 93 |
+
# 3. Build the features to feed the models
|
| 94 |
+
feature_dict = build_feature_array(
|
| 95 |
+
# From HUD sliders
|
| 96 |
+
launch_distance=launch_distance,
|
| 97 |
+
remaining_distance=remaining_distance,
|
| 98 |
+
azimuth=azimuth,
|
| 99 |
+
elevation=elevation,
|
| 100 |
+
your_altitude=your_altitude,
|
| 101 |
+
your_speed=your_speed,
|
| 102 |
+
enemy_altitude=enemy_altitude,
|
| 103 |
+
countermeasure_deployed=countermeasure_deployed,
|
| 104 |
+
# From metadata lookup (enemy aircraft)
|
| 105 |
+
missile_speed=metadata_dict['missile_speed'],
|
| 106 |
+
missile_range=metadata_dict['missile_range'],
|
| 107 |
+
enemy_generation=metadata_dict['enemy_generation'],
|
| 108 |
+
# From metadata lookup (friendly aircraft)
|
| 109 |
+
your_maneuverability=friendly_metadata['maneuverability']
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
# 4. Make predictions using models
|
| 113 |
+
predictions_dict = make_predictions(feature_dict=feature_dict)
|
| 114 |
+
|
| 115 |
+
# 5. Providing recommendations to the pilot
|
| 116 |
+
suggestion = get_recommendation(
|
| 117 |
+
eta_seconds=predictions_dict['eta_seconds'],
|
| 118 |
+
hit_probability=predictions_dict['hit_probability'],
|
| 119 |
+
no_aa_capability=metadata_dict['no_aa_capability'],
|
| 120 |
+
enemy_generation=metadata_dict['enemy_generation'],
|
| 121 |
+
friendly_generation=friendly_metadata['enemy_generation']
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
# 6. Delete the temparary file since work is done
|
| 125 |
+
os.remove(tmp_path)
|
| 126 |
+
|
| 127 |
+
# 7. Return the value predicted and derived
|
| 128 |
+
return {
|
| 129 |
+
'aircraft_name' : aircraft_name,
|
| 130 |
+
'missile_speed' : metadata_dict['missile_speed'],
|
| 131 |
+
'missile_range' : metadata_dict['missile_range'],
|
| 132 |
+
'enemy_generation' : metadata_dict['enemy_generation'],
|
| 133 |
+
'maneuverability' : metadata_dict['maneuverability'],
|
| 134 |
+
'no_aa_capability' : metadata_dict['no_aa_capability'],
|
| 135 |
+
'eta_seconds' : predictions_dict['eta_seconds'],
|
| 136 |
+
'hit_probability' : predictions_dict['hit_probability'],
|
| 137 |
+
'recommendation' : suggestion
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
"""
|
| 143 |
+
Request comes in
|
| 144 |
+
↓
|
| 145 |
+
Image saved to temp file
|
| 146 |
+
↓
|
| 147 |
+
predict_aircraft() → aircraft_name
|
| 148 |
+
↓
|
| 149 |
+
get_aircraft_metadata() x 2 → enemy + friendly metadata
|
| 150 |
+
↓
|
| 151 |
+
build_feature_array() → 14 feature dict
|
| 152 |
+
↓
|
| 153 |
+
make_predictions() → eta + hit probability
|
| 154 |
+
↓
|
| 155 |
+
get_recommendation() → tactical suggestion
|
| 156 |
+
↓
|
| 157 |
+
Temp file deleted
|
| 158 |
+
↓
|
| 159 |
+
JSON returned to HUD
|
| 160 |
+
"""
|
app/test_pipeline.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
test_pipeline.py -- End-to-End Pipeline Verification Script
|
| 3 |
+
|
| 4 |
+
WHAT:
|
| 5 |
+
Sends a real POST request to the running FastAPI server at /analyze.
|
| 6 |
+
Uses a real aircraft image and hardcoded engagement parameters.
|
| 7 |
+
|
| 8 |
+
WHY:
|
| 9 |
+
Confirms the full pipeline runs without errors before any UI work begins.
|
| 10 |
+
If this script returns clean JSON with all expected keys, the backend is ready.
|
| 11 |
+
If it fails, we fix the backend here, not inside the HUD.
|
| 12 |
+
|
| 13 |
+
HOW TO USE:
|
| 14 |
+
1. Start the FastAPI server in one terminal:
|
| 15 |
+
uvicorn app.main:app --reload
|
| 16 |
+
|
| 17 |
+
2. Run this script in a second terminal:
|
| 18 |
+
python app/test_pipeline.py
|
| 19 |
+
|
| 20 |
+
EXPECTED OUTPUT:
|
| 21 |
+
A JSON response containing:
|
| 22 |
+
aircraft_name, missile_speed, missile_range, enemy_generation,
|
| 23 |
+
maneuverability, no_aa_capability, eta_seconds, hit_probability, recommendation
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
# Import the necessary libraries
|
| 28 |
+
import requests
|
| 29 |
+
from pathlib import Path
|
| 30 |
+
|
| 31 |
+
# Define image test path
|
| 32 |
+
IMAGE_PATH = str(Path(__file__).parent.parent / 'assets'/ 'testing_images' / 'Su57.jpg')
|
| 33 |
+
print(IMAGE_PATH)
|
| 34 |
+
|
| 35 |
+
# Define Testing parameters
|
| 36 |
+
params = {
|
| 37 |
+
"your_speed": 500.0,
|
| 38 |
+
"your_altitude": 10000.0,
|
| 39 |
+
"enemy_altitude": 8000.0,
|
| 40 |
+
"countermeasure_deployed": 0,
|
| 41 |
+
"launch_distance": 15000.0,
|
| 42 |
+
"remaining_distance": 10000.0,
|
| 43 |
+
"azimuth": 45.0,
|
| 44 |
+
"elevation": 5.0,
|
| 45 |
+
"friendly_aircraft": "Tejas"
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
# Send the data to the server
|
| 50 |
+
# and
|
| 51 |
+
# store what the server sends back
|
| 52 |
+
response = requests.post(
|
| 53 |
+
url='http://localhost:8000/analyze', # Address of the running FastAPI server
|
| 54 |
+
data=params, # Form fields - speed, altitude, etc.
|
| 55 |
+
files={
|
| 56 |
+
"image": ( # "image" matches the field name in main.py
|
| 57 |
+
"Su57.jpg", # Filename the server will see
|
| 58 |
+
open(IMAGE_PATH, "rb"), # Open the image in read-binary mode
|
| 59 |
+
"image/jpeg" # Tell the server what type of file this is
|
| 60 |
+
)
|
| 61 |
+
}
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
print(response.status_code) # should be 200 if everything worked
|
| 65 |
+
print(response.json()) # the actual pipeline output
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
"""
|
| 71 |
+
VERIFIED OUTPUT - June 8, 2026
|
| 72 |
+
End-to-end pipeline confirmed working. Status 200.
|
| 73 |
+
|
| 74 |
+
Test image : assets/Su57.jpg
|
| 75 |
+
Server : uvicorn app.main:app (no --reload)
|
| 76 |
+
Run with : python -m app.test_pipeline
|
| 77 |
+
|
| 78 |
+
Response:
|
| 79 |
+
{
|
| 80 |
+
"aircraft_name" : "Su57",
|
| 81 |
+
"missile_speed" : 2058,
|
| 82 |
+
"missile_range" : 400000,
|
| 83 |
+
"enemy_generation": 5.0,
|
| 84 |
+
"maneuverability" : 2,
|
| 85 |
+
"no_aa_capability": 0,
|
| 86 |
+
"eta_seconds" : 3.77,
|
| 87 |
+
"hit_probability" : 0.969,
|
| 88 |
+
"recommendation" : "BREAK HARD + DEPLOY CM + DISENGAGE IMMEDIATELY"
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
All 6 src/ modules executed without error.
|
| 92 |
+
Aircraft correctly identified from image.
|
| 93 |
+
Recommendation matches expected threat profile for Gen-5 enemy at 10km.
|
| 94 |
+
"""
|
data/aircraft_metadata.csv
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
aircraft,aircraft_max_speed,missile_speed,missile_range,enemy_generation,maneuverability,no_aa_capability
|
| 2 |
+
A10,222,857,35000,4,1,0
|
| 3 |
+
A400M,255,-1,-1,4,0,1
|
| 4 |
+
AG600,155,-1,-1,4,0,1
|
| 5 |
+
AH64,101,750,8000,4,1,0
|
| 6 |
+
AKINCI,100,1372,65000,4,1,0
|
| 7 |
+
AV8B,300,1372,160000,4,1,0
|
| 8 |
+
An124,240,-1,-1,4,0,1
|
| 9 |
+
An22,205,-1,-1,3.5,0,1
|
| 10 |
+
An225,236,-1,-1,4,0,1
|
| 11 |
+
An72,196,-1,-1,4,0,1
|
| 12 |
+
B1,370,-1,-1,4,0,1
|
| 13 |
+
B2,280,-1,-1,4.5,0,1
|
| 14 |
+
B21,277,-1,-1,5,0,1
|
| 15 |
+
B52,290,-1,-1,3.5,0,1
|
| 16 |
+
Be200,194,-1,-1,4,0,1
|
| 17 |
+
C1,224,-1,-1,3.5,0,1
|
| 18 |
+
C130,186,-1,-1,3.5,0,1
|
| 19 |
+
C17,264,-1,-1,4,0,1
|
| 20 |
+
C2,255,-1,-1,4,0,1
|
| 21 |
+
C390,274,-1,-1,4.5,0,1
|
| 22 |
+
C5,237,-1,-1,3.5,0,1
|
| 23 |
+
CH47,87,-1,-1,3.5,0,1
|
| 24 |
+
CH53,87,-1,-1,3.5,0,1
|
| 25 |
+
CL415,100,-1,-1,4,0,1
|
| 26 |
+
E2,180,-1,-1,4,0,1
|
| 27 |
+
E7,265,-1,-1,4.5,0,1
|
| 28 |
+
EF2000,590,1372,200000,4.5,2,0
|
| 29 |
+
EMB314,164,857,35000,4,1,0
|
| 30 |
+
F117,289,-1,-1,4,0,1
|
| 31 |
+
F14,690,1475,190000,4,2,0
|
| 32 |
+
F15,737,1372,160000,4,2,0
|
| 33 |
+
F16,590,1372,160000,4,2,0
|
| 34 |
+
F18,532,1372,160000,4.5,2,0
|
| 35 |
+
F2,590,1544,105000,4.5,2,0
|
| 36 |
+
F22,669,1372,160000,5,2,0
|
| 37 |
+
F35,544,1372,160000,5,2,0
|
| 38 |
+
F4,658,1372,50000,3.5,1,0
|
| 39 |
+
FCK1,532,2058,100000,4,2,0
|
| 40 |
+
H6,292,-1,-1,3.5,0,1
|
| 41 |
+
Il76,250,-1,-1,3.5,0,1
|
| 42 |
+
J10,650,1715,300000,4.5,2,0
|
| 43 |
+
J20,590,1715,300000,5,2,0
|
| 44 |
+
J35,590,1715,300000,5,2,0
|
| 45 |
+
J36,590,1715,400000,5,2,0
|
| 46 |
+
J50,590,1715,400000,5,2,0
|
| 47 |
+
JAS39,590,1372,200000,4.5,2,0
|
| 48 |
+
JF17,544,1372,100000,4.5,2,0
|
| 49 |
+
JH7,502,1372,100000,4,1,0
|
| 50 |
+
KAAN,532,1372,65000,5,2,0
|
| 51 |
+
KC135,259,-1,-1,3.5,0,1
|
| 52 |
+
KF21,532,1372,200000,4.5,2,0
|
| 53 |
+
KIZILELMA,305,1372,65000,4.5,2,0
|
| 54 |
+
KJ600,166,-1,-1,4.5,0,1
|
| 55 |
+
Ka27,75,-1,-1,4,1,1
|
| 56 |
+
Ka52,87,857,40000,4,1,0
|
| 57 |
+
MQ20,205,-1,-1,4.5,1,1
|
| 58 |
+
MQ25,172,-1,-1,5,0,1
|
| 59 |
+
MQ28,277,1372,160000,5,1,0
|
| 60 |
+
MQ9,134,857,35000,4,1,0
|
| 61 |
+
Mi24,93,847,8000,3.5,1,0
|
| 62 |
+
Mi26,82,-1,-1,4,0,1
|
| 63 |
+
Mi28,89,857,40000,4,1,0
|
| 64 |
+
Mi8,78,-1,-1,3.5,0,1
|
| 65 |
+
Mig29,667,1372,110000,4,2,0
|
| 66 |
+
Mig31,833,2058,400000,4,1,0
|
| 67 |
+
Mirage2000,649,1372,80000,4,2,0
|
| 68 |
+
NH90,83,-1,-1,4,1,1
|
| 69 |
+
P3,208,-1,-1,3.5,0,1
|
| 70 |
+
RQ4,174,-1,-1,4,0,1
|
| 71 |
+
Rafale,531,1372,200000,4.5,2,0
|
| 72 |
+
SR71,983,-1,-1,3.5,0,1
|
| 73 |
+
Su24,444,847,8000,3.5,1,0
|
| 74 |
+
Su25,271,857,40000,3.5,1,0
|
| 75 |
+
Su34,527,1372,110000,4,1,0
|
| 76 |
+
Su47,611,1372,110000,4.5,2,0
|
| 77 |
+
Su57,590,2058,400000,5,2,0
|
| 78 |
+
T50,510,857,18000,4,1,0
|
| 79 |
+
TB001,78,-1,-1,4,1,1
|
| 80 |
+
TB2,61,-1,-1,4,1,1
|
| 81 |
+
Tejas,550,1544,110000,4.5,2,0
|
| 82 |
+
Tornado,667,1372,160000,4,1,0
|
| 83 |
+
Tu160,616,-1,-1,4,0,1
|
| 84 |
+
Tu22M,638,-1,-1,3.5,0,1
|
| 85 |
+
Tu95,255,-1,-1,3.5,0,1
|
| 86 |
+
U2,224,-1,-1,3.5,0,1
|
| 87 |
+
UH60,99,-1,-1,4,1,1
|
| 88 |
+
US2,155,-1,-1,4,0,1
|
| 89 |
+
V22,157,-1,-1,4,0,1
|
| 90 |
+
V280,154,-1,-1,5,1,1
|
| 91 |
+
Vulcan,288,-1,-1,3.5,0,1
|
| 92 |
+
WZ10,83,686,8000,4,1,0
|
| 93 |
+
WZ7,208,-1,-1,4,0,1
|
| 94 |
+
WZ9,85,686,8000,4,1,0
|
| 95 |
+
X29,536,857,35000,4,2,0
|
| 96 |
+
X32,536,-1,-1,5,2,1
|
| 97 |
+
XB70,919,-1,-1,3.5,0,1
|
| 98 |
+
XQ58,292,1372,160000,5,1,0
|
| 99 |
+
Y20,255,-1,-1,4,0,1
|
| 100 |
+
YF23,648,1372,160000,5,2,0
|
| 101 |
+
Z10,83,686,8000,4,1,0
|
| 102 |
+
Z19,78,686,8000,4,1,0
|
| 103 |
+
Z21,83,686,8000,4,1,0
|
frontend/atas_hud_v11.html
ADDED
|
@@ -0,0 +1,776 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>ATAS v2.5 Tactical MFD</title>
|
| 7 |
+
<style>
|
| 8 |
+
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600;700&display=swap');
|
| 9 |
+
|
| 10 |
+
*, *::before, *::after { box-sizing: border-box; }
|
| 11 |
+
body { background: #000; margin: 0; padding: 20px; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; font-family: 'IBM Plex Mono', monospace; }
|
| 12 |
+
|
| 13 |
+
/* ── Animations ── */
|
| 14 |
+
.hb { animation: hblink 1.6s ease-in-out infinite; }
|
| 15 |
+
@keyframes hblink { 0%,100%{opacity:1} 50%{opacity:.3} }
|
| 16 |
+
@keyframes scan { 0%{top:0} 100%{top:100%} }
|
| 17 |
+
@keyframes spin { 100%{transform:rotate(360deg)} }
|
| 18 |
+
@keyframes critPulse { 0%,100%{box-shadow:0 0 0 0 rgba(232,68,68,0)} 50%{box-shadow:0 0 18px 6px rgba(232,68,68,.25)} }
|
| 19 |
+
|
| 20 |
+
.scan-line {
|
| 21 |
+
width:100%; height:2px; background:rgba(0,212,176,.9); position:absolute;
|
| 22 |
+
box-shadow:0 0 14px #00d4b0; animation:scan 1.4s linear infinite; z-index:5; display:none;
|
| 23 |
+
}
|
| 24 |
+
.rotor-spin { transform-origin:60px 45px; animation:spin .15s linear infinite; }
|
| 25 |
+
.threat-critical { animation:critPulse 1s ease-in-out infinite; }
|
| 26 |
+
|
| 27 |
+
/* ── Reusable classes ── */
|
| 28 |
+
.bc { position:absolute; width:15px; height:15px; }
|
| 29 |
+
.bc-tl { top:-1px; left:-1px; border-top:2px solid #00d4b0; border-left:2px solid #00d4b0; }
|
| 30 |
+
.bc-tr { top:-1px; right:-1px; border-top:2px solid #00d4b0; border-right:2px solid #00d4b0; }
|
| 31 |
+
.bc-bl { bottom:-1px; left:-1px; border-bottom:2px solid #00d4b0; border-left:2px solid #00d4b0; }
|
| 32 |
+
.bc-br { bottom:-1px; right:-1px; border-bottom:2px solid #00d4b0; border-right:2px solid #00d4b0; }
|
| 33 |
+
|
| 34 |
+
.panel {
|
| 35 |
+
border:1px solid rgba(0,212,176,.2); background:rgba(0,212,176,.025);
|
| 36 |
+
padding:11px; position:relative;
|
| 37 |
+
}
|
| 38 |
+
.panel-label {
|
| 39 |
+
font-size:9px; color:rgba(0,212,176,.4); letter-spacing:.2em;
|
| 40 |
+
margin-bottom:8px; text-transform:uppercase;
|
| 41 |
+
}
|
| 42 |
+
.data-row {
|
| 43 |
+
display:flex; justify-content:space-between; align-items:center;
|
| 44 |
+
border-bottom:1px solid rgba(0,212,176,.08); padding-bottom:4px; margin-bottom:4px;
|
| 45 |
+
font-size:9px;
|
| 46 |
+
}
|
| 47 |
+
.data-row:last-child { border-bottom:none; margin-bottom:0; }
|
| 48 |
+
.data-key { color:rgba(0,212,176,.45); letter-spacing:.05em; }
|
| 49 |
+
.data-val { color:#00d4b0; font-size:10px; font-weight:600; }
|
| 50 |
+
|
| 51 |
+
.tac-slider {
|
| 52 |
+
-webkit-appearance:none; width:100%; height:2px;
|
| 53 |
+
background:rgba(0,212,176,.2); outline:none; margin:8px 0; display:block;
|
| 54 |
+
}
|
| 55 |
+
.tac-slider::-webkit-slider-thumb {
|
| 56 |
+
-webkit-appearance:none; width:8px; height:14px; background:#00d4b0;
|
| 57 |
+
cursor:pointer; border-radius:1px; box-shadow:0 0 5px rgba(0,212,176,.8);
|
| 58 |
+
}
|
| 59 |
+
.tac-select {
|
| 60 |
+
background:rgba(0,212,176,.05); border:1px solid rgba(0,212,176,.3);
|
| 61 |
+
color:#00d4b0; font-family:'IBM Plex Mono',monospace; font-size:10px;
|
| 62 |
+
padding:4px; outline:none; width:100%; cursor:pointer;
|
| 63 |
+
}
|
| 64 |
+
.tac-select option { background:#06070b; color:#00d4b0; }
|
| 65 |
+
|
| 66 |
+
.tactical-feed {
|
| 67 |
+
width:100%; height:100%; object-fit:cover;
|
| 68 |
+
filter:grayscale(100%) sepia(100%) hue-rotate(130deg) saturate(300%) contrast(1.2);
|
| 69 |
+
opacity:.7; display:none; position:absolute; top:0; left:0; z-index:1;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
/* Countermeasure toggle and Action Buttons */
|
| 73 |
+
.cm-btn {
|
| 74 |
+
cursor:pointer; border:1px solid rgba(0,212,176,.3);
|
| 75 |
+
background:rgba(0,212,176,.04); color:rgba(0,212,176,.6);
|
| 76 |
+
font-family:'IBM Plex Mono',monospace; font-size:9px; letter-spacing:.1em;
|
| 77 |
+
padding:8px 8px; width:100%; text-align:center; transition:all .2s;
|
| 78 |
+
}
|
| 79 |
+
.cm-btn:hover { background:rgba(0,212,176,.1); border-color:#00d4b0; color:#00d4b0; }
|
| 80 |
+
.cm-btn.deployed { border-color:#f0a030; background:rgba(240,160,48,.1); color:#f0a030; }
|
| 81 |
+
.cm-btn.deployed:hover { background:rgba(240,160,48,.2); }
|
| 82 |
+
|
| 83 |
+
.clear-btn {
|
| 84 |
+
margin-top:6px; border:1px solid rgba(0,212,176,.15); padding:4px 8px;
|
| 85 |
+
text-align:center; font-size:8px; color:rgba(0,212,176,.4); letter-spacing:.1em;
|
| 86 |
+
cursor:pointer; display:none; transition:all .2s;
|
| 87 |
+
}
|
| 88 |
+
.clear-btn:hover { border-color:rgba(0,212,176,.4); color:rgba(0,212,176,.7); }
|
| 89 |
+
</style>
|
| 90 |
+
</head>
|
| 91 |
+
<body>
|
| 92 |
+
|
| 93 |
+
<div style="background:#020203;padding:10px;border-radius:12px;box-shadow:inset 0 0 40px rgba(0,0,0,.8);width:880px;">
|
| 94 |
+
<div id="main-hud" style="position:relative;background:#06070b;border:1px solid rgba(0,212,176,.1);padding:20px;">
|
| 95 |
+
|
| 96 |
+
<div class="bc" style="top:5px;left:5px;border-top:3px solid #00d4b0;border-left:3px solid #00d4b0;"></div>
|
| 97 |
+
<div class="bc" style="top:5px;right:5px;border-top:3px solid #00d4b0;border-right:3px solid #00d4b0;"></div>
|
| 98 |
+
<div class="bc" style="bottom:5px;left:5px;border-bottom:3px solid #00d4b0;border-left:3px solid #00d4b0;"></div>
|
| 99 |
+
<div class="bc" style="bottom:5px;right:5px;border-bottom:3px solid #00d4b0;border-right:3px solid #00d4b0;"></div>
|
| 100 |
+
|
| 101 |
+
<div style="display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid rgba(0,212,176,.12);padding-bottom:12px;margin-bottom:14px;">
|
| 102 |
+
<div style="display:flex;align-items:baseline;gap:8px;">
|
| 103 |
+
<span style="font-size:18px;font-weight:700;letter-spacing:.3em;color:#00d4b0;">ATAS</span>
|
| 104 |
+
<span style="font-size:9px;color:rgba(0,212,176,.35);letter-spacing:.12em;">v2.5 // TACTICAL MFD PIPELINE</span>
|
| 105 |
+
</div>
|
| 106 |
+
<span id="header-alert" style="font-size:10px;color:rgba(0,212,176,.3);letter-spacing:.2em;font-weight:700;">SCANNING AIRSPACE</span>
|
| 107 |
+
<div style="font-size:9px;display:flex;gap:10px;">
|
| 108 |
+
<span style="color:#2dd4a0;">● UPLINK SECURE</span>
|
| 109 |
+
<span style="color:rgba(0,212,176,.4);">SYS.TIME: <span id="sys-time">00:00:00 IST</span></span>
|
| 110 |
+
</div>
|
| 111 |
+
</div>
|
| 112 |
+
|
| 113 |
+
<div style="display:grid;grid-template-columns:1fr 1.55fr 1fr;gap:14px;margin-bottom:12px;">
|
| 114 |
+
|
| 115 |
+
<div style="display:flex;flex-direction:column;gap:12px;">
|
| 116 |
+
|
| 117 |
+
<div class="panel">
|
| 118 |
+
<div class="bc bc-tl"></div><div class="bc bc-tr"></div>
|
| 119 |
+
<div class="bc bc-bl"></div><div class="bc bc-br"></div>
|
| 120 |
+
<div class="panel-label" style="display:flex;justify-content:space-between;">
|
| 121 |
+
<span>LIVE CAMERA FEED</span>
|
| 122 |
+
<span id="sensor-status" style="color:rgba(0,212,176,.3);">STANDBY</span>
|
| 123 |
+
</div>
|
| 124 |
+
<div id="sensor-feed"
|
| 125 |
+
style="border:1px dashed rgba(0,212,176,.28);background:rgba(0,0,0,.6);height:108px;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;cursor:pointer;"
|
| 126 |
+
onclick="document.getElementById('file-upload').click()">
|
| 127 |
+
<input type="file" id="file-upload" accept="image/*" style="display:none;" onchange="handleImageUpload(event)">
|
| 128 |
+
<div class="scan-line" id="scan-line"></div>
|
| 129 |
+
<img id="sensor-img" class="tactical-feed" alt="sensor feed">
|
| 130 |
+
<div id="sensor-text" style="font-size:9px;color:rgba(0,212,176,.38);letter-spacing:.1em;text-align:center;z-index:2;position:relative;">
|
| 131 |
+
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-bottom:5px;opacity:.55;display:block;margin-left:auto;margin-right:auto;">
|
| 132 |
+
<rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/>
|
| 133 |
+
</svg>
|
| 134 |
+
UPLOAD TARGET PHOTO
|
| 135 |
+
</div>
|
| 136 |
+
<div style="position:absolute;top:50%;left:50%;width:22px;height:1px;background:rgba(0,212,176,.25);transform:translate(-50%,-50%);z-index:3;"></div>
|
| 137 |
+
<div style="position:absolute;top:50%;left:50%;width:1px;height:22px;background:rgba(0,212,176,.25);transform:translate(-50%,-50%);z-index:3;"></div>
|
| 138 |
+
</div>
|
| 139 |
+
<div id="clear-btn" class="clear-btn" onclick="clearTarget()">↺ CLEAR TARGET LOCK</div>
|
| 140 |
+
</div>
|
| 141 |
+
|
| 142 |
+
<div class="panel" style="border-color:rgba(0,212,176,.28);background:rgba(0,212,176,.04);">
|
| 143 |
+
<div class="bc bc-tl"></div><div class="bc bc-tr"></div>
|
| 144 |
+
<div class="bc bc-bl"></div><div class="bc bc-br"></div>
|
| 145 |
+
<div style="font-size:9px;color:#00d4b0;letter-spacing:.15em;font-weight:700;margin-bottom:10px;">FRIENDLY PLATFORM</div>
|
| 146 |
+
<div style="display:flex;gap:10px;align-items:center;margin-bottom:10px;">
|
| 147 |
+
<svg width="36" height="36" viewBox="0 0 100 100" style="opacity:.8;flex-shrink:0;">
|
| 148 |
+
<polygon points="50,10 55,30 90,65 90,75 60,70 60,90 70,100 30,100 40,90 40,70 10,75 10,65 45,30" fill="none" stroke="#00d4b0" stroke-width="2"/>
|
| 149 |
+
<line x1="50" y1="10" x2="50" y2="90" stroke="rgba(0,212,176,.3)" stroke-width="1"/>
|
| 150 |
+
</svg>
|
| 151 |
+
<select id="own-platform-select" class="tac-select" style="flex:1;" onchange="updateOwnPlatform()"></select>
|
| 152 |
+
</div>
|
| 153 |
+
<div style="font-size:9px;color:rgba(0,212,176,.6);">
|
| 154 |
+
<div style="display:flex;justify-content:space-between;margin-bottom:2px;">
|
| 155 |
+
<span>AIRSPEED (MACH)</span>
|
| 156 |
+
<span>MAX <span id="max-spd-label">1.8</span> | <span id="val-spd" style="color:#00d4b0;font-weight:700;">1.20</span></span>
|
| 157 |
+
</div>
|
| 158 |
+
<input type="range" class="tac-slider" id="slide-spd" min="0.5" max="1.8" step="0.01" value="1.20" oninput="updateOwnSliders()">
|
| 159 |
+
<div style="display:flex;justify-content:space-between;margin-bottom:2px;">
|
| 160 |
+
<span>ALTITUDE (FT)</span>
|
| 161 |
+
<span>Max <span id="max-alt-label">60k</span> | <span id="val-alt" style="color:#00d4b0;font-weight:700;">32,000</span></span>
|
| 162 |
+
</div>
|
| 163 |
+
<input type="range" class="tac-slider" id="slide-alt" min="5000" max="60000" step="500" value="32000" oninput="updateOwnSliders()">
|
| 164 |
+
</div>
|
| 165 |
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:5px;font-size:8px;margin-top:4px;">
|
| 166 |
+
<div style="background:rgba(0,0,0,.3);padding:4px;border-left:1px solid rgba(0,212,176,.35);">
|
| 167 |
+
<div style="color:rgba(0,212,176,.4);">GENERATION</div>
|
| 168 |
+
<div id="own-gen-label" style="color:#2dd4a0;">4.5</div>
|
| 169 |
+
</div>
|
| 170 |
+
<div style="background:rgba(0,0,0,.3);padding:4px;border-left:1px solid rgba(0,212,176,.35);">
|
| 171 |
+
<div style="color:rgba(0,212,176,.4);">MANEUVER</div>
|
| 172 |
+
<div id="own-man-label" style="color:#2dd4a0;">HIGH (2)</div>
|
| 173 |
+
</div>
|
| 174 |
+
</div>
|
| 175 |
+
|
| 176 |
+
<div id="start-btn" class="cm-btn" style="margin-top:12px; border-color:#00d4b0; color:#00d4b0; font-weight:bold; letter-spacing:0.15em;" onclick="startSimulation()">▶ START SIMULATION</div>
|
| 177 |
+
</div>
|
| 178 |
+
</div>
|
| 179 |
+
|
| 180 |
+
<div class="panel" style="display:flex;flex-direction:column;">
|
| 181 |
+
<div class="bc bc-tl"></div><div class="bc bc-tr"></div>
|
| 182 |
+
<div class="bc bc-bl"></div><div class="bc bc-br"></div>
|
| 183 |
+
|
| 184 |
+
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;">
|
| 185 |
+
<div style="font-size:10px;color:#00d4b0;letter-spacing:.2em;font-weight:700;">TARGET CLASSIFIER</div>
|
| 186 |
+
<span style="font-size:7px;color:rgba(0,212,176,.35);background:rgba(0,212,176,.05);padding:2px 6px;border:1px solid rgba(0,212,176,.18);letter-spacing:.07em;">EFFICIENTNETV2-L · 78.08%</span>
|
| 187 |
+
</div>
|
| 188 |
+
|
| 189 |
+
<div style="flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;border:1px solid rgba(0,212,176,.05);background:rgba(0,0,0,.3);padding:10px;position:relative;overflow:hidden;">
|
| 190 |
+
<svg width="100%" height="100%" style="position:absolute;top:0;left:0;opacity:.07;pointer-events:none;">
|
| 191 |
+
<line x1="50%" y1="0" x2="50%" y2="100%" stroke="#00d4b0" stroke-width="1"/>
|
| 192 |
+
<line x1="0" y1="50%" x2="100%" y2="50%" stroke="#00d4b0" stroke-width="1"/>
|
| 193 |
+
<circle cx="50%" cy="50%" r="45" fill="none" stroke="#00d4b0" stroke-width="1"/>
|
| 194 |
+
<circle cx="50%" cy="50%" r="85" fill="none" stroke="#00d4b0" stroke-width="1" stroke-dasharray="2 4"/>
|
| 195 |
+
</svg>
|
| 196 |
+
|
| 197 |
+
<svg id="target-svg" width="158" height="158" viewBox="0 0 120 120"
|
| 198 |
+
style="margin-bottom:8px;z-index:1;position:relative;filter:drop-shadow(0 0 6px currentColor);flex-shrink:0;"></svg>
|
| 199 |
+
<div id="target-info" style="text-align:center;width:100%;z-index:1;position:relative;"></div>
|
| 200 |
+
|
| 201 |
+
<div id="true-specs-box" style="display:none;width:100%;margin-top:8px;border-top:1px dashed rgba(255,255,255,.1);padding-top:8px;z-index:1;position:relative;">
|
| 202 |
+
<div style="font-size:8px;color:rgba(255,255,255,.3);margin-bottom:5px;letter-spacing:.1em;">THREAT METADATA // LOOKUP TABLE</div>
|
| 203 |
+
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:3px;font-size:9px;">
|
| 204 |
+
<div style="padding:4px;background:rgba(0,0,0,.45);border-left:1px solid #444;">
|
| 205 |
+
<span style="color:rgba(255,255,255,.28);font-size:7px;display:block;">MANEUVER</span>
|
| 206 |
+
<span id="e-man" style="color:#ccc;">---</span>
|
| 207 |
+
</div>
|
| 208 |
+
<div style="padding:4px;background:rgba(0,0,0,.45);border-left:1px solid #444;">
|
| 209 |
+
<span style="color:rgba(255,255,255,.28);font-size:7px;display:block;">ALTITUDE</span>
|
| 210 |
+
<span id="e-alt" style="color:#ccc;">---</span>
|
| 211 |
+
</div>
|
| 212 |
+
<div style="padding:4px;background:rgba(0,0,0,.45);border-left:1px solid #444;">
|
| 213 |
+
<span style="color:rgba(255,255,255,.28);font-size:7px;display:block;">GENERATION</span>
|
| 214 |
+
<span id="e-gen" style="color:#ccc;">---</span>
|
| 215 |
+
</div>
|
| 216 |
+
<div style="padding:4px;background:rgba(0,0,0,.45);border-left:1px solid #444;">
|
| 217 |
+
<span style="color:rgba(255,255,255,.28);font-size:7px;display:block;">MSL RANGE</span>
|
| 218 |
+
<span id="e-rng" style="color:#ccc;">---</span>
|
| 219 |
+
</div>
|
| 220 |
+
<div style="padding:4px;background:rgba(0,0,0,.45);border-left:1px solid #444;">
|
| 221 |
+
<span style="color:rgba(255,255,255,.28);font-size:7px;display:block;">MSL SPEED</span>
|
| 222 |
+
<span id="e-mspd" style="color:#ccc;">---</span>
|
| 223 |
+
</div>
|
| 224 |
+
<div style="padding:4px;background:rgba(0,0,0,.45);border-left:1px solid #444;">
|
| 225 |
+
<span style="color:rgba(255,255,255,.28);font-size:7px;display:block;">AA CAPABLE</span>
|
| 226 |
+
<span id="e-aa" style="color:#ccc;">---</span>
|
| 227 |
+
</div>
|
| 228 |
+
</div>
|
| 229 |
+
<div id="gen-adv-box" style="margin-top:4px;padding:4px 7px;border:1px solid rgba(0,212,176,.18);font-size:8px;display:flex;justify-content:space-between;align-items:center;">
|
| 230 |
+
<span style="color:rgba(0,212,176,.4);letter-spacing:.08em;">GENERATION ADVANTAGE</span>
|
| 231 |
+
<span id="gen-adv-val" style="font-weight:700;color:#00d4b0;">--</span>
|
| 232 |
+
</div>
|
| 233 |
+
</div>
|
| 234 |
+
</div>
|
| 235 |
+
</div>
|
| 236 |
+
|
| 237 |
+
<div class="panel" style="display:flex;flex-direction:column;align-items:center;">
|
| 238 |
+
<div class="bc bc-tl"></div><div class="bc bc-tr"></div>
|
| 239 |
+
<div class="bc bc-bl"></div><div class="bc bc-br"></div>
|
| 240 |
+
|
| 241 |
+
<div class="panel-label" style="width:100%;text-align:center;margin-bottom:10px;">THREAT KINEMATICS</div>
|
| 242 |
+
|
| 243 |
+
<svg width="138" height="138" viewBox="0 0 132 132" style="flex-shrink:0;">
|
| 244 |
+
<circle cx="66" cy="66" r="58" fill="none" stroke="rgba(0,212,176,.05)" stroke-width="1" stroke-dasharray="2,4"/>
|
| 245 |
+
<circle cx="66" cy="66" r="52" fill="none" stroke="rgba(0,212,176,.1)" stroke-width="4"/>
|
| 246 |
+
<circle id="threat-arc" cx="66" cy="66" r="52" fill="none" stroke="#00d4b0" stroke-width="5"
|
| 247 |
+
stroke-dasharray="0 326" transform="rotate(-90 66 66)" style="transition:stroke-dasharray .5s ease-out,stroke .3s;"/>
|
| 248 |
+
<circle cx="66" cy="66" r="44" fill="none" stroke="rgba(0,212,176,.09)" stroke-width="9"/>
|
| 249 |
+
<circle cx="66" cy="66" r="35" fill="rgba(4,4,9,.92)" stroke="rgba(0,212,176,.14)" stroke-width=".8"/>
|
| 250 |
+
<line x1="66" y1="5" x2="66" y2="14" stroke="rgba(0,212,176,.4)" stroke-width="1"/>
|
| 251 |
+
<line x1="127" y1="66" x2="118" y2="66" stroke="rgba(0,212,176,.4)" stroke-width="1"/>
|
| 252 |
+
<line x1="66" y1="127" x2="66" y2="118" stroke="rgba(0,212,176,.4)" stroke-width="1"/>
|
| 253 |
+
<line x1="5" y1="66" x2="14" y2="66" stroke="rgba(0,212,176,.4)" stroke-width="1"/>
|
| 254 |
+
<text id="threat-val" x="66" y="59" text-anchor="middle" fill="#00d4b0"
|
| 255 |
+
font-family="'IBM Plex Mono',monospace" font-size="22" font-weight="700">0%</text>
|
| 256 |
+
<text id="threat-label" x="66" y="71" text-anchor="middle" fill="rgba(0,212,176,.5)"
|
| 257 |
+
font-family="'IBM Plex Mono',monospace" font-size="8" letter-spacing="2">SAFE</text>
|
| 258 |
+
<text x="66" y="81" text-anchor="middle" fill="rgba(0,212,176,.3)"
|
| 259 |
+
font-family="'IBM Plex Mono',monospace" font-size="5.5" letter-spacing="0">HIT PROBABILITY</text>
|
| 260 |
+
</svg>
|
| 261 |
+
|
| 262 |
+
<div style="width:100%;margin-top:10px;display:flex;flex-direction:column;gap:0;">
|
| 263 |
+
<div class="data-row">
|
| 264 |
+
<span class="data-key">EVASION ETA</span>
|
| 265 |
+
<span id="eta-val" class="data-val">N/A</span>
|
| 266 |
+
</div>
|
| 267 |
+
<div class="data-row">
|
| 268 |
+
<span class="data-key">CLOSURE RATE</span>
|
| 269 |
+
<span id="close-val" class="data-val">0 M/S</span>
|
| 270 |
+
</div>
|
| 271 |
+
<div class="data-row">
|
| 272 |
+
<span class="data-key">DIST TO TGT</span>
|
| 273 |
+
<span id="dist-val" class="data-val">---</span>
|
| 274 |
+
</div>
|
| 275 |
+
<div class="data-row">
|
| 276 |
+
<span class="data-key">ASPECT ANGLE</span>
|
| 277 |
+
<span id="aspect-val" class="data-val">---</span>
|
| 278 |
+
</div>
|
| 279 |
+
<div class="data-row" style="border-bottom:1px solid rgba(0,212,176,.08);padding-bottom:4px;margin-bottom:8px;">
|
| 280 |
+
<span class="data-key">MSL PHASE</span>
|
| 281 |
+
<span id="phase-val" class="data-val">---</span>
|
| 282 |
+
</div>
|
| 283 |
+
</div>
|
| 284 |
+
|
| 285 |
+
<div id="cm-toggle" class="cm-btn" onclick="toggleCM()">
|
| 286 |
+
CM: <span id="cm-status">NOT DEPLOYED</span>
|
| 287 |
+
</div>
|
| 288 |
+
</div>
|
| 289 |
+
</div>
|
| 290 |
+
|
| 291 |
+
<div style="display:flex;gap:10px;margin-bottom:8px;align-items:stretch;min-height:42px;">
|
| 292 |
+
<div id="tac-banner" style="flex:1;border:1px solid rgba(0,212,176,.2);background:rgba(0,212,176,.05);padding:10px 14px;display:flex;align-items:center;justify-content:space-between;transition:all .3s;">
|
| 293 |
+
<span style="font-size:9px;color:rgba(0,212,176,.45);letter-spacing:.15em;white-space:nowrap;flex-shrink:0;">SYS ACTION</span>
|
| 294 |
+
<span id="tac-text" style="font-size:12px;font-weight:700;letter-spacing:.15em;color:#00d4b0;text-align:center;flex:1;">CONTINUE SCANNING SECTOR</span>
|
| 295 |
+
<span id="conf-text" style="font-size:9px;color:rgba(0,212,176,.45);white-space:nowrap;flex-shrink:0;">SYS NORMAL</span>
|
| 296 |
+
</div>
|
| 297 |
+
</div>
|
| 298 |
+
|
| 299 |
+
<div style="display:flex;justify-content:space-between;font-size:8px;color:rgba(0,212,176,.22);letter-spacing:.05em;">
|
| 300 |
+
<span>CLASSIFIER: EFFICIENTNETV2-L · TOP-1 78.08% · TOP-5 92.02%</span>
|
| 301 |
+
<span>ETA: XGBOOST · R²=0.9939 · MAE=0.46s</span>
|
| 302 |
+
<span>HIT CLASSIFIER: XGBOOST · F1=0.9968</span>
|
| 303 |
+
<span>SYSTEM: ATAS PIPELINE ONLINE</span>
|
| 304 |
+
</div>
|
| 305 |
+
|
| 306 |
+
</div>
|
| 307 |
+
</div>
|
| 308 |
+
|
| 309 |
+
<script>
|
| 310 |
+
// ──────────────────────────────────────────────
|
| 311 |
+
// CLOCK (Indian Standard Time)
|
| 312 |
+
// ──────────────────────────────────────────────
|
| 313 |
+
setInterval(() => {
|
| 314 |
+
const now = new Date();
|
| 315 |
+
const options = { timeZone: 'Asia/Kolkata', hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' };
|
| 316 |
+
document.getElementById('sys-time').innerText = now.toLocaleTimeString('en-IN', options) + ' IST';
|
| 317 |
+
}, 1000);
|
| 318 |
+
|
| 319 |
+
// ──────────────────────────────────────────────
|
| 320 |
+
// ENEMY DATABASE (For Left Column UI population)
|
| 321 |
+
// ──────────────────────────────────────────────
|
| 322 |
+
const enemyDB = {
|
| 323 |
+
"A10": {aircraft_max_speed:222, enemy_generation:4.0, maneuverability:1},
|
| 324 |
+
"A400M": {aircraft_max_speed:255, enemy_generation:4.0, maneuverability:0},
|
| 325 |
+
"AG600": {aircraft_max_speed:155, enemy_generation:4.0, maneuverability:0},
|
| 326 |
+
"AH64": {aircraft_max_speed:101, enemy_generation:4.0, maneuverability:1},
|
| 327 |
+
"B21": {aircraft_max_speed:277, enemy_generation:5.0, maneuverability:0},
|
| 328 |
+
"F16": {aircraft_max_speed:590, enemy_generation:4.0, maneuverability:2},
|
| 329 |
+
"F22": {aircraft_max_speed:669, enemy_generation:5.0, maneuverability:2},
|
| 330 |
+
"Il76": {aircraft_max_speed:250, enemy_generation:3.5, maneuverability:0},
|
| 331 |
+
"J20": {aircraft_max_speed:590, enemy_generation:5.0, maneuverability:2},
|
| 332 |
+
"MQ9": {aircraft_max_speed:134, enemy_generation:4.0, maneuverability:1},
|
| 333 |
+
"Rafale": {aircraft_max_speed:531, enemy_generation:4.5, maneuverability:2},
|
| 334 |
+
"WZ9": {aircraft_max_speed:85, enemy_generation:4.0, maneuverability:1},
|
| 335 |
+
"Su57": {aircraft_max_speed:590, enemy_generation:5.0, maneuverability:2}
|
| 336 |
+
};
|
| 337 |
+
|
| 338 |
+
const fullList = [
|
| 339 |
+
'A10','A400M','AG600','AH64','AKINCI','AV8B','An124','An22','An225','An72',
|
| 340 |
+
'B1','B2','B21','B52','Be200','C1','C130','C17','C2','C390','C5','CH47','CH53',
|
| 341 |
+
'CL415','E2','E7','EF2000','EMB314','F117','F14','F15','F16','F18','F2','F22',
|
| 342 |
+
'F35','F4','FCK1','H6','Il76','J10','J20','J35','J36','J50','JAS39','JF17',
|
| 343 |
+
'JH7','KAAN','KC135','KF21','KIZILELMA','KJ600','Ka27','Ka52','MQ20','MQ25',
|
| 344 |
+
'MQ28','MQ9','Mi24','Mi26','Mi28','Mi8','Mig29','Mig31','Mirage2000','NH90',
|
| 345 |
+
'P3','RQ4','Rafale','SR71','Su24','Su25','Su34','Su47','Su57','T50','TB001',
|
| 346 |
+
'TB2','Tejas','Tornado','Tu160','Tu22M','Tu95','U2','UH60','US2','V22','V280',
|
| 347 |
+
'Vulcan','WZ10','WZ7','WZ9','X29','X32','XB70','XQ58','Y20','YF23','Z10','Z19','Z21'
|
| 348 |
+
];
|
| 349 |
+
fullList.forEach(ac => {
|
| 350 |
+
if (!enemyDB[ac]) enemyDB[ac] = {
|
| 351 |
+
aircraft_max_speed:400, enemy_generation:4.0, maneuverability:1
|
| 352 |
+
};
|
| 353 |
+
});
|
| 354 |
+
|
| 355 |
+
let currentOwn = {};
|
| 356 |
+
const ownSelect = document.getElementById('own-platform-select');
|
| 357 |
+
fullList.forEach(ac => {
|
| 358 |
+
const opt = document.createElement('option');
|
| 359 |
+
opt.value = ac;
|
| 360 |
+
opt.innerText = ac + ' (GEN ' + enemyDB[ac].enemy_generation.toFixed(1) + ')';
|
| 361 |
+
ownSelect.appendChild(opt);
|
| 362 |
+
});
|
| 363 |
+
ownSelect.value = 'Tejas';
|
| 364 |
+
|
| 365 |
+
// ──────────────────────────────────────────────
|
| 366 |
+
// STATE & TOGGLES
|
| 367 |
+
// ──────────────────────────────────────────────
|
| 368 |
+
let cmDeployed = false;
|
| 369 |
+
let isUI_Locked = false;
|
| 370 |
+
let uploadedFile = null;
|
| 371 |
+
|
| 372 |
+
function toggleCM() {
|
| 373 |
+
if (isUI_Locked) return;
|
| 374 |
+
cmDeployed = !cmDeployed;
|
| 375 |
+
const btn = document.getElementById('cm-toggle');
|
| 376 |
+
document.getElementById('cm-status').innerText = cmDeployed ? 'DEPLOYED' : 'NOT DEPLOYED';
|
| 377 |
+
cmDeployed ? btn.classList.add('deployed') : btn.classList.remove('deployed');
|
| 378 |
+
}
|
| 379 |
+
|
| 380 |
+
function updateOwnPlatform() {
|
| 381 |
+
if (isUI_Locked) return;
|
| 382 |
+
const key = document.getElementById('own-platform-select').value;
|
| 383 |
+
const data = enemyDB[key];
|
| 384 |
+
const cat = getCategory(key);
|
| 385 |
+
const machMax = parseFloat(Math.max(0.8, (data.aircraft_max_speed / 340)).toFixed(2));
|
| 386 |
+
const altMax = cat === 'fighter' ? 60000 : (cat === 'helo' ? 20000 : 42000);
|
| 387 |
+
|
| 388 |
+
currentOwn = {
|
| 389 |
+
max_spd: machMax, max_alt: altMax,
|
| 390 |
+
gen: data.enemy_generation, man: data.maneuverability,
|
| 391 |
+
name_man: data.maneuverability >= 2 ? 'HIGH (' + data.maneuverability + ')'
|
| 392 |
+
: (data.maneuverability === 1 ? 'MED (1)' : 'LOW (0)')
|
| 393 |
+
};
|
| 394 |
+
|
| 395 |
+
document.getElementById('max-spd-label').innerText = currentOwn.max_spd;
|
| 396 |
+
document.getElementById('max-alt-label').innerText = (currentOwn.max_alt / 1000) + 'k';
|
| 397 |
+
document.getElementById('own-gen-label').innerText = currentOwn.gen.toFixed(1);
|
| 398 |
+
document.getElementById('own-man-label').innerText = currentOwn.name_man;
|
| 399 |
+
document.getElementById('slide-spd').max = currentOwn.max_spd;
|
| 400 |
+
document.getElementById('slide-alt').max = currentOwn.max_alt;
|
| 401 |
+
|
| 402 |
+
if (parseFloat(document.getElementById('slide-spd').value) > currentOwn.max_spd)
|
| 403 |
+
document.getElementById('slide-spd').value = currentOwn.max_spd;
|
| 404 |
+
if (parseFloat(document.getElementById('slide-alt').value) > currentOwn.max_alt)
|
| 405 |
+
document.getElementById('slide-alt').value = currentOwn.max_alt;
|
| 406 |
+
|
| 407 |
+
updateOwnSliders();
|
| 408 |
+
}
|
| 409 |
+
|
| 410 |
+
function updateOwnSliders() {
|
| 411 |
+
if (isUI_Locked) return;
|
| 412 |
+
document.getElementById('val-spd').innerText = parseFloat(document.getElementById('slide-spd').value).toFixed(2);
|
| 413 |
+
document.getElementById('val-alt').innerText = parseInt(document.getElementById('slide-alt').value).toLocaleString();
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
function setLockState(locked) {
|
| 417 |
+
isUI_Locked = locked;
|
| 418 |
+
document.getElementById('slide-spd').disabled = locked;
|
| 419 |
+
document.getElementById('slide-alt').disabled = locked;
|
| 420 |
+
document.getElementById('own-platform-select').disabled = locked;
|
| 421 |
+
|
| 422 |
+
const opacityVal = locked ? '0.3' : '1';
|
| 423 |
+
const pointerVal = locked ? 'none' : 'auto';
|
| 424 |
+
|
| 425 |
+
document.getElementById('start-btn').style.opacity = opacityVal;
|
| 426 |
+
document.getElementById('start-btn').style.pointerEvents = pointerVal;
|
| 427 |
+
|
| 428 |
+
document.getElementById('cm-toggle').style.opacity = opacityVal;
|
| 429 |
+
document.getElementById('cm-toggle').style.pointerEvents = pointerVal;
|
| 430 |
+
|
| 431 |
+
document.getElementById('sensor-feed').style.pointerEvents = pointerVal;
|
| 432 |
+
}
|
| 433 |
+
|
| 434 |
+
// ────────��─────────────────────────────────────
|
| 435 |
+
// AIRCRAFT BLUEPRINTS (SVG) & CATEGORIES
|
| 436 |
+
// ──────────────────────────────────────────────
|
| 437 |
+
const premiumSVGs = {
|
| 438 |
+
none: `
|
| 439 |
+
<circle cx="60" cy="60" r="50" fill="none" stroke="rgba(0,212,176,0.08)" stroke-width="1"/>
|
| 440 |
+
<circle cx="60" cy="60" r="25" fill="none" stroke="rgba(0,212,176,0.05)" stroke-width="1" stroke-dasharray="3 5"/>
|
| 441 |
+
<line x1="60" y1="10" x2="60" y2="110" stroke="rgba(0,212,176,0.12)" stroke-width="1"/>
|
| 442 |
+
<line x1="10" y1="60" x2="110" y2="60" stroke="rgba(0,212,176,0.12)" stroke-width="1"/>
|
| 443 |
+
<circle cx="60" cy="60" r="3" fill="#00d4b0" opacity="0.5"/>`,
|
| 444 |
+
|
| 445 |
+
fighter: `
|
| 446 |
+
<g transform="translate(0,5)">
|
| 447 |
+
<polygon points="60,30 20,70 20,85 45,80 60,85" fill="rgba(232,68,68,0.1)" stroke="currentColor" stroke-width="1.2"/>
|
| 448 |
+
<polygon points="60,30 100,70 100,85 75,80 60,85" fill="rgba(232,68,68,0.1)" stroke="currentColor" stroke-width="1.2"/>
|
| 449 |
+
<path d="M60,10 L65,30 L70,70 L68,100 L52,100 L50,70 L55,30 Z" fill="rgba(232,68,68,0.15)" stroke="currentColor" stroke-width="1.5"/>
|
| 450 |
+
<path d="M58,25 L62,25 L63,40 L57,40 Z" fill="none" stroke="currentColor" stroke-width="0.8" opacity="0.8"/>
|
| 451 |
+
<line x1="60" y1="40" x2="60" y2="100" stroke="currentColor" stroke-width="1" opacity="0.3"/>
|
| 452 |
+
<rect x="53" y="95" width="4" height="10" fill="rgba(0,0,0,0.8)" stroke="currentColor" stroke-width="1"/>
|
| 453 |
+
<rect x="63" y="95" width="4" height="10" fill="rgba(0,0,0,0.8)" stroke="currentColor" stroke-width="1"/>
|
| 454 |
+
<polygon points="52,80 40,105 48,105 54,90" fill="rgba(232,68,68,0.2)" stroke="currentColor" stroke-width="1"/>
|
| 455 |
+
<polygon points="68,80 80,105 72,105 66,90" fill="rgba(232,68,68,0.2)" stroke="currentColor" stroke-width="1"/>
|
| 456 |
+
</g>`,
|
| 457 |
+
|
| 458 |
+
bomber: `
|
| 459 |
+
<g transform="translate(0,15)">
|
| 460 |
+
<path d="M60,10 L110,60 L90,75 L75,65 L60,75 L45,65 L30,75 L10,60 Z"
|
| 461 |
+
fill="rgba(232,68,68,0.15)" stroke="currentColor" stroke-width="1.5"/>
|
| 462 |
+
<path d="M55,25 L65,25 L62,30 L58,30 Z" fill="none" stroke="currentColor" stroke-width="0.8" opacity="0.8"/>
|
| 463 |
+
<line x1="50" y1="60" x2="50" y2="70" stroke="currentColor" stroke-width="1" opacity="0.5"/>
|
| 464 |
+
<line x1="70" y1="60" x2="70" y2="70" stroke="currentColor" stroke-width="1" opacity="0.5"/>
|
| 465 |
+
<path d="M45,65 Q60,55 75,65" fill="none" stroke="currentColor" stroke-width="1" stroke-dasharray="2,2" opacity="0.6"/>
|
| 466 |
+
</g>`,
|
| 467 |
+
|
| 468 |
+
helo: `
|
| 469 |
+
<g transform="translate(0,5)">
|
| 470 |
+
<g class="rotor-spin">
|
| 471 |
+
<circle cx="60" cy="45" r="45" fill="rgba(240,160,48,0.04)" stroke="currentColor" stroke-width="0.5" stroke-dasharray="2 6"/>
|
| 472 |
+
<line x1="60" y1="2" x2="60" y2="88" stroke="currentColor" stroke-width="1.5" opacity="0.7"/>
|
| 473 |
+
<line x1="17" y1="45" x2="103" y2="45" stroke="currentColor" stroke-width="1.5" opacity="0.7"/>
|
| 474 |
+
<circle cx="60" cy="45" r="3" fill="currentColor" opacity="0.9"/>
|
| 475 |
+
</g>
|
| 476 |
+
<path d="M60,15 C63,15 65,20 65,35 L62,65 L58,65 L55,35 C55,20 57,15 60,15 Z"
|
| 477 |
+
fill="rgba(240,160,48,0.2)" stroke="currentColor" stroke-width="1.5"/>
|
| 478 |
+
<rect x="42" y="42" width="13" height="4" fill="rgba(240,160,48,0.1)" stroke="currentColor" stroke-width="1"/>
|
| 479 |
+
<rect x="65" y="42" width="13" height="4" fill="rgba(240,160,48,0.1)" stroke="currentColor" stroke-width="1"/>
|
| 480 |
+
<polygon points="58,65 62,65 61,95 59,95" fill="rgba(240,160,48,0.15)" stroke="currentColor" stroke-width="1.2"/>
|
| 481 |
+
<polygon points="50,88 70,88 70,92 50,92" fill="rgba(240,160,48,0.1)" stroke="currentColor" stroke-width="1"/>
|
| 482 |
+
<g style="transform-origin:60px 98px;" class="rotor-spin">
|
| 483 |
+
<line x1="52" y1="98" x2="68" y2="98" stroke="currentColor" stroke-width="1"/>
|
| 484 |
+
<line x1="60" y1="90" x2="60" y2="106" stroke="currentColor" stroke-width="1"/>
|
| 485 |
+
</g>
|
| 486 |
+
</g>`,
|
| 487 |
+
|
| 488 |
+
cargo: `
|
| 489 |
+
<g transform="translate(0,5)">
|
| 490 |
+
<defs>
|
| 491 |
+
<filter id="eGlow" x="-50%" y="-50%" width="200%" height="200%">
|
| 492 |
+
<feGaussianBlur stdDeviation="1.5" result="b"/>
|
| 493 |
+
<feComposite in="SourceGraphic" in2="b" operator="over"/>
|
| 494 |
+
</filter>
|
| 495 |
+
</defs>
|
| 496 |
+
<path d="M54,40 L10,65 L8,75 L25,72 L54,55 Z" fill="rgba(0,212,176,0.1)" stroke="currentColor" stroke-width="1.2"/>
|
| 497 |
+
<path d="M66,40 L110,65 L112,75 L95,72 L66,55 Z" fill="rgba(0,212,176,0.1)" stroke="currentColor" stroke-width="1.2"/>
|
| 498 |
+
<rect x="28" y="52" width="5" height="12" rx="2" fill="rgba(0,0,0,.8)" stroke="currentColor" stroke-width="1"/>
|
| 499 |
+
<rect x="40" y="47" width="5" height="14" rx="2" fill="rgba(0,0,0,.8)" stroke="currentColor" stroke-width="1"/>
|
| 500 |
+
<rect x="87" y="52" width="5" height="12" rx="2" fill="rgba(0,0,0,.8)" stroke="currentColor" stroke-width="1"/>
|
| 501 |
+
<rect x="75" y="47" width="5" height="14" rx="2" fill="rgba(0,0,0,.8)" stroke="currentColor" stroke-width="1"/>
|
| 502 |
+
<path d="M29,64 L32,64 L30.5,72 Z" fill="#ffaa00" opacity="0.8"/>
|
| 503 |
+
<path d="M41,61 L44,61 L42.5,70 Z" fill="#ffaa00" opacity="0.8"/>
|
| 504 |
+
<path d="M88,64 L91,64 L89.5,72 Z" fill="#ffaa00" opacity="0.8"/>
|
| 505 |
+
<path d="M76,61 L79,61 L77.5,70 Z" fill="#ffaa00" opacity="0.8"/>
|
| 506 |
+
<path d="M60,10 C68,10 70,18 70,45 L66,95 L54,95 L50,45 C50,18 52,10 60,10 Z"
|
| 507 |
+
fill="rgba(0,212,176,0.15)" stroke="currentColor" stroke-width="1.5"/>
|
| 508 |
+
<polygon points="60,100 85,108 85,112 60,105 35,112 35,108"
|
| 509 |
+
fill="rgba(0,212,176,0.2)" stroke="currentColor" stroke-width="1.5"/>
|
| 510 |
+
</g>`
|
| 511 |
+
};
|
| 512 |
+
|
| 513 |
+
function getCategory(name) {
|
| 514 |
+
const bombers = ['B1','B2','B21','B52','H6','Tu160','Tu22M','Tu95','Vulcan','XB70'];
|
| 515 |
+
const cargo = ['A400M','An124','An22','An225','An72','C1','C130','C17','C2','C390',
|
| 516 |
+
'C5','Il76','KC135','Y20','P3','E2','E7','KJ600','AG600','Be200','CL415','US2'];
|
| 517 |
+
const helo = ['AH64','CH47','CH53','Ka27','Ka52','Mi24','Mi26','Mi28','Mi8',
|
| 518 |
+
'NH90','UH60','WZ10','WZ9','Z10','Z19','Z21','V22','V280'];
|
| 519 |
+
if (bombers.includes(name)) return 'bomber';
|
| 520 |
+
if (cargo.includes(name)) return 'cargo';
|
| 521 |
+
if (helo.includes(name)) return 'helo';
|
| 522 |
+
return 'fighter';
|
| 523 |
+
}
|
| 524 |
+
|
| 525 |
+
function aspectDescriptor(angle) {
|
| 526 |
+
if (angle < 30) return '(HEAD-ON)';
|
| 527 |
+
if (angle > 150) return '(TAIL)';
|
| 528 |
+
if (angle > 75 && angle < 105) return '(BEAM)';
|
| 529 |
+
return '(OBLIQUE)';
|
| 530 |
+
}
|
| 531 |
+
|
| 532 |
+
// ──────────────────────────────────────────────
|
| 533 |
+
// BACKEND API INTEGRATION (The Real Brain)
|
| 534 |
+
// ──────────────────────────────────────────────
|
| 535 |
+
function handleImageUpload(event) {
|
| 536 |
+
const file = event.target.files[0];
|
| 537 |
+
if (!file) return;
|
| 538 |
+
uploadedFile = file;
|
| 539 |
+
|
| 540 |
+
const img = document.getElementById('sensor-img');
|
| 541 |
+
img.src = URL.createObjectURL(file);
|
| 542 |
+
img.style.display = 'block';
|
| 543 |
+
document.getElementById('sensor-text').style.display = 'none';
|
| 544 |
+
document.getElementById('sensor-status').innerText = 'TARGET ACQUIRED';
|
| 545 |
+
document.getElementById('sensor-status').style.color = '#00d4b0';
|
| 546 |
+
}
|
| 547 |
+
|
| 548 |
+
async function startSimulation() {
|
| 549 |
+
if (isUI_Locked) return;
|
| 550 |
+
if (!uploadedFile) {
|
| 551 |
+
alert("Please upload a target photo to initiate pipeline.");
|
| 552 |
+
return;
|
| 553 |
+
}
|
| 554 |
+
|
| 555 |
+
// 1. Enter Loading State & Lock UI
|
| 556 |
+
setLockState(true);
|
| 557 |
+
document.getElementById('sensor-status').innerText = 'UPLINK ACTIVE...';
|
| 558 |
+
document.getElementById('sensor-status').style.color = '#f0a030';
|
| 559 |
+
document.getElementById('scan-line').style.display = 'block';
|
| 560 |
+
document.getElementById('clear-btn').style.display = 'none';
|
| 561 |
+
|
| 562 |
+
// 2. Gather UI values and generate plausible geometry for missing fields
|
| 563 |
+
const ownSpeedMS = parseFloat(document.getElementById('slide-spd').value) * 340;
|
| 564 |
+
const ownAltM = parseInt(document.getElementById('slide-alt').value) * 0.3048;
|
| 565 |
+
const enemyAltM = Math.floor(Math.random() * 9000 + 1000);
|
| 566 |
+
const launchDist = Math.floor(Math.random() * 25000 + 10000);
|
| 567 |
+
const remainDist = Math.floor(Math.random() * launchDist);
|
| 568 |
+
const azimuth = Math.floor(Math.random() * 180);
|
| 569 |
+
const elevation = Math.floor(Math.random() * 20);
|
| 570 |
+
const friendly = document.getElementById('own-platform-select').value || 'Tejas';
|
| 571 |
+
|
| 572 |
+
// 3. Construct Payload
|
| 573 |
+
const formData = new FormData();
|
| 574 |
+
formData.append('image', uploadedFile);
|
| 575 |
+
formData.append('your_speed', ownSpeedMS);
|
| 576 |
+
formData.append('your_altitude', ownAltM);
|
| 577 |
+
formData.append('enemy_altitude', enemyAltM);
|
| 578 |
+
formData.append('countermeasure_deployed', cmDeployed ? 1 : 0);
|
| 579 |
+
formData.append('launch_distance', launchDist);
|
| 580 |
+
formData.append('remaining_distance', remainDist);
|
| 581 |
+
formData.append('azimuth', azimuth);
|
| 582 |
+
formData.append('elevation', elevation);
|
| 583 |
+
formData.append('friendly_aircraft', friendly);
|
| 584 |
+
|
| 585 |
+
try {
|
| 586 |
+
// 4. Fire to FastAPI Backend
|
| 587 |
+
const response = await fetch('http://localhost:8000/analyze', {
|
| 588 |
+
method: 'POST',
|
| 589 |
+
body: formData
|
| 590 |
+
});
|
| 591 |
+
|
| 592 |
+
if (!response.ok) throw new Error("API Connection Failed");
|
| 593 |
+
const data = await response.json();
|
| 594 |
+
|
| 595 |
+
// 5. Success - Proceed to DOM update
|
| 596 |
+
document.getElementById('scan-line').style.display = 'none';
|
| 597 |
+
document.getElementById('sensor-status').innerText = 'LOCKED';
|
| 598 |
+
document.getElementById('sensor-status').style.color = '#e84444';
|
| 599 |
+
|
| 600 |
+
updateDOM(data, remainDist, azimuth, ownSpeedMS, enemyAltM);
|
| 601 |
+
|
| 602 |
+
} catch (error) {
|
| 603 |
+
console.error(error);
|
| 604 |
+
document.getElementById('scan-line').style.display = 'none';
|
| 605 |
+
document.getElementById('sensor-status').innerText = 'UPLINK FAILED';
|
| 606 |
+
document.getElementById('sensor-status').style.color = '#e84444';
|
| 607 |
+
} finally {
|
| 608 |
+
setLockState(false);
|
| 609 |
+
}
|
| 610 |
+
}
|
| 611 |
+
|
| 612 |
+
// ──────────────────────────────────────────────
|
| 613 |
+
// HUD UPDATER (Maps Real JSON to UI)
|
| 614 |
+
// ──────────────────────────────────────────────
|
| 615 |
+
function updateDOM(data, distMeters, azimuth, ownSpeedMS, enemyAltM) {
|
| 616 |
+
console.log(data);
|
| 617 |
+
const name = data.aircraft_name;
|
| 618 |
+
const cat = getCategory(name);
|
| 619 |
+
const isAA = data.no_aa_capability === 0;
|
| 620 |
+
const threatProb = isAA ? Math.floor(data.hit_probability * 100) : 0;
|
| 621 |
+
|
| 622 |
+
// Determine colour based on threat
|
| 623 |
+
let col = '#00d4b0'; // Safe
|
| 624 |
+
if (isAA) {
|
| 625 |
+
if (threatProb > 60 || data.eta_seconds < 10) col = '#e84444'; // Crit
|
| 626 |
+
else if (threatProb > 30) col = '#f0a030'; // Warn
|
| 627 |
+
}
|
| 628 |
+
|
| 629 |
+
// ── Header ──
|
| 630 |
+
const hdr = document.getElementById('header-alert');
|
| 631 |
+
hdr.innerText = 'TARGET LOCKED: ' + name.toUpperCase();
|
| 632 |
+
hdr.style.color = '#e84444';
|
| 633 |
+
hdr.classList.add('hb');
|
| 634 |
+
|
| 635 |
+
// ── SVG Canvas ──
|
| 636 |
+
const svgNode = document.getElementById('target-svg');
|
| 637 |
+
svgNode.style.color = col;
|
| 638 |
+
svgNode.innerHTML = premiumSVGs[cat];
|
| 639 |
+
|
| 640 |
+
document.getElementById('target-info').innerHTML = `
|
| 641 |
+
<div style="font-size:15px;font-weight:700;letter-spacing:.1em;color:${col};margin-top:5px;">${name.toUpperCase()}</div>
|
| 642 |
+
<div style="font-size:8px;color:rgba(255,255,255,.35);letter-spacing:.1em;margin-top:2px;">CLASS: ${cat.toUpperCase()}</div>
|
| 643 |
+
`;
|
| 644 |
+
|
| 645 |
+
// ── True Metadata ──
|
| 646 |
+
document.getElementById('true-specs-box').style.display = 'block';
|
| 647 |
+
document.getElementById('e-man').innerText = data.maneuverability >= 2 ? "HIGH (2)" : (data.maneuverability === 1 ? "MED (1)" : "LOW (0)");
|
| 648 |
+
document.getElementById('e-alt').textContent = Math.round(enemyAltM / 0.3048).toLocaleString() + ' FT';
|
| 649 |
+
document.getElementById('e-gen').innerText = 'GEN ' + data.enemy_generation.toFixed(1);
|
| 650 |
+
document.getElementById('e-rng').innerText = data.missile_range > 0 ? (data.missile_range / 1000).toFixed(0) + ' KM' : 'NONE';
|
| 651 |
+
document.getElementById('e-mspd').innerText = data.missile_speed > 0 ? data.missile_speed + ' M/S' : 'N/A';
|
| 652 |
+
document.getElementById('e-aa').innerText = isAA ? 'YES' : 'NO';
|
| 653 |
+
|
| 654 |
+
// ── Generation Advantage ──
|
| 655 |
+
if (isAA) {
|
| 656 |
+
const genDiff = currentOwn.gen - data.enemy_generation;
|
| 657 |
+
const advEl = document.getElementById('gen-adv-val');
|
| 658 |
+
const advBox = document.getElementById('gen-adv-box');
|
| 659 |
+
|
| 660 |
+
if (genDiff > 0) {
|
| 661 |
+
advEl.innerText = '+' + genDiff.toFixed(1) + ' FRIENDLY ADV';
|
| 662 |
+
advEl.style.color = '#2dd4a0';
|
| 663 |
+
advBox.style.borderColor = 'rgba(45,212,160,.28)';
|
| 664 |
+
} else if (genDiff < 0) {
|
| 665 |
+
advEl.innerText = genDiff.toFixed(1) + ' ENEMY ADV';
|
| 666 |
+
advEl.style.color = '#e84444';
|
| 667 |
+
advBox.style.borderColor = 'rgba(232,68,68,.28)';
|
| 668 |
+
} else {
|
| 669 |
+
advEl.innerText = 'PARITY';
|
| 670 |
+
advEl.style.color = '#f0a030';
|
| 671 |
+
advBox.style.borderColor = 'rgba(240,160,48,.28)';
|
| 672 |
+
}
|
| 673 |
+
} else {
|
| 674 |
+
document.getElementById('gen-adv-val').textContent = 'N/A';
|
| 675 |
+
document.getElementById('gen-adv-box').style.borderColor = 'rgba(0,212,176,.18)';
|
| 676 |
+
}
|
| 677 |
+
|
| 678 |
+
// ── Threat Gauge ──
|
| 679 |
+
const dash = (threatProb / 100) * 326;
|
| 680 |
+
document.getElementById('threat-arc').style.strokeDasharray = `${dash} 326`;
|
| 681 |
+
document.getElementById('threat-arc').style.stroke = col;
|
| 682 |
+
document.getElementById('threat-val').textContent = `${threatProb}%`;
|
| 683 |
+
document.getElementById('threat-val').setAttribute('fill', col);
|
| 684 |
+
|
| 685 |
+
let labelText = threatProb > 60 ? 'CRITICAL' : (threatProb > 30 ? 'ELEVATED' : 'LOW');
|
| 686 |
+
document.getElementById('threat-label').textContent = labelText;
|
| 687 |
+
document.getElementById('threat-label').setAttribute('fill', col);
|
| 688 |
+
|
| 689 |
+
// ── Kinematics ──
|
| 690 |
+
document.getElementById('eta-val').innerText = isAA ? data.eta_seconds.toFixed(1) + 's' : 'N/A';
|
| 691 |
+
document.getElementById('eta-val').style.color = col;
|
| 692 |
+
|
| 693 |
+
const approxClose = data.missile_speed > 0 ? Math.floor(data.missile_speed + ownSpeedMS) : 0;
|
| 694 |
+
document.getElementById('close-val').innerText = isAA ? approxClose.toLocaleString() + ' M/S' : '0 M/S';
|
| 695 |
+
|
| 696 |
+
document.getElementById('dist-val').innerText = (distMeters / 1000).toFixed(1) + ' KM';
|
| 697 |
+
document.getElementById('aspect-val').innerText = azimuth + '\u00b0 ' + aspectDescriptor(azimuth);
|
| 698 |
+
document.getElementById('phase-val').innerText = isAA ? 'TRACKING' : 'N/A';
|
| 699 |
+
|
| 700 |
+
// ── Banner ──
|
| 701 |
+
const banner = document.getElementById('tac-banner');
|
| 702 |
+
banner.style.borderColor = col;
|
| 703 |
+
banner.style.background = col === '#00d4b0' ? 'rgba(0,212,176,.05)'
|
| 704 |
+
: col === '#f0a030' ? 'rgba(240,160,48,.05)'
|
| 705 |
+
: 'rgba(232,68,68,.05)';
|
| 706 |
+
document.getElementById('tac-text').innerText = data.recommendation;
|
| 707 |
+
document.getElementById('tac-text').style.color = col;
|
| 708 |
+
|
| 709 |
+
document.getElementById('conf-text').innerText = threatProb > 70 ? 'HIGH CONF'
|
| 710 |
+
: threatProb > 35 ? 'MED CONF'
|
| 711 |
+
: 'SYS NORMAL';
|
| 712 |
+
|
| 713 |
+
// ── Critical Pulse ──
|
| 714 |
+
const hud = document.getElementById('main-hud');
|
| 715 |
+
threatProb > 60 ? hud.classList.add('threat-critical') : hud.classList.remove('threat-critical');
|
| 716 |
+
|
| 717 |
+
document.getElementById('clear-btn').style.display = 'block';
|
| 718 |
+
}
|
| 719 |
+
|
| 720 |
+
// ──────────────────────────────────────────────
|
| 721 |
+
// RESET HUD TO NEUTRAL
|
| 722 |
+
// ──────────────────────────────────────────────
|
| 723 |
+
function clearTarget() {
|
| 724 |
+
uploadedFile = null;
|
| 725 |
+
// SVG
|
| 726 |
+
const svgNode = document.getElementById('target-svg');
|
| 727 |
+
svgNode.style.color = '#00d4b0';
|
| 728 |
+
svgNode.innerHTML = premiumSVGs.none;
|
| 729 |
+
document.getElementById('target-info').innerHTML =
|
| 730 |
+
`<div style="font-size:13px;font-weight:700;color:#00d4b0;margin-top:12px;letter-spacing:.1em;">SCANNING AIRSPACE...</div>`;
|
| 731 |
+
document.getElementById('true-specs-box').style.display = 'none';
|
| 732 |
+
|
| 733 |
+
// Threat gauge
|
| 734 |
+
document.getElementById('threat-arc').style.strokeDasharray = '0 326';
|
| 735 |
+
document.getElementById('threat-arc').style.stroke = '#00d4b0';
|
| 736 |
+
document.getElementById('threat-val').textContent = '0%';
|
| 737 |
+
document.getElementById('threat-val').setAttribute('fill', '#00d4b0');
|
| 738 |
+
document.getElementById('threat-label').textContent = 'SAFE';
|
| 739 |
+
document.getElementById('threat-label').setAttribute('fill', 'rgba(0,212,176,.5)');
|
| 740 |
+
|
| 741 |
+
// Kinematics
|
| 742 |
+
document.getElementById('eta-val').innerText = 'N/A';
|
| 743 |
+
document.getElementById('eta-val').style.color = '#00d4b0';
|
| 744 |
+
document.getElementById('close-val').innerText = '0 M/S';
|
| 745 |
+
document.getElementById('dist-val').innerText = '---';
|
| 746 |
+
document.getElementById('aspect-val').innerText = '---';
|
| 747 |
+
document.getElementById('phase-val').innerText = '---';
|
| 748 |
+
|
| 749 |
+
// Banner
|
| 750 |
+
const banner = document.getElementById('tac-banner');
|
| 751 |
+
banner.style.borderColor = 'rgba(0,212,176,.2)';
|
| 752 |
+
banner.style.background = 'rgba(0,212,176,.05)';
|
| 753 |
+
document.getElementById('tac-text').innerText = 'CONTINUE SCANNING SECTOR';
|
| 754 |
+
document.getElementById('tac-text').style.color = '#00d4b0';
|
| 755 |
+
document.getElementById('conf-text').innerText = 'SYS NORMAL';
|
| 756 |
+
|
| 757 |
+
// Header & Feed Reset
|
| 758 |
+
const hdr = document.getElementById('header-alert');
|
| 759 |
+
hdr.innerText = 'SCANNING AIRSPACE';
|
| 760 |
+
hdr.style.color = 'rgba(0,212,176,.3)';
|
| 761 |
+
hdr.classList.remove('hb');
|
| 762 |
+
|
| 763 |
+
document.getElementById('main-hud').classList.remove('threat-critical');
|
| 764 |
+
document.getElementById('clear-btn').style.display = 'none';
|
| 765 |
+
document.getElementById('sensor-status').innerText = 'STANDBY';
|
| 766 |
+
document.getElementById('sensor-status').style.color = 'rgba(0,212,176,.3)';
|
| 767 |
+
document.getElementById('sensor-img').style.display = 'none';
|
| 768 |
+
document.getElementById('sensor-text').style.display = 'block';
|
| 769 |
+
document.getElementById('file-upload').value = '';
|
| 770 |
+
}
|
| 771 |
+
|
| 772 |
+
// ── INIT ──
|
| 773 |
+
updateOwnPlatform();
|
| 774 |
+
</script>
|
| 775 |
+
</body>
|
| 776 |
+
</html>
|
release_models/aircraft_classifier/atas_final_fine_tuned_aircraft_classifier_model.keras
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f35c73c33c0318b07ec261f6d1cf37e5ce15543a32867a398841793c9f1fca2d
|
| 3 |
+
size 941789413
|
release_models/eta/atas_final_eta_regressor_model.joblib
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:599498f9d68a49295abe2d221e8e5aaf20ba071b2c524b640e176b52bc5a19d7
|
| 3 |
+
size 42999244
|
release_models/hit/atas_final_hit_classifier_model.joblib
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d4a4900f2e5e1a4027d6652ff605e363f5c79a97abb640aba09708d6792a78c1
|
| 3 |
+
size 425666
|
requirements.txt
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi==0.136.3
|
| 2 |
+
uvicorn==0.49.0
|
| 3 |
+
python-multipart==0.0.32
|
| 4 |
+
pydantic==2.13.4
|
| 5 |
+
tensorflow==2.16.1
|
| 6 |
+
tf-keras==2.16.0
|
| 7 |
+
tensorflow-hub==0.16.1
|
| 8 |
+
numpy==1.26.4
|
| 9 |
+
pandas
|
| 10 |
+
pillow
|
| 11 |
+
scikit-learn
|
| 12 |
+
xgboost
|
| 13 |
+
joblib
|
src/__pycache__/classifier.cpython-311.pyc
ADDED
|
Binary file (6.89 kB). View file
|
|
|
src/__pycache__/classifier.cpython-313.pyc
ADDED
|
Binary file (6.39 kB). View file
|
|
|
src/__pycache__/decision.cpython-311.pyc
ADDED
|
Binary file (3.52 kB). View file
|
|
|
src/__pycache__/metadata.cpython-311.pyc
ADDED
|
Binary file (1.61 kB). View file
|
|
|
src/__pycache__/models.cpython-311.pyc
ADDED
|
Binary file (3.15 kB). View file
|
|
|
src/__pycache__/physics_generator.cpython-311.pyc
ADDED
|
Binary file (15 kB). View file
|
|
|
src/__pycache__/schemas.cpython-311.pyc
ADDED
|
Binary file (1.86 kB). View file
|
|
|
src/__pycache__/schemas.cpython-313.pyc
ADDED
|
Binary file (1.83 kB). View file
|
|
|
src/analysis_for_physics_generator.ipynb
ADDED
|
@@ -0,0 +1,2770 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"id": "f8aa68cc",
|
| 6 |
+
"metadata": {},
|
| 7 |
+
"source": [
|
| 8 |
+
"# **Physics Generator - Design Philosophy**\n",
|
| 9 |
+
"\n",
|
| 10 |
+
"The physics generator creates synthetic missile engagement scenarios for ATAS.\n",
|
| 11 |
+
"\n",
|
| 12 |
+
"Real engagement data is difficult and unrealistic to obtain at scale, so synthetic data is used to generate controlled training scenarios with known ground-truth labels.\n",
|
| 13 |
+
"\n",
|
| 14 |
+
"The generator uses metadata-derived aircraft capability ranges from `aircraft_metadata.csv` instead of fully hardcoded values.\n"
|
| 15 |
+
]
|
| 16 |
+
},
|
| 17 |
+
{
|
| 18 |
+
"cell_type": "markdown",
|
| 19 |
+
"id": "57887b61",
|
| 20 |
+
"metadata": {},
|
| 21 |
+
"source": [
|
| 22 |
+
"### **aircraft_metadata.csv**\n",
|
| 23 |
+
"\n",
|
| 24 |
+
"This CSV is the threat database for ATAS.\n",
|
| 25 |
+
"\n",
|
| 26 |
+
"The classifier only predicts:\n",
|
| 27 |
+
"```python\n",
|
| 28 |
+
"\"F22\"\n",
|
| 29 |
+
"````\n",
|
| 30 |
+
"\n",
|
| 31 |
+
"The metadata adds tactical information:\n",
|
| 32 |
+
"\n",
|
| 33 |
+
"* missile speed\n",
|
| 34 |
+
"* missile range\n",
|
| 35 |
+
"* aircraft generation\n",
|
| 36 |
+
"* maneuverability\n",
|
| 37 |
+
"* combat capability\n",
|
| 38 |
+
"\n",
|
| 39 |
+
"This allows the system to simulate engagement scenarios.\n",
|
| 40 |
+
"\n",
|
| 41 |
+
"---\n",
|
| 42 |
+
"\n",
|
| 43 |
+
"### Columns\n",
|
| 44 |
+
"\n",
|
| 45 |
+
"#### `aircraft`\n",
|
| 46 |
+
"\n",
|
| 47 |
+
"Aircraft class name.\n",
|
| 48 |
+
"\n",
|
| 49 |
+
"Used to match classifier output with metadata.\n",
|
| 50 |
+
"\n",
|
| 51 |
+
"---\n",
|
| 52 |
+
"\n",
|
| 53 |
+
"#### `missile_speed`\n",
|
| 54 |
+
"\n",
|
| 55 |
+
"Approx missile speed (m/s).\n",
|
| 56 |
+
"\n",
|
| 57 |
+
"Used for:\n",
|
| 58 |
+
"\n",
|
| 59 |
+
"* closure rate\n",
|
| 60 |
+
"* evasion time\n",
|
| 61 |
+
"* threat level\n",
|
| 62 |
+
"\n",
|
| 63 |
+
"Higher speed = less reaction time.\n",
|
| 64 |
+
"\n",
|
| 65 |
+
"---\n",
|
| 66 |
+
"\n",
|
| 67 |
+
"#### `missile_range`\n",
|
| 68 |
+
"\n",
|
| 69 |
+
"Approx max missile range (m).\n",
|
| 70 |
+
"\n",
|
| 71 |
+
"Used for:\n",
|
| 72 |
+
"\n",
|
| 73 |
+
"* launch distance generation\n",
|
| 74 |
+
"* engagement realism\n",
|
| 75 |
+
"\n",
|
| 76 |
+
"Higher range = longer reach.\n",
|
| 77 |
+
"\n",
|
| 78 |
+
"---\n",
|
| 79 |
+
"\n",
|
| 80 |
+
"#### `enemy_generation`\n",
|
| 81 |
+
"\n",
|
| 82 |
+
"Aircraft technology level.\n",
|
| 83 |
+
"\n",
|
| 84 |
+
"Values:\n",
|
| 85 |
+
"\n",
|
| 86 |
+
"* 3.5\n",
|
| 87 |
+
"* 4\n",
|
| 88 |
+
"* 4.5\n",
|
| 89 |
+
"* 5\n",
|
| 90 |
+
"\n",
|
| 91 |
+
"Used for:\n",
|
| 92 |
+
"\n",
|
| 93 |
+
"* threat weighting\n",
|
| 94 |
+
"* hit probability modifiers\n",
|
| 95 |
+
"\n",
|
| 96 |
+
"Higher generation = more dangerous.\n",
|
| 97 |
+
"\n",
|
| 98 |
+
"---\n",
|
| 99 |
+
"\n",
|
| 100 |
+
"#### `maneuverability`\n",
|
| 101 |
+
"\n",
|
| 102 |
+
"Aircraft agility.\n",
|
| 103 |
+
"\n",
|
| 104 |
+
"Values:\n",
|
| 105 |
+
"\n",
|
| 106 |
+
"* 0 = low\n",
|
| 107 |
+
"* 1 = medium\n",
|
| 108 |
+
"* 2 = high\n",
|
| 109 |
+
"\n",
|
| 110 |
+
"Used for:\n",
|
| 111 |
+
"\n",
|
| 112 |
+
"* evasion logic\n",
|
| 113 |
+
"* survival probability\n",
|
| 114 |
+
"\n",
|
| 115 |
+
"Higher maneuverability = harder to hit.\n",
|
| 116 |
+
"\n",
|
| 117 |
+
"---\n",
|
| 118 |
+
"\n",
|
| 119 |
+
"#### `no_aa_capability`\n",
|
| 120 |
+
"\n",
|
| 121 |
+
"Whether aircraft lacks air-to-air combat capability.\n",
|
| 122 |
+
"\n",
|
| 123 |
+
"Values:\n",
|
| 124 |
+
"\n",
|
| 125 |
+
"* 0 = combat capable\n",
|
| 126 |
+
"* 1 = not combat capable\n",
|
| 127 |
+
"\n",
|
| 128 |
+
"Used to:\n",
|
| 129 |
+
"\n",
|
| 130 |
+
"* lower threat score\n",
|
| 131 |
+
"* skip missile logic for support aircraft\n",
|
| 132 |
+
"\n",
|
| 133 |
+
"---\n",
|
| 134 |
+
"\n",
|
| 135 |
+
"## Why This Exists\n",
|
| 136 |
+
"\n",
|
| 137 |
+
"The metadata converts:\n",
|
| 138 |
+
"\n",
|
| 139 |
+
"```python\n",
|
| 140 |
+
"\"What aircraft is this?\"\n",
|
| 141 |
+
"```\n",
|
| 142 |
+
"\n",
|
| 143 |
+
"into:\n",
|
| 144 |
+
"\n",
|
| 145 |
+
"```python\n",
|
| 146 |
+
"\"How dangerous is this aircraft?\"\n",
|
| 147 |
+
"```"
|
| 148 |
+
]
|
| 149 |
+
},
|
| 150 |
+
{
|
| 151 |
+
"cell_type": "code",
|
| 152 |
+
"execution_count": 2,
|
| 153 |
+
"id": "ce0f85d7",
|
| 154 |
+
"metadata": {},
|
| 155 |
+
"outputs": [],
|
| 156 |
+
"source": [
|
| 157 |
+
"# Importing the libraries\n",
|
| 158 |
+
"\n",
|
| 159 |
+
"import pandas as pd\n",
|
| 160 |
+
"import numpy as np"
|
| 161 |
+
]
|
| 162 |
+
},
|
| 163 |
+
{
|
| 164 |
+
"cell_type": "code",
|
| 165 |
+
"execution_count": 3,
|
| 166 |
+
"id": "6831bd71",
|
| 167 |
+
"metadata": {},
|
| 168 |
+
"outputs": [
|
| 169 |
+
{
|
| 170 |
+
"data": {
|
| 171 |
+
"text/html": [
|
| 172 |
+
"<div>\n",
|
| 173 |
+
"<style scoped>\n",
|
| 174 |
+
" .dataframe tbody tr th:only-of-type {\n",
|
| 175 |
+
" vertical-align: middle;\n",
|
| 176 |
+
" }\n",
|
| 177 |
+
"\n",
|
| 178 |
+
" .dataframe tbody tr th {\n",
|
| 179 |
+
" vertical-align: top;\n",
|
| 180 |
+
" }\n",
|
| 181 |
+
"\n",
|
| 182 |
+
" .dataframe thead th {\n",
|
| 183 |
+
" text-align: right;\n",
|
| 184 |
+
" }\n",
|
| 185 |
+
"</style>\n",
|
| 186 |
+
"<table border=\"1\" class=\"dataframe\">\n",
|
| 187 |
+
" <thead>\n",
|
| 188 |
+
" <tr style=\"text-align: right;\">\n",
|
| 189 |
+
" <th></th>\n",
|
| 190 |
+
" <th>aircraft</th>\n",
|
| 191 |
+
" <th>aircraft_max_speed</th>\n",
|
| 192 |
+
" <th>missile_speed</th>\n",
|
| 193 |
+
" <th>missile_range</th>\n",
|
| 194 |
+
" <th>enemy_generation</th>\n",
|
| 195 |
+
" <th>maneuverability</th>\n",
|
| 196 |
+
" <th>no_aa_capability</th>\n",
|
| 197 |
+
" </tr>\n",
|
| 198 |
+
" </thead>\n",
|
| 199 |
+
" <tbody>\n",
|
| 200 |
+
" <tr>\n",
|
| 201 |
+
" <th>0</th>\n",
|
| 202 |
+
" <td>A10</td>\n",
|
| 203 |
+
" <td>222</td>\n",
|
| 204 |
+
" <td>857</td>\n",
|
| 205 |
+
" <td>35000</td>\n",
|
| 206 |
+
" <td>4.0</td>\n",
|
| 207 |
+
" <td>1</td>\n",
|
| 208 |
+
" <td>0</td>\n",
|
| 209 |
+
" </tr>\n",
|
| 210 |
+
" <tr>\n",
|
| 211 |
+
" <th>1</th>\n",
|
| 212 |
+
" <td>A400M</td>\n",
|
| 213 |
+
" <td>255</td>\n",
|
| 214 |
+
" <td>-1</td>\n",
|
| 215 |
+
" <td>-1</td>\n",
|
| 216 |
+
" <td>4.0</td>\n",
|
| 217 |
+
" <td>0</td>\n",
|
| 218 |
+
" <td>1</td>\n",
|
| 219 |
+
" </tr>\n",
|
| 220 |
+
" <tr>\n",
|
| 221 |
+
" <th>2</th>\n",
|
| 222 |
+
" <td>AG600</td>\n",
|
| 223 |
+
" <td>155</td>\n",
|
| 224 |
+
" <td>-1</td>\n",
|
| 225 |
+
" <td>-1</td>\n",
|
| 226 |
+
" <td>4.0</td>\n",
|
| 227 |
+
" <td>0</td>\n",
|
| 228 |
+
" <td>1</td>\n",
|
| 229 |
+
" </tr>\n",
|
| 230 |
+
" <tr>\n",
|
| 231 |
+
" <th>3</th>\n",
|
| 232 |
+
" <td>AH64</td>\n",
|
| 233 |
+
" <td>101</td>\n",
|
| 234 |
+
" <td>750</td>\n",
|
| 235 |
+
" <td>8000</td>\n",
|
| 236 |
+
" <td>4.0</td>\n",
|
| 237 |
+
" <td>1</td>\n",
|
| 238 |
+
" <td>0</td>\n",
|
| 239 |
+
" </tr>\n",
|
| 240 |
+
" <tr>\n",
|
| 241 |
+
" <th>4</th>\n",
|
| 242 |
+
" <td>AKINCI</td>\n",
|
| 243 |
+
" <td>100</td>\n",
|
| 244 |
+
" <td>1372</td>\n",
|
| 245 |
+
" <td>65000</td>\n",
|
| 246 |
+
" <td>4.0</td>\n",
|
| 247 |
+
" <td>1</td>\n",
|
| 248 |
+
" <td>0</td>\n",
|
| 249 |
+
" </tr>\n",
|
| 250 |
+
" <tr>\n",
|
| 251 |
+
" <th>...</th>\n",
|
| 252 |
+
" <td>...</td>\n",
|
| 253 |
+
" <td>...</td>\n",
|
| 254 |
+
" <td>...</td>\n",
|
| 255 |
+
" <td>...</td>\n",
|
| 256 |
+
" <td>...</td>\n",
|
| 257 |
+
" <td>...</td>\n",
|
| 258 |
+
" <td>...</td>\n",
|
| 259 |
+
" </tr>\n",
|
| 260 |
+
" <tr>\n",
|
| 261 |
+
" <th>97</th>\n",
|
| 262 |
+
" <td>Y20</td>\n",
|
| 263 |
+
" <td>255</td>\n",
|
| 264 |
+
" <td>-1</td>\n",
|
| 265 |
+
" <td>-1</td>\n",
|
| 266 |
+
" <td>4.0</td>\n",
|
| 267 |
+
" <td>0</td>\n",
|
| 268 |
+
" <td>1</td>\n",
|
| 269 |
+
" </tr>\n",
|
| 270 |
+
" <tr>\n",
|
| 271 |
+
" <th>98</th>\n",
|
| 272 |
+
" <td>YF23</td>\n",
|
| 273 |
+
" <td>648</td>\n",
|
| 274 |
+
" <td>1372</td>\n",
|
| 275 |
+
" <td>160000</td>\n",
|
| 276 |
+
" <td>5.0</td>\n",
|
| 277 |
+
" <td>2</td>\n",
|
| 278 |
+
" <td>0</td>\n",
|
| 279 |
+
" </tr>\n",
|
| 280 |
+
" <tr>\n",
|
| 281 |
+
" <th>99</th>\n",
|
| 282 |
+
" <td>Z10</td>\n",
|
| 283 |
+
" <td>83</td>\n",
|
| 284 |
+
" <td>686</td>\n",
|
| 285 |
+
" <td>8000</td>\n",
|
| 286 |
+
" <td>4.0</td>\n",
|
| 287 |
+
" <td>1</td>\n",
|
| 288 |
+
" <td>0</td>\n",
|
| 289 |
+
" </tr>\n",
|
| 290 |
+
" <tr>\n",
|
| 291 |
+
" <th>100</th>\n",
|
| 292 |
+
" <td>Z19</td>\n",
|
| 293 |
+
" <td>78</td>\n",
|
| 294 |
+
" <td>686</td>\n",
|
| 295 |
+
" <td>8000</td>\n",
|
| 296 |
+
" <td>4.0</td>\n",
|
| 297 |
+
" <td>1</td>\n",
|
| 298 |
+
" <td>0</td>\n",
|
| 299 |
+
" </tr>\n",
|
| 300 |
+
" <tr>\n",
|
| 301 |
+
" <th>101</th>\n",
|
| 302 |
+
" <td>Z21</td>\n",
|
| 303 |
+
" <td>83</td>\n",
|
| 304 |
+
" <td>686</td>\n",
|
| 305 |
+
" <td>8000</td>\n",
|
| 306 |
+
" <td>4.0</td>\n",
|
| 307 |
+
" <td>1</td>\n",
|
| 308 |
+
" <td>0</td>\n",
|
| 309 |
+
" </tr>\n",
|
| 310 |
+
" </tbody>\n",
|
| 311 |
+
"</table>\n",
|
| 312 |
+
"<p>102 rows × 7 columns</p>\n",
|
| 313 |
+
"</div>"
|
| 314 |
+
],
|
| 315 |
+
"text/plain": [
|
| 316 |
+
" aircraft aircraft_max_speed missile_speed missile_range \\\n",
|
| 317 |
+
"0 A10 222 857 35000 \n",
|
| 318 |
+
"1 A400M 255 -1 -1 \n",
|
| 319 |
+
"2 AG600 155 -1 -1 \n",
|
| 320 |
+
"3 AH64 101 750 8000 \n",
|
| 321 |
+
"4 AKINCI 100 1372 65000 \n",
|
| 322 |
+
".. ... ... ... ... \n",
|
| 323 |
+
"97 Y20 255 -1 -1 \n",
|
| 324 |
+
"98 YF23 648 1372 160000 \n",
|
| 325 |
+
"99 Z10 83 686 8000 \n",
|
| 326 |
+
"100 Z19 78 686 8000 \n",
|
| 327 |
+
"101 Z21 83 686 8000 \n",
|
| 328 |
+
"\n",
|
| 329 |
+
" enemy_generation maneuverability no_aa_capability \n",
|
| 330 |
+
"0 4.0 1 0 \n",
|
| 331 |
+
"1 4.0 0 1 \n",
|
| 332 |
+
"2 4.0 0 1 \n",
|
| 333 |
+
"3 4.0 1 0 \n",
|
| 334 |
+
"4 4.0 1 0 \n",
|
| 335 |
+
".. ... ... ... \n",
|
| 336 |
+
"97 4.0 0 1 \n",
|
| 337 |
+
"98 5.0 2 0 \n",
|
| 338 |
+
"99 4.0 1 0 \n",
|
| 339 |
+
"100 4.0 1 0 \n",
|
| 340 |
+
"101 4.0 1 0 \n",
|
| 341 |
+
"\n",
|
| 342 |
+
"[102 rows x 7 columns]"
|
| 343 |
+
]
|
| 344 |
+
},
|
| 345 |
+
"execution_count": 3,
|
| 346 |
+
"metadata": {},
|
| 347 |
+
"output_type": "execute_result"
|
| 348 |
+
}
|
| 349 |
+
],
|
| 350 |
+
"source": [
|
| 351 |
+
"# Importing the CSV to work with\n",
|
| 352 |
+
"\n",
|
| 353 |
+
"df = pd.read_csv(\"../data/aircraft_metadata.csv\")\n",
|
| 354 |
+
"df"
|
| 355 |
+
]
|
| 356 |
+
},
|
| 357 |
+
{
|
| 358 |
+
"cell_type": "code",
|
| 359 |
+
"execution_count": 4,
|
| 360 |
+
"id": "3ac9444c",
|
| 361 |
+
"metadata": {},
|
| 362 |
+
"outputs": [
|
| 363 |
+
{
|
| 364 |
+
"name": "stdout",
|
| 365 |
+
"output_type": "stream",
|
| 366 |
+
"text": [
|
| 367 |
+
"<class 'pandas.DataFrame'>\n",
|
| 368 |
+
"RangeIndex: 102 entries, 0 to 101\n",
|
| 369 |
+
"Data columns (total 7 columns):\n",
|
| 370 |
+
" # Column Non-Null Count Dtype \n",
|
| 371 |
+
"--- ------ -------------- ----- \n",
|
| 372 |
+
" 0 aircraft 102 non-null str \n",
|
| 373 |
+
" 1 aircraft_max_speed 102 non-null int64 \n",
|
| 374 |
+
" 2 missile_speed 102 non-null int64 \n",
|
| 375 |
+
" 3 missile_range 102 non-null int64 \n",
|
| 376 |
+
" 4 enemy_generation 102 non-null float64\n",
|
| 377 |
+
" 5 maneuverability 102 non-null int64 \n",
|
| 378 |
+
" 6 no_aa_capability 102 non-null int64 \n",
|
| 379 |
+
"dtypes: float64(1), int64(5), str(1)\n",
|
| 380 |
+
"memory usage: 5.7 KB\n"
|
| 381 |
+
]
|
| 382 |
+
}
|
| 383 |
+
],
|
| 384 |
+
"source": [
|
| 385 |
+
"df.info()"
|
| 386 |
+
]
|
| 387 |
+
},
|
| 388 |
+
{
|
| 389 |
+
"cell_type": "code",
|
| 390 |
+
"execution_count": 5,
|
| 391 |
+
"id": "c29a7656",
|
| 392 |
+
"metadata": {},
|
| 393 |
+
"outputs": [
|
| 394 |
+
{
|
| 395 |
+
"data": {
|
| 396 |
+
"text/html": [
|
| 397 |
+
"<div>\n",
|
| 398 |
+
"<style scoped>\n",
|
| 399 |
+
" .dataframe tbody tr th:only-of-type {\n",
|
| 400 |
+
" vertical-align: middle;\n",
|
| 401 |
+
" }\n",
|
| 402 |
+
"\n",
|
| 403 |
+
" .dataframe tbody tr th {\n",
|
| 404 |
+
" vertical-align: top;\n",
|
| 405 |
+
" }\n",
|
| 406 |
+
"\n",
|
| 407 |
+
" .dataframe thead th {\n",
|
| 408 |
+
" text-align: right;\n",
|
| 409 |
+
" }\n",
|
| 410 |
+
"</style>\n",
|
| 411 |
+
"<table border=\"1\" class=\"dataframe\">\n",
|
| 412 |
+
" <thead>\n",
|
| 413 |
+
" <tr style=\"text-align: right;\">\n",
|
| 414 |
+
" <th></th>\n",
|
| 415 |
+
" <th>aircraft</th>\n",
|
| 416 |
+
" <th>aircraft_max_speed</th>\n",
|
| 417 |
+
" <th>missile_speed</th>\n",
|
| 418 |
+
" <th>missile_range</th>\n",
|
| 419 |
+
" <th>enemy_generation</th>\n",
|
| 420 |
+
" <th>maneuverability</th>\n",
|
| 421 |
+
" <th>no_aa_capability</th>\n",
|
| 422 |
+
" </tr>\n",
|
| 423 |
+
" </thead>\n",
|
| 424 |
+
" <tbody>\n",
|
| 425 |
+
" <tr>\n",
|
| 426 |
+
" <th>count</th>\n",
|
| 427 |
+
" <td>102</td>\n",
|
| 428 |
+
" <td>102.000000</td>\n",
|
| 429 |
+
" <td>102.000000</td>\n",
|
| 430 |
+
" <td>102.000000</td>\n",
|
| 431 |
+
" <td>102.000000</td>\n",
|
| 432 |
+
" <td>102.000000</td>\n",
|
| 433 |
+
" <td>102.000000</td>\n",
|
| 434 |
+
" </tr>\n",
|
| 435 |
+
" <tr>\n",
|
| 436 |
+
" <th>unique</th>\n",
|
| 437 |
+
" <td>102</td>\n",
|
| 438 |
+
" <td>NaN</td>\n",
|
| 439 |
+
" <td>NaN</td>\n",
|
| 440 |
+
" <td>NaN</td>\n",
|
| 441 |
+
" <td>NaN</td>\n",
|
| 442 |
+
" <td>NaN</td>\n",
|
| 443 |
+
" <td>NaN</td>\n",
|
| 444 |
+
" </tr>\n",
|
| 445 |
+
" <tr>\n",
|
| 446 |
+
" <th>top</th>\n",
|
| 447 |
+
" <td>A10</td>\n",
|
| 448 |
+
" <td>NaN</td>\n",
|
| 449 |
+
" <td>NaN</td>\n",
|
| 450 |
+
" <td>NaN</td>\n",
|
| 451 |
+
" <td>NaN</td>\n",
|
| 452 |
+
" <td>NaN</td>\n",
|
| 453 |
+
" <td>NaN</td>\n",
|
| 454 |
+
" </tr>\n",
|
| 455 |
+
" <tr>\n",
|
| 456 |
+
" <th>freq</th>\n",
|
| 457 |
+
" <td>1</td>\n",
|
| 458 |
+
" <td>NaN</td>\n",
|
| 459 |
+
" <td>NaN</td>\n",
|
| 460 |
+
" <td>NaN</td>\n",
|
| 461 |
+
" <td>NaN</td>\n",
|
| 462 |
+
" <td>NaN</td>\n",
|
| 463 |
+
" <td>NaN</td>\n",
|
| 464 |
+
" </tr>\n",
|
| 465 |
+
" <tr>\n",
|
| 466 |
+
" <th>mean</th>\n",
|
| 467 |
+
" <td>NaN</td>\n",
|
| 468 |
+
" <td>345.764706</td>\n",
|
| 469 |
+
" <td>636.460784</td>\n",
|
| 470 |
+
" <td>64724.990196</td>\n",
|
| 471 |
+
" <td>4.117647</td>\n",
|
| 472 |
+
" <td>0.852941</td>\n",
|
| 473 |
+
" <td>0.500000</td>\n",
|
| 474 |
+
" </tr>\n",
|
| 475 |
+
" <tr>\n",
|
| 476 |
+
" <th>std</th>\n",
|
| 477 |
+
" <td>NaN</td>\n",
|
| 478 |
+
" <td>226.619242</td>\n",
|
| 479 |
+
" <td>692.537831</td>\n",
|
| 480 |
+
" <td>102267.602554</td>\n",
|
| 481 |
+
" <td>0.478118</td>\n",
|
| 482 |
+
" <td>0.825306</td>\n",
|
| 483 |
+
" <td>0.502469</td>\n",
|
| 484 |
+
" </tr>\n",
|
| 485 |
+
" <tr>\n",
|
| 486 |
+
" <th>min</th>\n",
|
| 487 |
+
" <td>NaN</td>\n",
|
| 488 |
+
" <td>61.000000</td>\n",
|
| 489 |
+
" <td>-1.000000</td>\n",
|
| 490 |
+
" <td>-1.000000</td>\n",
|
| 491 |
+
" <td>3.500000</td>\n",
|
| 492 |
+
" <td>0.000000</td>\n",
|
| 493 |
+
" <td>0.000000</td>\n",
|
| 494 |
+
" </tr>\n",
|
| 495 |
+
" <tr>\n",
|
| 496 |
+
" <th>25%</th>\n",
|
| 497 |
+
" <td>NaN</td>\n",
|
| 498 |
+
" <td>164.500000</td>\n",
|
| 499 |
+
" <td>-1.000000</td>\n",
|
| 500 |
+
" <td>-1.000000</td>\n",
|
| 501 |
+
" <td>4.000000</td>\n",
|
| 502 |
+
" <td>0.000000</td>\n",
|
| 503 |
+
" <td>0.000000</td>\n",
|
| 504 |
+
" </tr>\n",
|
| 505 |
+
" <tr>\n",
|
| 506 |
+
" <th>50%</th>\n",
|
| 507 |
+
" <td>NaN</td>\n",
|
| 508 |
+
" <td>268.000000</td>\n",
|
| 509 |
+
" <td>342.500000</td>\n",
|
| 510 |
+
" <td>3999.500000</td>\n",
|
| 511 |
+
" <td>4.000000</td>\n",
|
| 512 |
+
" <td>1.000000</td>\n",
|
| 513 |
+
" <td>0.500000</td>\n",
|
| 514 |
+
" </tr>\n",
|
| 515 |
+
" <tr>\n",
|
| 516 |
+
" <th>75%</th>\n",
|
| 517 |
+
" <td>NaN</td>\n",
|
| 518 |
+
" <td>544.000000</td>\n",
|
| 519 |
+
" <td>1372.000000</td>\n",
|
| 520 |
+
" <td>108750.000000</td>\n",
|
| 521 |
+
" <td>4.500000</td>\n",
|
| 522 |
+
" <td>2.000000</td>\n",
|
| 523 |
+
" <td>1.000000</td>\n",
|
| 524 |
+
" </tr>\n",
|
| 525 |
+
" <tr>\n",
|
| 526 |
+
" <th>max</th>\n",
|
| 527 |
+
" <td>NaN</td>\n",
|
| 528 |
+
" <td>983.000000</td>\n",
|
| 529 |
+
" <td>2058.000000</td>\n",
|
| 530 |
+
" <td>400000.000000</td>\n",
|
| 531 |
+
" <td>5.000000</td>\n",
|
| 532 |
+
" <td>2.000000</td>\n",
|
| 533 |
+
" <td>1.000000</td>\n",
|
| 534 |
+
" </tr>\n",
|
| 535 |
+
" </tbody>\n",
|
| 536 |
+
"</table>\n",
|
| 537 |
+
"</div>"
|
| 538 |
+
],
|
| 539 |
+
"text/plain": [
|
| 540 |
+
" aircraft aircraft_max_speed missile_speed missile_range \\\n",
|
| 541 |
+
"count 102 102.000000 102.000000 102.000000 \n",
|
| 542 |
+
"unique 102 NaN NaN NaN \n",
|
| 543 |
+
"top A10 NaN NaN NaN \n",
|
| 544 |
+
"freq 1 NaN NaN NaN \n",
|
| 545 |
+
"mean NaN 345.764706 636.460784 64724.990196 \n",
|
| 546 |
+
"std NaN 226.619242 692.537831 102267.602554 \n",
|
| 547 |
+
"min NaN 61.000000 -1.000000 -1.000000 \n",
|
| 548 |
+
"25% NaN 164.500000 -1.000000 -1.000000 \n",
|
| 549 |
+
"50% NaN 268.000000 342.500000 3999.500000 \n",
|
| 550 |
+
"75% NaN 544.000000 1372.000000 108750.000000 \n",
|
| 551 |
+
"max NaN 983.000000 2058.000000 400000.000000 \n",
|
| 552 |
+
"\n",
|
| 553 |
+
" enemy_generation maneuverability no_aa_capability \n",
|
| 554 |
+
"count 102.000000 102.000000 102.000000 \n",
|
| 555 |
+
"unique NaN NaN NaN \n",
|
| 556 |
+
"top NaN NaN NaN \n",
|
| 557 |
+
"freq NaN NaN NaN \n",
|
| 558 |
+
"mean 4.117647 0.852941 0.500000 \n",
|
| 559 |
+
"std 0.478118 0.825306 0.502469 \n",
|
| 560 |
+
"min 3.500000 0.000000 0.000000 \n",
|
| 561 |
+
"25% 4.000000 0.000000 0.000000 \n",
|
| 562 |
+
"50% 4.000000 1.000000 0.500000 \n",
|
| 563 |
+
"75% 4.500000 2.000000 1.000000 \n",
|
| 564 |
+
"max 5.000000 2.000000 1.000000 "
|
| 565 |
+
]
|
| 566 |
+
},
|
| 567 |
+
"execution_count": 5,
|
| 568 |
+
"metadata": {},
|
| 569 |
+
"output_type": "execute_result"
|
| 570 |
+
}
|
| 571 |
+
],
|
| 572 |
+
"source": [
|
| 573 |
+
"df.describe(include=\"all\")"
|
| 574 |
+
]
|
| 575 |
+
},
|
| 576 |
+
{
|
| 577 |
+
"cell_type": "code",
|
| 578 |
+
"execution_count": 6,
|
| 579 |
+
"id": "ce49133d",
|
| 580 |
+
"metadata": {},
|
| 581 |
+
"outputs": [
|
| 582 |
+
{
|
| 583 |
+
"name": "stdout",
|
| 584 |
+
"output_type": "stream",
|
| 585 |
+
"text": [
|
| 586 |
+
"[4. 3.5 4.5 5. ]\n",
|
| 587 |
+
"[1 0 2]\n",
|
| 588 |
+
"[0 1]\n"
|
| 589 |
+
]
|
| 590 |
+
}
|
| 591 |
+
],
|
| 592 |
+
"source": [
|
| 593 |
+
"print(df[\"enemy_generation\"].unique())\n",
|
| 594 |
+
"print(df[\"maneuverability\"].unique())\n",
|
| 595 |
+
"print(df[\"no_aa_capability\"].unique())"
|
| 596 |
+
]
|
| 597 |
+
},
|
| 598 |
+
{
|
| 599 |
+
"cell_type": "code",
|
| 600 |
+
"execution_count": 7,
|
| 601 |
+
"id": "0297a837",
|
| 602 |
+
"metadata": {},
|
| 603 |
+
"outputs": [
|
| 604 |
+
{
|
| 605 |
+
"data": {
|
| 606 |
+
"text/html": [
|
| 607 |
+
"<div>\n",
|
| 608 |
+
"<style scoped>\n",
|
| 609 |
+
" .dataframe tbody tr th:only-of-type {\n",
|
| 610 |
+
" vertical-align: middle;\n",
|
| 611 |
+
" }\n",
|
| 612 |
+
"\n",
|
| 613 |
+
" .dataframe tbody tr th {\n",
|
| 614 |
+
" vertical-align: top;\n",
|
| 615 |
+
" }\n",
|
| 616 |
+
"\n",
|
| 617 |
+
" .dataframe thead th {\n",
|
| 618 |
+
" text-align: right;\n",
|
| 619 |
+
" }\n",
|
| 620 |
+
"</style>\n",
|
| 621 |
+
"<table border=\"1\" class=\"dataframe\">\n",
|
| 622 |
+
" <thead>\n",
|
| 623 |
+
" <tr style=\"text-align: right;\">\n",
|
| 624 |
+
" <th></th>\n",
|
| 625 |
+
" <th>aircraft</th>\n",
|
| 626 |
+
" <th>aircraft_max_speed</th>\n",
|
| 627 |
+
" <th>missile_speed</th>\n",
|
| 628 |
+
" <th>missile_range</th>\n",
|
| 629 |
+
" <th>enemy_generation</th>\n",
|
| 630 |
+
" <th>maneuverability</th>\n",
|
| 631 |
+
" <th>no_aa_capability</th>\n",
|
| 632 |
+
" </tr>\n",
|
| 633 |
+
" </thead>\n",
|
| 634 |
+
" <tbody>\n",
|
| 635 |
+
" <tr>\n",
|
| 636 |
+
" <th>0</th>\n",
|
| 637 |
+
" <td>A10</td>\n",
|
| 638 |
+
" <td>222</td>\n",
|
| 639 |
+
" <td>857</td>\n",
|
| 640 |
+
" <td>35000</td>\n",
|
| 641 |
+
" <td>4.0</td>\n",
|
| 642 |
+
" <td>1</td>\n",
|
| 643 |
+
" <td>0</td>\n",
|
| 644 |
+
" </tr>\n",
|
| 645 |
+
" <tr>\n",
|
| 646 |
+
" <th>1</th>\n",
|
| 647 |
+
" <td>AH64</td>\n",
|
| 648 |
+
" <td>101</td>\n",
|
| 649 |
+
" <td>750</td>\n",
|
| 650 |
+
" <td>8000</td>\n",
|
| 651 |
+
" <td>4.0</td>\n",
|
| 652 |
+
" <td>1</td>\n",
|
| 653 |
+
" <td>0</td>\n",
|
| 654 |
+
" </tr>\n",
|
| 655 |
+
" <tr>\n",
|
| 656 |
+
" <th>2</th>\n",
|
| 657 |
+
" <td>AKINCI</td>\n",
|
| 658 |
+
" <td>100</td>\n",
|
| 659 |
+
" <td>1372</td>\n",
|
| 660 |
+
" <td>65000</td>\n",
|
| 661 |
+
" <td>4.0</td>\n",
|
| 662 |
+
" <td>1</td>\n",
|
| 663 |
+
" <td>0</td>\n",
|
| 664 |
+
" </tr>\n",
|
| 665 |
+
" <tr>\n",
|
| 666 |
+
" <th>3</th>\n",
|
| 667 |
+
" <td>AV8B</td>\n",
|
| 668 |
+
" <td>300</td>\n",
|
| 669 |
+
" <td>1372</td>\n",
|
| 670 |
+
" <td>160000</td>\n",
|
| 671 |
+
" <td>4.0</td>\n",
|
| 672 |
+
" <td>1</td>\n",
|
| 673 |
+
" <td>0</td>\n",
|
| 674 |
+
" </tr>\n",
|
| 675 |
+
" <tr>\n",
|
| 676 |
+
" <th>4</th>\n",
|
| 677 |
+
" <td>EF2000</td>\n",
|
| 678 |
+
" <td>590</td>\n",
|
| 679 |
+
" <td>1372</td>\n",
|
| 680 |
+
" <td>200000</td>\n",
|
| 681 |
+
" <td>4.5</td>\n",
|
| 682 |
+
" <td>2</td>\n",
|
| 683 |
+
" <td>0</td>\n",
|
| 684 |
+
" </tr>\n",
|
| 685 |
+
" </tbody>\n",
|
| 686 |
+
"</table>\n",
|
| 687 |
+
"</div>"
|
| 688 |
+
],
|
| 689 |
+
"text/plain": [
|
| 690 |
+
" aircraft aircraft_max_speed missile_speed missile_range \\\n",
|
| 691 |
+
"0 A10 222 857 35000 \n",
|
| 692 |
+
"1 AH64 101 750 8000 \n",
|
| 693 |
+
"2 AKINCI 100 1372 65000 \n",
|
| 694 |
+
"3 AV8B 300 1372 160000 \n",
|
| 695 |
+
"4 EF2000 590 1372 200000 \n",
|
| 696 |
+
"\n",
|
| 697 |
+
" enemy_generation maneuverability no_aa_capability \n",
|
| 698 |
+
"0 4.0 1 0 \n",
|
| 699 |
+
"1 4.0 1 0 \n",
|
| 700 |
+
"2 4.0 1 0 \n",
|
| 701 |
+
"3 4.0 1 0 \n",
|
| 702 |
+
"4 4.5 2 0 "
|
| 703 |
+
]
|
| 704 |
+
},
|
| 705 |
+
"execution_count": 7,
|
| 706 |
+
"metadata": {},
|
| 707 |
+
"output_type": "execute_result"
|
| 708 |
+
}
|
| 709 |
+
],
|
| 710 |
+
"source": [
|
| 711 |
+
"# Getting data of aircarfts that has air-to-air combact ability\n",
|
| 712 |
+
"combat_df = df[df[\"no_aa_capability\"]==0].reset_index(drop=True)\n",
|
| 713 |
+
"combat_df.head()"
|
| 714 |
+
]
|
| 715 |
+
},
|
| 716 |
+
{
|
| 717 |
+
"cell_type": "code",
|
| 718 |
+
"execution_count": 8,
|
| 719 |
+
"id": "c578c9c1",
|
| 720 |
+
"metadata": {},
|
| 721 |
+
"outputs": [
|
| 722 |
+
{
|
| 723 |
+
"name": "stdout",
|
| 724 |
+
"output_type": "stream",
|
| 725 |
+
"text": [
|
| 726 |
+
"<class 'pandas.DataFrame'>\n",
|
| 727 |
+
"RangeIndex: 51 entries, 0 to 50\n",
|
| 728 |
+
"Data columns (total 7 columns):\n",
|
| 729 |
+
" # Column Non-Null Count Dtype \n",
|
| 730 |
+
"--- ------ -------------- ----- \n",
|
| 731 |
+
" 0 aircraft 51 non-null str \n",
|
| 732 |
+
" 1 aircraft_max_speed 51 non-null int64 \n",
|
| 733 |
+
" 2 missile_speed 51 non-null int64 \n",
|
| 734 |
+
" 3 missile_range 51 non-null int64 \n",
|
| 735 |
+
" 4 enemy_generation 51 non-null float64\n",
|
| 736 |
+
" 5 maneuverability 51 non-null int64 \n",
|
| 737 |
+
" 6 no_aa_capability 51 non-null int64 \n",
|
| 738 |
+
"dtypes: float64(1), int64(5), str(1)\n",
|
| 739 |
+
"memory usage: 2.9 KB\n"
|
| 740 |
+
]
|
| 741 |
+
}
|
| 742 |
+
],
|
| 743 |
+
"source": [
|
| 744 |
+
"combat_df.info()"
|
| 745 |
+
]
|
| 746 |
+
},
|
| 747 |
+
{
|
| 748 |
+
"cell_type": "code",
|
| 749 |
+
"execution_count": 9,
|
| 750 |
+
"id": "2dc32a13",
|
| 751 |
+
"metadata": {},
|
| 752 |
+
"outputs": [
|
| 753 |
+
{
|
| 754 |
+
"data": {
|
| 755 |
+
"text/html": [
|
| 756 |
+
"<div>\n",
|
| 757 |
+
"<style scoped>\n",
|
| 758 |
+
" .dataframe tbody tr th:only-of-type {\n",
|
| 759 |
+
" vertical-align: middle;\n",
|
| 760 |
+
" }\n",
|
| 761 |
+
"\n",
|
| 762 |
+
" .dataframe tbody tr th {\n",
|
| 763 |
+
" vertical-align: top;\n",
|
| 764 |
+
" }\n",
|
| 765 |
+
"\n",
|
| 766 |
+
" .dataframe thead th {\n",
|
| 767 |
+
" text-align: right;\n",
|
| 768 |
+
" }\n",
|
| 769 |
+
"</style>\n",
|
| 770 |
+
"<table border=\"1\" class=\"dataframe\">\n",
|
| 771 |
+
" <thead>\n",
|
| 772 |
+
" <tr style=\"text-align: right;\">\n",
|
| 773 |
+
" <th></th>\n",
|
| 774 |
+
" <th>aircraft</th>\n",
|
| 775 |
+
" <th>aircraft_max_speed</th>\n",
|
| 776 |
+
" <th>missile_speed</th>\n",
|
| 777 |
+
" <th>missile_range</th>\n",
|
| 778 |
+
" <th>enemy_generation</th>\n",
|
| 779 |
+
" <th>maneuverability</th>\n",
|
| 780 |
+
" <th>no_aa_capability</th>\n",
|
| 781 |
+
" </tr>\n",
|
| 782 |
+
" </thead>\n",
|
| 783 |
+
" <tbody>\n",
|
| 784 |
+
" <tr>\n",
|
| 785 |
+
" <th>count</th>\n",
|
| 786 |
+
" <td>51</td>\n",
|
| 787 |
+
" <td>51.000000</td>\n",
|
| 788 |
+
" <td>51.000000</td>\n",
|
| 789 |
+
" <td>51.000000</td>\n",
|
| 790 |
+
" <td>51.000000</td>\n",
|
| 791 |
+
" <td>51.000000</td>\n",
|
| 792 |
+
" <td>51.0</td>\n",
|
| 793 |
+
" </tr>\n",
|
| 794 |
+
" <tr>\n",
|
| 795 |
+
" <th>unique</th>\n",
|
| 796 |
+
" <td>51</td>\n",
|
| 797 |
+
" <td>NaN</td>\n",
|
| 798 |
+
" <td>NaN</td>\n",
|
| 799 |
+
" <td>NaN</td>\n",
|
| 800 |
+
" <td>NaN</td>\n",
|
| 801 |
+
" <td>NaN</td>\n",
|
| 802 |
+
" <td>NaN</td>\n",
|
| 803 |
+
" </tr>\n",
|
| 804 |
+
" <tr>\n",
|
| 805 |
+
" <th>top</th>\n",
|
| 806 |
+
" <td>A10</td>\n",
|
| 807 |
+
" <td>NaN</td>\n",
|
| 808 |
+
" <td>NaN</td>\n",
|
| 809 |
+
" <td>NaN</td>\n",
|
| 810 |
+
" <td>NaN</td>\n",
|
| 811 |
+
" <td>NaN</td>\n",
|
| 812 |
+
" <td>NaN</td>\n",
|
| 813 |
+
" </tr>\n",
|
| 814 |
+
" <tr>\n",
|
| 815 |
+
" <th>freq</th>\n",
|
| 816 |
+
" <td>1</td>\n",
|
| 817 |
+
" <td>NaN</td>\n",
|
| 818 |
+
" <td>NaN</td>\n",
|
| 819 |
+
" <td>NaN</td>\n",
|
| 820 |
+
" <td>NaN</td>\n",
|
| 821 |
+
" <td>NaN</td>\n",
|
| 822 |
+
" <td>NaN</td>\n",
|
| 823 |
+
" </tr>\n",
|
| 824 |
+
" <tr>\n",
|
| 825 |
+
" <th>mean</th>\n",
|
| 826 |
+
" <td>NaN</td>\n",
|
| 827 |
+
" <td>440.235294</td>\n",
|
| 828 |
+
" <td>1273.921569</td>\n",
|
| 829 |
+
" <td>129450.980392</td>\n",
|
| 830 |
+
" <td>4.284314</td>\n",
|
| 831 |
+
" <td>1.529412</td>\n",
|
| 832 |
+
" <td>0.0</td>\n",
|
| 833 |
+
" </tr>\n",
|
| 834 |
+
" <tr>\n",
|
| 835 |
+
" <th>std</th>\n",
|
| 836 |
+
" <td>NaN</td>\n",
|
| 837 |
+
" <td>225.509609</td>\n",
|
| 838 |
+
" <td>373.955497</td>\n",
|
| 839 |
+
" <td>112160.655085</td>\n",
|
| 840 |
+
" <td>0.461030</td>\n",
|
| 841 |
+
" <td>0.504101</td>\n",
|
| 842 |
+
" <td>0.0</td>\n",
|
| 843 |
+
" </tr>\n",
|
| 844 |
+
" <tr>\n",
|
| 845 |
+
" <th>min</th>\n",
|
| 846 |
+
" <td>NaN</td>\n",
|
| 847 |
+
" <td>78.000000</td>\n",
|
| 848 |
+
" <td>686.000000</td>\n",
|
| 849 |
+
" <td>8000.000000</td>\n",
|
| 850 |
+
" <td>3.500000</td>\n",
|
| 851 |
+
" <td>1.000000</td>\n",
|
| 852 |
+
" <td>0.0</td>\n",
|
| 853 |
+
" </tr>\n",
|
| 854 |
+
" <tr>\n",
|
| 855 |
+
" <th>25%</th>\n",
|
| 856 |
+
" <td>NaN</td>\n",
|
| 857 |
+
" <td>246.500000</td>\n",
|
| 858 |
+
" <td>857.000000</td>\n",
|
| 859 |
+
" <td>37500.000000</td>\n",
|
| 860 |
+
" <td>4.000000</td>\n",
|
| 861 |
+
" <td>1.000000</td>\n",
|
| 862 |
+
" <td>0.0</td>\n",
|
| 863 |
+
" </tr>\n",
|
| 864 |
+
" <tr>\n",
|
| 865 |
+
" <th>50%</th>\n",
|
| 866 |
+
" <td>NaN</td>\n",
|
| 867 |
+
" <td>532.000000</td>\n",
|
| 868 |
+
" <td>1372.000000</td>\n",
|
| 869 |
+
" <td>110000.000000</td>\n",
|
| 870 |
+
" <td>4.000000</td>\n",
|
| 871 |
+
" <td>2.000000</td>\n",
|
| 872 |
+
" <td>0.0</td>\n",
|
| 873 |
+
" </tr>\n",
|
| 874 |
+
" <tr>\n",
|
| 875 |
+
" <th>75%</th>\n",
|
| 876 |
+
" <td>NaN</td>\n",
|
| 877 |
+
" <td>590.000000</td>\n",
|
| 878 |
+
" <td>1372.000000</td>\n",
|
| 879 |
+
" <td>160000.000000</td>\n",
|
| 880 |
+
" <td>4.500000</td>\n",
|
| 881 |
+
" <td>2.000000</td>\n",
|
| 882 |
+
" <td>0.0</td>\n",
|
| 883 |
+
" </tr>\n",
|
| 884 |
+
" <tr>\n",
|
| 885 |
+
" <th>max</th>\n",
|
| 886 |
+
" <td>NaN</td>\n",
|
| 887 |
+
" <td>833.000000</td>\n",
|
| 888 |
+
" <td>2058.000000</td>\n",
|
| 889 |
+
" <td>400000.000000</td>\n",
|
| 890 |
+
" <td>5.000000</td>\n",
|
| 891 |
+
" <td>2.000000</td>\n",
|
| 892 |
+
" <td>0.0</td>\n",
|
| 893 |
+
" </tr>\n",
|
| 894 |
+
" </tbody>\n",
|
| 895 |
+
"</table>\n",
|
| 896 |
+
"</div>"
|
| 897 |
+
],
|
| 898 |
+
"text/plain": [
|
| 899 |
+
" aircraft aircraft_max_speed missile_speed missile_range \\\n",
|
| 900 |
+
"count 51 51.000000 51.000000 51.000000 \n",
|
| 901 |
+
"unique 51 NaN NaN NaN \n",
|
| 902 |
+
"top A10 NaN NaN NaN \n",
|
| 903 |
+
"freq 1 NaN NaN NaN \n",
|
| 904 |
+
"mean NaN 440.235294 1273.921569 129450.980392 \n",
|
| 905 |
+
"std NaN 225.509609 373.955497 112160.655085 \n",
|
| 906 |
+
"min NaN 78.000000 686.000000 8000.000000 \n",
|
| 907 |
+
"25% NaN 246.500000 857.000000 37500.000000 \n",
|
| 908 |
+
"50% NaN 532.000000 1372.000000 110000.000000 \n",
|
| 909 |
+
"75% NaN 590.000000 1372.000000 160000.000000 \n",
|
| 910 |
+
"max NaN 833.000000 2058.000000 400000.000000 \n",
|
| 911 |
+
"\n",
|
| 912 |
+
" enemy_generation maneuverability no_aa_capability \n",
|
| 913 |
+
"count 51.000000 51.000000 51.0 \n",
|
| 914 |
+
"unique NaN NaN NaN \n",
|
| 915 |
+
"top NaN NaN NaN \n",
|
| 916 |
+
"freq NaN NaN NaN \n",
|
| 917 |
+
"mean 4.284314 1.529412 0.0 \n",
|
| 918 |
+
"std 0.461030 0.504101 0.0 \n",
|
| 919 |
+
"min 3.500000 1.000000 0.0 \n",
|
| 920 |
+
"25% 4.000000 1.000000 0.0 \n",
|
| 921 |
+
"50% 4.000000 2.000000 0.0 \n",
|
| 922 |
+
"75% 4.500000 2.000000 0.0 \n",
|
| 923 |
+
"max 5.000000 2.000000 0.0 "
|
| 924 |
+
]
|
| 925 |
+
},
|
| 926 |
+
"execution_count": 9,
|
| 927 |
+
"metadata": {},
|
| 928 |
+
"output_type": "execute_result"
|
| 929 |
+
}
|
| 930 |
+
],
|
| 931 |
+
"source": [
|
| 932 |
+
"combat_df.describe(include=\"all\")"
|
| 933 |
+
]
|
| 934 |
+
},
|
| 935 |
+
{
|
| 936 |
+
"cell_type": "code",
|
| 937 |
+
"execution_count": 10,
|
| 938 |
+
"id": "b51a060f",
|
| 939 |
+
"metadata": {},
|
| 940 |
+
"outputs": [
|
| 941 |
+
{
|
| 942 |
+
"name": "stdout",
|
| 943 |
+
"output_type": "stream",
|
| 944 |
+
"text": [
|
| 945 |
+
"Missile Speed ranges: \n",
|
| 946 |
+
"\n",
|
| 947 |
+
"Max → 2058 \n",
|
| 948 |
+
"Min → 686\n"
|
| 949 |
+
]
|
| 950 |
+
}
|
| 951 |
+
],
|
| 952 |
+
"source": [
|
| 953 |
+
"# Getting missile speed ranges\n",
|
| 954 |
+
"print(\"Missile Speed ranges: \\n\")\n",
|
| 955 |
+
"print(f\"Max → {combat_df['missile_speed'].max()} \\nMin → {combat_df['missile_speed'].min()}\")"
|
| 956 |
+
]
|
| 957 |
+
},
|
| 958 |
+
{
|
| 959 |
+
"cell_type": "code",
|
| 960 |
+
"execution_count": 11,
|
| 961 |
+
"id": "d76aabf2",
|
| 962 |
+
"metadata": {},
|
| 963 |
+
"outputs": [
|
| 964 |
+
{
|
| 965 |
+
"name": "stdout",
|
| 966 |
+
"output_type": "stream",
|
| 967 |
+
"text": [
|
| 968 |
+
"Missile range: \n",
|
| 969 |
+
"\n",
|
| 970 |
+
"Max → 400000 \n",
|
| 971 |
+
"Min → 8000\n"
|
| 972 |
+
]
|
| 973 |
+
}
|
| 974 |
+
],
|
| 975 |
+
"source": [
|
| 976 |
+
"# Getting missile speed ranges\n",
|
| 977 |
+
"print(\"Missile range: \\n\")\n",
|
| 978 |
+
"print(f\"Max → {combat_df['missile_range'].max()} \\nMin → {combat_df['missile_range'].min()}\")"
|
| 979 |
+
]
|
| 980 |
+
},
|
| 981 |
+
{
|
| 982 |
+
"cell_type": "code",
|
| 983 |
+
"execution_count": 12,
|
| 984 |
+
"id": "1e7207e0",
|
| 985 |
+
"metadata": {},
|
| 986 |
+
"outputs": [
|
| 987 |
+
{
|
| 988 |
+
"name": "stdout",
|
| 989 |
+
"output_type": "stream",
|
| 990 |
+
"text": [
|
| 991 |
+
"Aircraft speed range: \n",
|
| 992 |
+
"\n",
|
| 993 |
+
"Max → 983 \n",
|
| 994 |
+
"Min → 61\n"
|
| 995 |
+
]
|
| 996 |
+
}
|
| 997 |
+
],
|
| 998 |
+
"source": [
|
| 999 |
+
"# Getting aircraft speed ranges\n",
|
| 1000 |
+
"print(\"Aircraft speed range: \\n\")\n",
|
| 1001 |
+
"print(f\"Max → {df['aircraft_max_speed'].max()} \\nMin → {df['aircraft_max_speed'].min()}\")"
|
| 1002 |
+
]
|
| 1003 |
+
},
|
| 1004 |
+
{
|
| 1005 |
+
"cell_type": "code",
|
| 1006 |
+
"execution_count": 13,
|
| 1007 |
+
"id": "21a1d9d3",
|
| 1008 |
+
"metadata": {},
|
| 1009 |
+
"outputs": [
|
| 1010 |
+
{
|
| 1011 |
+
"data": {
|
| 1012 |
+
"text/plain": [
|
| 1013 |
+
"enemy_generation\n",
|
| 1014 |
+
"4.0 25\n",
|
| 1015 |
+
"4.5 11\n",
|
| 1016 |
+
"5.0 11\n",
|
| 1017 |
+
"3.5 4\n",
|
| 1018 |
+
"Name: count, dtype: int64"
|
| 1019 |
+
]
|
| 1020 |
+
},
|
| 1021 |
+
"execution_count": 13,
|
| 1022 |
+
"metadata": {},
|
| 1023 |
+
"output_type": "execute_result"
|
| 1024 |
+
}
|
| 1025 |
+
],
|
| 1026 |
+
"source": [
|
| 1027 |
+
"# Getting the number of unique generation\n",
|
| 1028 |
+
"combat_df[\"enemy_generation\"].value_counts()"
|
| 1029 |
+
]
|
| 1030 |
+
},
|
| 1031 |
+
{
|
| 1032 |
+
"cell_type": "code",
|
| 1033 |
+
"execution_count": 14,
|
| 1034 |
+
"id": "d47323a4",
|
| 1035 |
+
"metadata": {},
|
| 1036 |
+
"outputs": [
|
| 1037 |
+
{
|
| 1038 |
+
"data": {
|
| 1039 |
+
"text/plain": [
|
| 1040 |
+
"maneuverability\n",
|
| 1041 |
+
"2 27\n",
|
| 1042 |
+
"1 24\n",
|
| 1043 |
+
"Name: count, dtype: int64"
|
| 1044 |
+
]
|
| 1045 |
+
},
|
| 1046 |
+
"execution_count": 14,
|
| 1047 |
+
"metadata": {},
|
| 1048 |
+
"output_type": "execute_result"
|
| 1049 |
+
}
|
| 1050 |
+
],
|
| 1051 |
+
"source": [
|
| 1052 |
+
"# Getting the maneverability level\n",
|
| 1053 |
+
"combat_df[\"maneuverability\"].value_counts()"
|
| 1054 |
+
]
|
| 1055 |
+
},
|
| 1056 |
+
{
|
| 1057 |
+
"cell_type": "code",
|
| 1058 |
+
"execution_count": 15,
|
| 1059 |
+
"id": "7471829e",
|
| 1060 |
+
"metadata": {},
|
| 1061 |
+
"outputs": [
|
| 1062 |
+
{
|
| 1063 |
+
"data": {
|
| 1064 |
+
"text/html": [
|
| 1065 |
+
"<div>\n",
|
| 1066 |
+
"<style scoped>\n",
|
| 1067 |
+
" .dataframe tbody tr th:only-of-type {\n",
|
| 1068 |
+
" vertical-align: middle;\n",
|
| 1069 |
+
" }\n",
|
| 1070 |
+
"\n",
|
| 1071 |
+
" .dataframe tbody tr th {\n",
|
| 1072 |
+
" vertical-align: top;\n",
|
| 1073 |
+
" }\n",
|
| 1074 |
+
"\n",
|
| 1075 |
+
" .dataframe thead th {\n",
|
| 1076 |
+
" text-align: right;\n",
|
| 1077 |
+
" }\n",
|
| 1078 |
+
"</style>\n",
|
| 1079 |
+
"<table border=\"1\" class=\"dataframe\">\n",
|
| 1080 |
+
" <thead>\n",
|
| 1081 |
+
" <tr style=\"text-align: right;\">\n",
|
| 1082 |
+
" <th></th>\n",
|
| 1083 |
+
" <th>aircraft</th>\n",
|
| 1084 |
+
" <th>aircraft_max_speed</th>\n",
|
| 1085 |
+
" <th>missile_speed</th>\n",
|
| 1086 |
+
" <th>missile_range</th>\n",
|
| 1087 |
+
" <th>enemy_generation</th>\n",
|
| 1088 |
+
" <th>maneuverability</th>\n",
|
| 1089 |
+
" <th>no_aa_capability</th>\n",
|
| 1090 |
+
" </tr>\n",
|
| 1091 |
+
" </thead>\n",
|
| 1092 |
+
" <tbody>\n",
|
| 1093 |
+
" <tr>\n",
|
| 1094 |
+
" <th>14</th>\n",
|
| 1095 |
+
" <td>FCK1</td>\n",
|
| 1096 |
+
" <td>532</td>\n",
|
| 1097 |
+
" <td>2058</td>\n",
|
| 1098 |
+
" <td>100000</td>\n",
|
| 1099 |
+
" <td>4.0</td>\n",
|
| 1100 |
+
" <td>2</td>\n",
|
| 1101 |
+
" <td>0</td>\n",
|
| 1102 |
+
" </tr>\n",
|
| 1103 |
+
" <tr>\n",
|
| 1104 |
+
" <th>39</th>\n",
|
| 1105 |
+
" <td>Su57</td>\n",
|
| 1106 |
+
" <td>590</td>\n",
|
| 1107 |
+
" <td>2058</td>\n",
|
| 1108 |
+
" <td>400000</td>\n",
|
| 1109 |
+
" <td>5.0</td>\n",
|
| 1110 |
+
" <td>2</td>\n",
|
| 1111 |
+
" <td>0</td>\n",
|
| 1112 |
+
" </tr>\n",
|
| 1113 |
+
" <tr>\n",
|
| 1114 |
+
" <th>32</th>\n",
|
| 1115 |
+
" <td>Mig31</td>\n",
|
| 1116 |
+
" <td>833</td>\n",
|
| 1117 |
+
" <td>2058</td>\n",
|
| 1118 |
+
" <td>400000</td>\n",
|
| 1119 |
+
" <td>4.0</td>\n",
|
| 1120 |
+
" <td>1</td>\n",
|
| 1121 |
+
" <td>0</td>\n",
|
| 1122 |
+
" </tr>\n",
|
| 1123 |
+
" <tr>\n",
|
| 1124 |
+
" <th>18</th>\n",
|
| 1125 |
+
" <td>J36</td>\n",
|
| 1126 |
+
" <td>590</td>\n",
|
| 1127 |
+
" <td>1715</td>\n",
|
| 1128 |
+
" <td>400000</td>\n",
|
| 1129 |
+
" <td>5.0</td>\n",
|
| 1130 |
+
" <td>2</td>\n",
|
| 1131 |
+
" <td>0</td>\n",
|
| 1132 |
+
" </tr>\n",
|
| 1133 |
+
" <tr>\n",
|
| 1134 |
+
" <th>19</th>\n",
|
| 1135 |
+
" <td>J50</td>\n",
|
| 1136 |
+
" <td>590</td>\n",
|
| 1137 |
+
" <td>1715</td>\n",
|
| 1138 |
+
" <td>400000</td>\n",
|
| 1139 |
+
" <td>5.0</td>\n",
|
| 1140 |
+
" <td>2</td>\n",
|
| 1141 |
+
" <td>0</td>\n",
|
| 1142 |
+
" </tr>\n",
|
| 1143 |
+
" <tr>\n",
|
| 1144 |
+
" <th>17</th>\n",
|
| 1145 |
+
" <td>J35</td>\n",
|
| 1146 |
+
" <td>590</td>\n",
|
| 1147 |
+
" <td>1715</td>\n",
|
| 1148 |
+
" <td>300000</td>\n",
|
| 1149 |
+
" <td>5.0</td>\n",
|
| 1150 |
+
" <td>2</td>\n",
|
| 1151 |
+
" <td>0</td>\n",
|
| 1152 |
+
" </tr>\n",
|
| 1153 |
+
" <tr>\n",
|
| 1154 |
+
" <th>16</th>\n",
|
| 1155 |
+
" <td>J20</td>\n",
|
| 1156 |
+
" <td>590</td>\n",
|
| 1157 |
+
" <td>1715</td>\n",
|
| 1158 |
+
" <td>300000</td>\n",
|
| 1159 |
+
" <td>5.0</td>\n",
|
| 1160 |
+
" <td>2</td>\n",
|
| 1161 |
+
" <td>0</td>\n",
|
| 1162 |
+
" </tr>\n",
|
| 1163 |
+
" <tr>\n",
|
| 1164 |
+
" <th>15</th>\n",
|
| 1165 |
+
" <td>J10</td>\n",
|
| 1166 |
+
" <td>650</td>\n",
|
| 1167 |
+
" <td>1715</td>\n",
|
| 1168 |
+
" <td>300000</td>\n",
|
| 1169 |
+
" <td>4.5</td>\n",
|
| 1170 |
+
" <td>2</td>\n",
|
| 1171 |
+
" <td>0</td>\n",
|
| 1172 |
+
" </tr>\n",
|
| 1173 |
+
" <tr>\n",
|
| 1174 |
+
" <th>10</th>\n",
|
| 1175 |
+
" <td>F2</td>\n",
|
| 1176 |
+
" <td>590</td>\n",
|
| 1177 |
+
" <td>1544</td>\n",
|
| 1178 |
+
" <td>105000</td>\n",
|
| 1179 |
+
" <td>4.5</td>\n",
|
| 1180 |
+
" <td>2</td>\n",
|
| 1181 |
+
" <td>0</td>\n",
|
| 1182 |
+
" </tr>\n",
|
| 1183 |
+
" <tr>\n",
|
| 1184 |
+
" <th>41</th>\n",
|
| 1185 |
+
" <td>Tejas</td>\n",
|
| 1186 |
+
" <td>550</td>\n",
|
| 1187 |
+
" <td>1544</td>\n",
|
| 1188 |
+
" <td>110000</td>\n",
|
| 1189 |
+
" <td>4.5</td>\n",
|
| 1190 |
+
" <td>2</td>\n",
|
| 1191 |
+
" <td>0</td>\n",
|
| 1192 |
+
" </tr>\n",
|
| 1193 |
+
" </tbody>\n",
|
| 1194 |
+
"</table>\n",
|
| 1195 |
+
"</div>"
|
| 1196 |
+
],
|
| 1197 |
+
"text/plain": [
|
| 1198 |
+
" aircraft aircraft_max_speed missile_speed missile_range \\\n",
|
| 1199 |
+
"14 FCK1 532 2058 100000 \n",
|
| 1200 |
+
"39 Su57 590 2058 400000 \n",
|
| 1201 |
+
"32 Mig31 833 2058 400000 \n",
|
| 1202 |
+
"18 J36 590 1715 400000 \n",
|
| 1203 |
+
"19 J50 590 1715 400000 \n",
|
| 1204 |
+
"17 J35 590 1715 300000 \n",
|
| 1205 |
+
"16 J20 590 1715 300000 \n",
|
| 1206 |
+
"15 J10 650 1715 300000 \n",
|
| 1207 |
+
"10 F2 590 1544 105000 \n",
|
| 1208 |
+
"41 Tejas 550 1544 110000 \n",
|
| 1209 |
+
"\n",
|
| 1210 |
+
" enemy_generation maneuverability no_aa_capability \n",
|
| 1211 |
+
"14 4.0 2 0 \n",
|
| 1212 |
+
"39 5.0 2 0 \n",
|
| 1213 |
+
"32 4.0 1 0 \n",
|
| 1214 |
+
"18 5.0 2 0 \n",
|
| 1215 |
+
"19 5.0 2 0 \n",
|
| 1216 |
+
"17 5.0 2 0 \n",
|
| 1217 |
+
"16 5.0 2 0 \n",
|
| 1218 |
+
"15 4.5 2 0 \n",
|
| 1219 |
+
"10 4.5 2 0 \n",
|
| 1220 |
+
"41 4.5 2 0 "
|
| 1221 |
+
]
|
| 1222 |
+
},
|
| 1223 |
+
"execution_count": 15,
|
| 1224 |
+
"metadata": {},
|
| 1225 |
+
"output_type": "execute_result"
|
| 1226 |
+
}
|
| 1227 |
+
],
|
| 1228 |
+
"source": [
|
| 1229 |
+
"# get values of ranges of missile speed\n",
|
| 1230 |
+
"combat_df.sort_values(\"missile_speed\", ascending=False).head(10)"
|
| 1231 |
+
]
|
| 1232 |
+
},
|
| 1233 |
+
{
|
| 1234 |
+
"cell_type": "code",
|
| 1235 |
+
"execution_count": 16,
|
| 1236 |
+
"id": "f14cb2cf",
|
| 1237 |
+
"metadata": {},
|
| 1238 |
+
"outputs": [
|
| 1239 |
+
{
|
| 1240 |
+
"data": {
|
| 1241 |
+
"text/html": [
|
| 1242 |
+
"<div>\n",
|
| 1243 |
+
"<style scoped>\n",
|
| 1244 |
+
" .dataframe tbody tr th:only-of-type {\n",
|
| 1245 |
+
" vertical-align: middle;\n",
|
| 1246 |
+
" }\n",
|
| 1247 |
+
"\n",
|
| 1248 |
+
" .dataframe tbody tr th {\n",
|
| 1249 |
+
" vertical-align: top;\n",
|
| 1250 |
+
" }\n",
|
| 1251 |
+
"\n",
|
| 1252 |
+
" .dataframe thead th {\n",
|
| 1253 |
+
" text-align: right;\n",
|
| 1254 |
+
" }\n",
|
| 1255 |
+
"</style>\n",
|
| 1256 |
+
"<table border=\"1\" class=\"dataframe\">\n",
|
| 1257 |
+
" <thead>\n",
|
| 1258 |
+
" <tr style=\"text-align: right;\">\n",
|
| 1259 |
+
" <th></th>\n",
|
| 1260 |
+
" <th>aircraft</th>\n",
|
| 1261 |
+
" <th>aircraft_max_speed</th>\n",
|
| 1262 |
+
" <th>missile_speed</th>\n",
|
| 1263 |
+
" <th>missile_range</th>\n",
|
| 1264 |
+
" <th>enemy_generation</th>\n",
|
| 1265 |
+
" <th>maneuverability</th>\n",
|
| 1266 |
+
" <th>no_aa_capability</th>\n",
|
| 1267 |
+
" </tr>\n",
|
| 1268 |
+
" </thead>\n",
|
| 1269 |
+
" <tbody>\n",
|
| 1270 |
+
" <tr>\n",
|
| 1271 |
+
" <th>19</th>\n",
|
| 1272 |
+
" <td>J50</td>\n",
|
| 1273 |
+
" <td>590</td>\n",
|
| 1274 |
+
" <td>1715</td>\n",
|
| 1275 |
+
" <td>400000</td>\n",
|
| 1276 |
+
" <td>5.0</td>\n",
|
| 1277 |
+
" <td>2</td>\n",
|
| 1278 |
+
" <td>0</td>\n",
|
| 1279 |
+
" </tr>\n",
|
| 1280 |
+
" <tr>\n",
|
| 1281 |
+
" <th>39</th>\n",
|
| 1282 |
+
" <td>Su57</td>\n",
|
| 1283 |
+
" <td>590</td>\n",
|
| 1284 |
+
" <td>2058</td>\n",
|
| 1285 |
+
" <td>400000</td>\n",
|
| 1286 |
+
" <td>5.0</td>\n",
|
| 1287 |
+
" <td>2</td>\n",
|
| 1288 |
+
" <td>0</td>\n",
|
| 1289 |
+
" </tr>\n",
|
| 1290 |
+
" <tr>\n",
|
| 1291 |
+
" <th>18</th>\n",
|
| 1292 |
+
" <td>J36</td>\n",
|
| 1293 |
+
" <td>590</td>\n",
|
| 1294 |
+
" <td>1715</td>\n",
|
| 1295 |
+
" <td>400000</td>\n",
|
| 1296 |
+
" <td>5.0</td>\n",
|
| 1297 |
+
" <td>2</td>\n",
|
| 1298 |
+
" <td>0</td>\n",
|
| 1299 |
+
" </tr>\n",
|
| 1300 |
+
" <tr>\n",
|
| 1301 |
+
" <th>32</th>\n",
|
| 1302 |
+
" <td>Mig31</td>\n",
|
| 1303 |
+
" <td>833</td>\n",
|
| 1304 |
+
" <td>2058</td>\n",
|
| 1305 |
+
" <td>400000</td>\n",
|
| 1306 |
+
" <td>4.0</td>\n",
|
| 1307 |
+
" <td>1</td>\n",
|
| 1308 |
+
" <td>0</td>\n",
|
| 1309 |
+
" </tr>\n",
|
| 1310 |
+
" <tr>\n",
|
| 1311 |
+
" <th>17</th>\n",
|
| 1312 |
+
" <td>J35</td>\n",
|
| 1313 |
+
" <td>590</td>\n",
|
| 1314 |
+
" <td>1715</td>\n",
|
| 1315 |
+
" <td>300000</td>\n",
|
| 1316 |
+
" <td>5.0</td>\n",
|
| 1317 |
+
" <td>2</td>\n",
|
| 1318 |
+
" <td>0</td>\n",
|
| 1319 |
+
" </tr>\n",
|
| 1320 |
+
" <tr>\n",
|
| 1321 |
+
" <th>16</th>\n",
|
| 1322 |
+
" <td>J20</td>\n",
|
| 1323 |
+
" <td>590</td>\n",
|
| 1324 |
+
" <td>1715</td>\n",
|
| 1325 |
+
" <td>300000</td>\n",
|
| 1326 |
+
" <td>5.0</td>\n",
|
| 1327 |
+
" <td>2</td>\n",
|
| 1328 |
+
" <td>0</td>\n",
|
| 1329 |
+
" </tr>\n",
|
| 1330 |
+
" <tr>\n",
|
| 1331 |
+
" <th>15</th>\n",
|
| 1332 |
+
" <td>J10</td>\n",
|
| 1333 |
+
" <td>650</td>\n",
|
| 1334 |
+
" <td>1715</td>\n",
|
| 1335 |
+
" <td>300000</td>\n",
|
| 1336 |
+
" <td>4.5</td>\n",
|
| 1337 |
+
" <td>2</td>\n",
|
| 1338 |
+
" <td>0</td>\n",
|
| 1339 |
+
" </tr>\n",
|
| 1340 |
+
" <tr>\n",
|
| 1341 |
+
" <th>34</th>\n",
|
| 1342 |
+
" <td>Rafale</td>\n",
|
| 1343 |
+
" <td>531</td>\n",
|
| 1344 |
+
" <td>1372</td>\n",
|
| 1345 |
+
" <td>200000</td>\n",
|
| 1346 |
+
" <td>4.5</td>\n",
|
| 1347 |
+
" <td>2</td>\n",
|
| 1348 |
+
" <td>0</td>\n",
|
| 1349 |
+
" </tr>\n",
|
| 1350 |
+
" <tr>\n",
|
| 1351 |
+
" <th>20</th>\n",
|
| 1352 |
+
" <td>JAS39</td>\n",
|
| 1353 |
+
" <td>590</td>\n",
|
| 1354 |
+
" <td>1372</td>\n",
|
| 1355 |
+
" <td>200000</td>\n",
|
| 1356 |
+
" <td>4.5</td>\n",
|
| 1357 |
+
" <td>2</td>\n",
|
| 1358 |
+
" <td>0</td>\n",
|
| 1359 |
+
" </tr>\n",
|
| 1360 |
+
" <tr>\n",
|
| 1361 |
+
" <th>24</th>\n",
|
| 1362 |
+
" <td>KF21</td>\n",
|
| 1363 |
+
" <td>532</td>\n",
|
| 1364 |
+
" <td>1372</td>\n",
|
| 1365 |
+
" <td>200000</td>\n",
|
| 1366 |
+
" <td>4.5</td>\n",
|
| 1367 |
+
" <td>2</td>\n",
|
| 1368 |
+
" <td>0</td>\n",
|
| 1369 |
+
" </tr>\n",
|
| 1370 |
+
" </tbody>\n",
|
| 1371 |
+
"</table>\n",
|
| 1372 |
+
"</div>"
|
| 1373 |
+
],
|
| 1374 |
+
"text/plain": [
|
| 1375 |
+
" aircraft aircraft_max_speed missile_speed missile_range \\\n",
|
| 1376 |
+
"19 J50 590 1715 400000 \n",
|
| 1377 |
+
"39 Su57 590 2058 400000 \n",
|
| 1378 |
+
"18 J36 590 1715 400000 \n",
|
| 1379 |
+
"32 Mig31 833 2058 400000 \n",
|
| 1380 |
+
"17 J35 590 1715 300000 \n",
|
| 1381 |
+
"16 J20 590 1715 300000 \n",
|
| 1382 |
+
"15 J10 650 1715 300000 \n",
|
| 1383 |
+
"34 Rafale 531 1372 200000 \n",
|
| 1384 |
+
"20 JAS39 590 1372 200000 \n",
|
| 1385 |
+
"24 KF21 532 1372 200000 \n",
|
| 1386 |
+
"\n",
|
| 1387 |
+
" enemy_generation maneuverability no_aa_capability \n",
|
| 1388 |
+
"19 5.0 2 0 \n",
|
| 1389 |
+
"39 5.0 2 0 \n",
|
| 1390 |
+
"18 5.0 2 0 \n",
|
| 1391 |
+
"32 4.0 1 0 \n",
|
| 1392 |
+
"17 5.0 2 0 \n",
|
| 1393 |
+
"16 5.0 2 0 \n",
|
| 1394 |
+
"15 4.5 2 0 \n",
|
| 1395 |
+
"34 4.5 2 0 \n",
|
| 1396 |
+
"20 4.5 2 0 \n",
|
| 1397 |
+
"24 4.5 2 0 "
|
| 1398 |
+
]
|
| 1399 |
+
},
|
| 1400 |
+
"execution_count": 16,
|
| 1401 |
+
"metadata": {},
|
| 1402 |
+
"output_type": "execute_result"
|
| 1403 |
+
}
|
| 1404 |
+
],
|
| 1405 |
+
"source": [
|
| 1406 |
+
"# Get sorted decending missile ranges\n",
|
| 1407 |
+
"combat_df.sort_values(\"missile_range\", ascending=False)[:10]"
|
| 1408 |
+
]
|
| 1409 |
+
},
|
| 1410 |
+
{
|
| 1411 |
+
"cell_type": "code",
|
| 1412 |
+
"execution_count": 17,
|
| 1413 |
+
"id": "5e723e87",
|
| 1414 |
+
"metadata": {},
|
| 1415 |
+
"outputs": [
|
| 1416 |
+
{
|
| 1417 |
+
"data": {
|
| 1418 |
+
"text/html": [
|
| 1419 |
+
"<div>\n",
|
| 1420 |
+
"<style scoped>\n",
|
| 1421 |
+
" .dataframe tbody tr th:only-of-type {\n",
|
| 1422 |
+
" vertical-align: middle;\n",
|
| 1423 |
+
" }\n",
|
| 1424 |
+
"\n",
|
| 1425 |
+
" .dataframe tbody tr th {\n",
|
| 1426 |
+
" vertical-align: top;\n",
|
| 1427 |
+
" }\n",
|
| 1428 |
+
"\n",
|
| 1429 |
+
" .dataframe thead th {\n",
|
| 1430 |
+
" text-align: right;\n",
|
| 1431 |
+
" }\n",
|
| 1432 |
+
"</style>\n",
|
| 1433 |
+
"<table border=\"1\" class=\"dataframe\">\n",
|
| 1434 |
+
" <thead>\n",
|
| 1435 |
+
" <tr style=\"text-align: right;\">\n",
|
| 1436 |
+
" <th></th>\n",
|
| 1437 |
+
" <th>aircraft</th>\n",
|
| 1438 |
+
" <th>aircraft_max_speed</th>\n",
|
| 1439 |
+
" <th>missile_speed</th>\n",
|
| 1440 |
+
" <th>missile_range</th>\n",
|
| 1441 |
+
" <th>enemy_generation</th>\n",
|
| 1442 |
+
" <th>maneuverability</th>\n",
|
| 1443 |
+
" <th>no_aa_capability</th>\n",
|
| 1444 |
+
" </tr>\n",
|
| 1445 |
+
" </thead>\n",
|
| 1446 |
+
" <tbody>\n",
|
| 1447 |
+
" <tr>\n",
|
| 1448 |
+
" <th>11</th>\n",
|
| 1449 |
+
" <td>F22</td>\n",
|
| 1450 |
+
" <td>669</td>\n",
|
| 1451 |
+
" <td>1372</td>\n",
|
| 1452 |
+
" <td>160000</td>\n",
|
| 1453 |
+
" <td>5.0</td>\n",
|
| 1454 |
+
" <td>2</td>\n",
|
| 1455 |
+
" <td>0</td>\n",
|
| 1456 |
+
" </tr>\n",
|
| 1457 |
+
" </tbody>\n",
|
| 1458 |
+
"</table>\n",
|
| 1459 |
+
"</div>"
|
| 1460 |
+
],
|
| 1461 |
+
"text/plain": [
|
| 1462 |
+
" aircraft aircraft_max_speed missile_speed missile_range \\\n",
|
| 1463 |
+
"11 F22 669 1372 160000 \n",
|
| 1464 |
+
"\n",
|
| 1465 |
+
" enemy_generation maneuverability no_aa_capability \n",
|
| 1466 |
+
"11 5.0 2 0 "
|
| 1467 |
+
]
|
| 1468 |
+
},
|
| 1469 |
+
"execution_count": 17,
|
| 1470 |
+
"metadata": {},
|
| 1471 |
+
"output_type": "execute_result"
|
| 1472 |
+
}
|
| 1473 |
+
],
|
| 1474 |
+
"source": [
|
| 1475 |
+
"combat_df[combat_df[\"aircraft\"]==\"F22\"]"
|
| 1476 |
+
]
|
| 1477 |
+
},
|
| 1478 |
+
{
|
| 1479 |
+
"cell_type": "code",
|
| 1480 |
+
"execution_count": 18,
|
| 1481 |
+
"id": "41f0712c",
|
| 1482 |
+
"metadata": {},
|
| 1483 |
+
"outputs": [
|
| 1484 |
+
{
|
| 1485 |
+
"data": {
|
| 1486 |
+
"text/html": [
|
| 1487 |
+
"<div>\n",
|
| 1488 |
+
"<style scoped>\n",
|
| 1489 |
+
" .dataframe tbody tr th:only-of-type {\n",
|
| 1490 |
+
" vertical-align: middle;\n",
|
| 1491 |
+
" }\n",
|
| 1492 |
+
"\n",
|
| 1493 |
+
" .dataframe tbody tr th {\n",
|
| 1494 |
+
" vertical-align: top;\n",
|
| 1495 |
+
" }\n",
|
| 1496 |
+
"\n",
|
| 1497 |
+
" .dataframe thead th {\n",
|
| 1498 |
+
" text-align: right;\n",
|
| 1499 |
+
" }\n",
|
| 1500 |
+
"</style>\n",
|
| 1501 |
+
"<table border=\"1\" class=\"dataframe\">\n",
|
| 1502 |
+
" <thead>\n",
|
| 1503 |
+
" <tr style=\"text-align: right;\">\n",
|
| 1504 |
+
" <th></th>\n",
|
| 1505 |
+
" <th>aircraft_max_speed</th>\n",
|
| 1506 |
+
" <th>missile_speed</th>\n",
|
| 1507 |
+
" <th>missile_range</th>\n",
|
| 1508 |
+
" </tr>\n",
|
| 1509 |
+
" </thead>\n",
|
| 1510 |
+
" <tbody>\n",
|
| 1511 |
+
" <tr>\n",
|
| 1512 |
+
" <th>aircraft_max_speed</th>\n",
|
| 1513 |
+
" <td>1.000000</td>\n",
|
| 1514 |
+
" <td>0.753865</td>\n",
|
| 1515 |
+
" <td>0.633663</td>\n",
|
| 1516 |
+
" </tr>\n",
|
| 1517 |
+
" <tr>\n",
|
| 1518 |
+
" <th>missile_speed</th>\n",
|
| 1519 |
+
" <td>0.753865</td>\n",
|
| 1520 |
+
" <td>1.000000</td>\n",
|
| 1521 |
+
" <td>0.820434</td>\n",
|
| 1522 |
+
" </tr>\n",
|
| 1523 |
+
" <tr>\n",
|
| 1524 |
+
" <th>missile_range</th>\n",
|
| 1525 |
+
" <td>0.633663</td>\n",
|
| 1526 |
+
" <td>0.820434</td>\n",
|
| 1527 |
+
" <td>1.000000</td>\n",
|
| 1528 |
+
" </tr>\n",
|
| 1529 |
+
" </tbody>\n",
|
| 1530 |
+
"</table>\n",
|
| 1531 |
+
"</div>"
|
| 1532 |
+
],
|
| 1533 |
+
"text/plain": [
|
| 1534 |
+
" aircraft_max_speed missile_speed missile_range\n",
|
| 1535 |
+
"aircraft_max_speed 1.000000 0.753865 0.633663\n",
|
| 1536 |
+
"missile_speed 0.753865 1.000000 0.820434\n",
|
| 1537 |
+
"missile_range 0.633663 0.820434 1.000000"
|
| 1538 |
+
]
|
| 1539 |
+
},
|
| 1540 |
+
"execution_count": 18,
|
| 1541 |
+
"metadata": {},
|
| 1542 |
+
"output_type": "execute_result"
|
| 1543 |
+
}
|
| 1544 |
+
],
|
| 1545 |
+
"source": [
|
| 1546 |
+
"# correlation between aircraft speed and missile \n",
|
| 1547 |
+
"combat_df[[\"aircraft_max_speed\", \"missile_speed\", \"missile_range\"]].corr()"
|
| 1548 |
+
]
|
| 1549 |
+
},
|
| 1550 |
+
{
|
| 1551 |
+
"cell_type": "code",
|
| 1552 |
+
"execution_count": 19,
|
| 1553 |
+
"id": "468e508e",
|
| 1554 |
+
"metadata": {},
|
| 1555 |
+
"outputs": [
|
| 1556 |
+
{
|
| 1557 |
+
"data": {
|
| 1558 |
+
"text/plain": [
|
| 1559 |
+
"enemy_generation\n",
|
| 1560 |
+
"3.5 26500.000000\n",
|
| 1561 |
+
"4.0 83240.000000\n",
|
| 1562 |
+
"4.5 159090.909091\n",
|
| 1563 |
+
"5.0 242272.727273\n",
|
| 1564 |
+
"Name: missile_range, dtype: float64"
|
| 1565 |
+
]
|
| 1566 |
+
},
|
| 1567 |
+
"execution_count": 19,
|
| 1568 |
+
"metadata": {},
|
| 1569 |
+
"output_type": "execute_result"
|
| 1570 |
+
}
|
| 1571 |
+
],
|
| 1572 |
+
"source": [
|
| 1573 |
+
"combat_df.groupby(\"enemy_generation\")[\"missile_range\"].mean()"
|
| 1574 |
+
]
|
| 1575 |
+
},
|
| 1576 |
+
{
|
| 1577 |
+
"cell_type": "markdown",
|
| 1578 |
+
"id": "f7850284",
|
| 1579 |
+
"metadata": {},
|
| 1580 |
+
"source": [
|
| 1581 |
+
"## **Extracted Metadata Insights**\n",
|
| 1582 |
+
"\n",
|
| 1583 |
+
"### **Combat-Capable Aircraft**\n",
|
| 1584 |
+
"- Total aircraft: 102\n",
|
| 1585 |
+
"- Combat-capable aircraft: 56\n",
|
| 1586 |
+
"- Non combat-capable aircraft: 46\n",
|
| 1587 |
+
"\n",
|
| 1588 |
+
"---\n",
|
| 1589 |
+
"\n",
|
| 1590 |
+
"### Aircraft Speed Range\n",
|
| 1591 |
+
"\n",
|
| 1592 |
+
"```python\n",
|
| 1593 |
+
"61 m/s → 983 m/s\n",
|
| 1594 |
+
"```\n",
|
| 1595 |
+
"\n",
|
| 1596 |
+
"---\n",
|
| 1597 |
+
"\n",
|
| 1598 |
+
"### Missile Speed Range\n",
|
| 1599 |
+
"\n",
|
| 1600 |
+
"```python \n",
|
| 1601 |
+
"686 m/s → 2058 m/s\n",
|
| 1602 |
+
"````\n",
|
| 1603 |
+
"\n",
|
| 1604 |
+
"---\n",
|
| 1605 |
+
"\n",
|
| 1606 |
+
"### Missile Range\n",
|
| 1607 |
+
"\n",
|
| 1608 |
+
"```python id=\"e2h15d\"\n",
|
| 1609 |
+
"8,000 m → 400,000 m\n",
|
| 1610 |
+
"```\n",
|
| 1611 |
+
"\n",
|
| 1612 |
+
"---\n",
|
| 1613 |
+
"\n",
|
| 1614 |
+
"### Enemy Generation Distribution\n",
|
| 1615 |
+
"\n",
|
| 1616 |
+
"```python id=\"txut2q\"\n",
|
| 1617 |
+
"4.0 → 23 aircraft\n",
|
| 1618 |
+
"5.0 → 13 aircraft\n",
|
| 1619 |
+
"4.5 → 10 aircraft\n",
|
| 1620 |
+
"3.5 → 5 aircraft\n",
|
| 1621 |
+
"```\n",
|
| 1622 |
+
"\n",
|
| 1623 |
+
"---\n",
|
| 1624 |
+
"\n",
|
| 1625 |
+
"### Maneuverability Distribution\n",
|
| 1626 |
+
"\n",
|
| 1627 |
+
"```python id=\"nt11ao\"\n",
|
| 1628 |
+
"2 (high) → 27 aircraft\n",
|
| 1629 |
+
"1 (medium) → 24 aircraft\n",
|
| 1630 |
+
"```\n",
|
| 1631 |
+
"\n",
|
| 1632 |
+
"---\n",
|
| 1633 |
+
"\n",
|
| 1634 |
+
"### Key Observations\n",
|
| 1635 |
+
"\n",
|
| 1636 |
+
"* Most combat aircraft belong to Gen 4 and Gen 5.\n",
|
| 1637 |
+
"* Most combat aircraft have medium or high maneuverability.\n",
|
| 1638 |
+
"* Advanced aircraft generally have longer missile ranges.\n",
|
| 1639 |
+
"* High-end aircraft like Su57, Mig31, J35, J36, and J50 dominate the upper missile range limits.\n",
|
| 1640 |
+
"\n",
|
| 1641 |
+
"The goal of the generator is not perfect aerospace simulation, but generation of believable and internally consistent combat scenarios that allow ML models to learn meaningful relationships.\n",
|
| 1642 |
+
"\n",
|
| 1643 |
+
"---"
|
| 1644 |
+
]
|
| 1645 |
+
},
|
| 1646 |
+
{
|
| 1647 |
+
"cell_type": "markdown",
|
| 1648 |
+
"id": "8832d356",
|
| 1649 |
+
"metadata": {},
|
| 1650 |
+
"source": [
|
| 1651 |
+
"## **Plan to build physics generator**\n",
|
| 1652 |
+
"\n",
|
| 1653 |
+
"### **How it should be called**\n",
|
| 1654 |
+
"\n",
|
| 1655 |
+
"```python\n",
|
| 1656 |
+
"from src.physics_generator import generate_dataset\n",
|
| 1657 |
+
"generate_dataset()\n",
|
| 1658 |
+
"```\n",
|
| 1659 |
+
"\n",
|
| 1660 |
+
"### **How it will be build**\n",
|
| 1661 |
+
"\n",
|
| 1662 |
+
"```markdown\n",
|
| 1663 |
+
"generate_dataset() ← the one function the notebook calls\n",
|
| 1664 |
+
" └── load_metadata() ← reads aircraft_metadata.csv\n",
|
| 1665 |
+
" └── generate_row() ← builds one scenario\n",
|
| 1666 |
+
" └── derive_missile_phase()\n",
|
| 1667 |
+
" └── derive_closure_rate()\n",
|
| 1668 |
+
" └── derive_evasion_time()\n",
|
| 1669 |
+
" └── derive_hit_label()\n",
|
| 1670 |
+
" └── save_dataset() ← saves to CSV\n",
|
| 1671 |
+
"```\n",
|
| 1672 |
+
"---\n",
|
| 1673 |
+
"\n",
|
| 1674 |
+
"### **The sequence of `physics_generator.py` script**\n",
|
| 1675 |
+
"\n",
|
| 1676 |
+
"```python\n",
|
| 1677 |
+
"_generate_row()\n",
|
| 1678 |
+
"│\n",
|
| 1679 |
+
"├── 1. Pick a random enemy aircraft from metadata\n",
|
| 1680 |
+
"│ (combat-capable only — no_aa_capability = 0)\n",
|
| 1681 |
+
"│\n",
|
| 1682 |
+
"├── 2. Sample raw feature values\n",
|
| 1683 |
+
"│ your_speed, your_altitude, azimuth, elevation,\n",
|
| 1684 |
+
"│ maneuverability, countermeasure_deployed\n",
|
| 1685 |
+
"│ launch_distance (within that aircraft's missile_range)\n",
|
| 1686 |
+
"│ remaining_distance (between 0 and launch_distance)\n",
|
| 1687 |
+
"│\n",
|
| 1688 |
+
"├── 3. _derive_missile_phase()\n",
|
| 1689 |
+
"│ \"How far has the missile already traveled?\"\n",
|
| 1690 |
+
"│ → returns 0, 1, or 2\n",
|
| 1691 |
+
"│\n",
|
| 1692 |
+
"├── 4. _derive_closure_rate()\n",
|
| 1693 |
+
"│ \"How fast is the gap closing in 3D space?\"\n",
|
| 1694 |
+
"│ → returns a speed in m/s\n",
|
| 1695 |
+
"│\n",
|
| 1696 |
+
"├── 5. _derive_evasion_time()\n",
|
| 1697 |
+
"│ \"How many seconds before it reaches you?\"\n",
|
| 1698 |
+
"│ → returns a float in seconds\n",
|
| 1699 |
+
"│\n",
|
| 1700 |
+
"├── 6. _derive_hit_label()\n",
|
| 1701 |
+
"│ \"After your evasion attempt, does it hit?\"\n",
|
| 1702 |
+
"│ → returns 0 or 1\n",
|
| 1703 |
+
"│\n",
|
| 1704 |
+
"└── 7. Package everything into a dict → one row\n",
|
| 1705 |
+
"```\n",
|
| 1706 |
+
"\n",
|
| 1707 |
+
"---"
|
| 1708 |
+
]
|
| 1709 |
+
},
|
| 1710 |
+
{
|
| 1711 |
+
"cell_type": "markdown",
|
| 1712 |
+
"id": "c3d32245",
|
| 1713 |
+
"metadata": {},
|
| 1714 |
+
"source": [
|
| 1715 |
+
"## **Sanity Check - Physics Generator**\n",
|
| 1716 |
+
"\n",
|
| 1717 |
+
"Quick validation that `generate_dataset()` produces clean, physically sensible output.\n",
|
| 1718 |
+
"Run with `N_ROWS = 1_000` before the full 1M generation."
|
| 1719 |
+
]
|
| 1720 |
+
},
|
| 1721 |
+
{
|
| 1722 |
+
"cell_type": "code",
|
| 1723 |
+
"execution_count": 26,
|
| 1724 |
+
"id": "37b78e79",
|
| 1725 |
+
"metadata": {},
|
| 1726 |
+
"outputs": [],
|
| 1727 |
+
"source": [
|
| 1728 |
+
"import sys\n",
|
| 1729 |
+
"from pathlib import Path\n",
|
| 1730 |
+
"\n",
|
| 1731 |
+
"# Add src/ to path so we can import physics_generator\n",
|
| 1732 |
+
"sys.path.append(str(Path.cwd().resolve().parent.parent / \"src\"))\n",
|
| 1733 |
+
"\n",
|
| 1734 |
+
"from physics_generator import generate_dataset"
|
| 1735 |
+
]
|
| 1736 |
+
},
|
| 1737 |
+
{
|
| 1738 |
+
"cell_type": "code",
|
| 1739 |
+
"execution_count": null,
|
| 1740 |
+
"id": "4181ca14",
|
| 1741 |
+
"metadata": {},
|
| 1742 |
+
"outputs": [
|
| 1743 |
+
{
|
| 1744 |
+
"name": "stdout",
|
| 1745 |
+
"output_type": "stream",
|
| 1746 |
+
"text": [
|
| 1747 |
+
"Done. 1,000 rows saved to /mnt/d/Eakem/Learning ML and DS/Project_ATAS/ATAS_Project/data/synthetic_engagements.csv\n"
|
| 1748 |
+
]
|
| 1749 |
+
}
|
| 1750 |
+
],
|
| 1751 |
+
"source": [
|
| 1752 |
+
"# # Running the generator for N_ROWS = 1_000\n",
|
| 1753 |
+
"# df = generate_dataset()"
|
| 1754 |
+
]
|
| 1755 |
+
},
|
| 1756 |
+
{
|
| 1757 |
+
"cell_type": "code",
|
| 1758 |
+
"execution_count": null,
|
| 1759 |
+
"id": "54d3fc2c",
|
| 1760 |
+
"metadata": {},
|
| 1761 |
+
"outputs": [
|
| 1762 |
+
{
|
| 1763 |
+
"name": "stdout",
|
| 1764 |
+
"output_type": "stream",
|
| 1765 |
+
"text": [
|
| 1766 |
+
"Rows: 1000\n",
|
| 1767 |
+
"Columns: 16\n",
|
| 1768 |
+
"Shape: (1000, 16)\n"
|
| 1769 |
+
]
|
| 1770 |
+
}
|
| 1771 |
+
],
|
| 1772 |
+
"source": [
|
| 1773 |
+
"# import pandas as pd\n",
|
| 1774 |
+
"# df = pd.read_csv(\"../data/synthetic_engagements_1000.csv\")\n",
|
| 1775 |
+
"# print(f\"Rows: {len(df)}\")\n",
|
| 1776 |
+
"# print(f\"Columns: {len(df.columns)}\")\n",
|
| 1777 |
+
"# print(f\"Shape: {df.shape}\")"
|
| 1778 |
+
]
|
| 1779 |
+
},
|
| 1780 |
+
{
|
| 1781 |
+
"cell_type": "code",
|
| 1782 |
+
"execution_count": null,
|
| 1783 |
+
"id": "14d1f643",
|
| 1784 |
+
"metadata": {},
|
| 1785 |
+
"outputs": [
|
| 1786 |
+
{
|
| 1787 |
+
"data": {
|
| 1788 |
+
"text/html": [
|
| 1789 |
+
"<div>\n",
|
| 1790 |
+
"<style scoped>\n",
|
| 1791 |
+
" .dataframe tbody tr th:only-of-type {\n",
|
| 1792 |
+
" vertical-align: middle;\n",
|
| 1793 |
+
" }\n",
|
| 1794 |
+
"\n",
|
| 1795 |
+
" .dataframe tbody tr th {\n",
|
| 1796 |
+
" vertical-align: top;\n",
|
| 1797 |
+
" }\n",
|
| 1798 |
+
"\n",
|
| 1799 |
+
" .dataframe thead th {\n",
|
| 1800 |
+
" text-align: right;\n",
|
| 1801 |
+
" }\n",
|
| 1802 |
+
"</style>\n",
|
| 1803 |
+
"<table border=\"1\" class=\"dataframe\">\n",
|
| 1804 |
+
" <thead>\n",
|
| 1805 |
+
" <tr style=\"text-align: right;\">\n",
|
| 1806 |
+
" <th></th>\n",
|
| 1807 |
+
" <th>launch_distance</th>\n",
|
| 1808 |
+
" <th>remaining_distance</th>\n",
|
| 1809 |
+
" <th>closure_rate</th>\n",
|
| 1810 |
+
" <th>azimuth</th>\n",
|
| 1811 |
+
" <th>elevation</th>\n",
|
| 1812 |
+
" <th>missile_phase</th>\n",
|
| 1813 |
+
" <th>your_speed</th>\n",
|
| 1814 |
+
" <th>your_altitude</th>\n",
|
| 1815 |
+
" <th>your_maneuverability</th>\n",
|
| 1816 |
+
" <th>enemy_altitude</th>\n",
|
| 1817 |
+
" <th>missile_speed</th>\n",
|
| 1818 |
+
" <th>missile_range</th>\n",
|
| 1819 |
+
" <th>enemy_generation</th>\n",
|
| 1820 |
+
" <th>countermeasure_deployed</th>\n",
|
| 1821 |
+
" <th>evasion_time</th>\n",
|
| 1822 |
+
" <th>hit</th>\n",
|
| 1823 |
+
" </tr>\n",
|
| 1824 |
+
" </thead>\n",
|
| 1825 |
+
" <tbody>\n",
|
| 1826 |
+
" <tr>\n",
|
| 1827 |
+
" <th>0</th>\n",
|
| 1828 |
+
" <td>22570.893367</td>\n",
|
| 1829 |
+
" <td>2961.980067</td>\n",
|
| 1830 |
+
" <td>1185.763992</td>\n",
|
| 1831 |
+
" <td>61.005549</td>\n",
|
| 1832 |
+
" <td>-34.707563</td>\n",
|
| 1833 |
+
" <td>2</td>\n",
|
| 1834 |
+
" <td>825.050818</td>\n",
|
| 1835 |
+
" <td>6512.661972</td>\n",
|
| 1836 |
+
" <td>1</td>\n",
|
| 1837 |
+
" <td>3655.798699</td>\n",
|
| 1838 |
+
" <td>857</td>\n",
|
| 1839 |
+
" <td>35000</td>\n",
|
| 1840 |
+
" <td>4.0</td>\n",
|
| 1841 |
+
" <td>1</td>\n",
|
| 1842 |
+
" <td>2.229421</td>\n",
|
| 1843 |
+
" <td>0</td>\n",
|
| 1844 |
+
" </tr>\n",
|
| 1845 |
+
" <tr>\n",
|
| 1846 |
+
" <th>1</th>\n",
|
| 1847 |
+
" <td>2367.007196</td>\n",
|
| 1848 |
+
" <td>1652.662358</td>\n",
|
| 1849 |
+
" <td>625.878279</td>\n",
|
| 1850 |
+
" <td>212.466382</td>\n",
|
| 1851 |
+
" <td>68.630275</td>\n",
|
| 1852 |
+
" <td>0</td>\n",
|
| 1853 |
+
" <td>751.777144</td>\n",
|
| 1854 |
+
" <td>18782.156933</td>\n",
|
| 1855 |
+
" <td>1</td>\n",
|
| 1856 |
+
" <td>19508.648175</td>\n",
|
| 1857 |
+
" <td>857</td>\n",
|
| 1858 |
+
" <td>35000</td>\n",
|
| 1859 |
+
" <td>4.0</td>\n",
|
| 1860 |
+
" <td>0</td>\n",
|
| 1861 |
+
" <td>2.772577</td>\n",
|
| 1862 |
+
" <td>1</td>\n",
|
| 1863 |
+
" </tr>\n",
|
| 1864 |
+
" <tr>\n",
|
| 1865 |
+
" <th>2</th>\n",
|
| 1866 |
+
" <td>6602.346475</td>\n",
|
| 1867 |
+
" <td>2558.752556</td>\n",
|
| 1868 |
+
" <td>871.179381</td>\n",
|
| 1869 |
+
" <td>281.112640</td>\n",
|
| 1870 |
+
" <td>45.266055</td>\n",
|
| 1871 |
+
" <td>1</td>\n",
|
| 1872 |
+
" <td>104.527357</td>\n",
|
| 1873 |
+
" <td>3625.183143</td>\n",
|
| 1874 |
+
" <td>2</td>\n",
|
| 1875 |
+
" <td>29894.396018</td>\n",
|
| 1876 |
+
" <td>857</td>\n",
|
| 1877 |
+
" <td>35000</td>\n",
|
| 1878 |
+
" <td>4.0</td>\n",
|
| 1879 |
+
" <td>0</td>\n",
|
| 1880 |
+
" <td>3.230825</td>\n",
|
| 1881 |
+
" <td>0</td>\n",
|
| 1882 |
+
" </tr>\n",
|
| 1883 |
+
" <tr>\n",
|
| 1884 |
+
" <th>3</th>\n",
|
| 1885 |
+
" <td>18281.792405</td>\n",
|
| 1886 |
+
" <td>18049.709650</td>\n",
|
| 1887 |
+
" <td>648.636953</td>\n",
|
| 1888 |
+
" <td>115.135435</td>\n",
|
| 1889 |
+
" <td>-37.372887</td>\n",
|
| 1890 |
+
" <td>0</td>\n",
|
| 1891 |
+
" <td>617.267514</td>\n",
|
| 1892 |
+
" <td>5759.552561</td>\n",
|
| 1893 |
+
" <td>1</td>\n",
|
| 1894 |
+
" <td>15002.849014</td>\n",
|
| 1895 |
+
" <td>857</td>\n",
|
| 1896 |
+
" <td>35000</td>\n",
|
| 1897 |
+
" <td>4.0</td>\n",
|
| 1898 |
+
" <td>1</td>\n",
|
| 1899 |
+
" <td>32.140344</td>\n",
|
| 1900 |
+
" <td>0</td>\n",
|
| 1901 |
+
" </tr>\n",
|
| 1902 |
+
" <tr>\n",
|
| 1903 |
+
" <th>4</th>\n",
|
| 1904 |
+
" <td>9834.379665</td>\n",
|
| 1905 |
+
" <td>1522.983460</td>\n",
|
| 1906 |
+
" <td>1088.038782</td>\n",
|
| 1907 |
+
" <td>316.104680</td>\n",
|
| 1908 |
+
" <td>-45.805628</td>\n",
|
| 1909 |
+
" <td>2</td>\n",
|
| 1910 |
+
" <td>459.932597</td>\n",
|
| 1911 |
+
" <td>28821.640152</td>\n",
|
| 1912 |
+
" <td>2</td>\n",
|
| 1913 |
+
" <td>4643.721244</td>\n",
|
| 1914 |
+
" <td>857</td>\n",
|
| 1915 |
+
" <td>35000</td>\n",
|
| 1916 |
+
" <td>4.0</td>\n",
|
| 1917 |
+
" <td>1</td>\n",
|
| 1918 |
+
" <td>1.308767</td>\n",
|
| 1919 |
+
" <td>0</td>\n",
|
| 1920 |
+
" </tr>\n",
|
| 1921 |
+
" </tbody>\n",
|
| 1922 |
+
"</table>\n",
|
| 1923 |
+
"</div>"
|
| 1924 |
+
],
|
| 1925 |
+
"text/plain": [
|
| 1926 |
+
" launch_distance remaining_distance closure_rate azimuth elevation \\\n",
|
| 1927 |
+
"0 22570.893367 2961.980067 1185.763992 61.005549 -34.707563 \n",
|
| 1928 |
+
"1 2367.007196 1652.662358 625.878279 212.466382 68.630275 \n",
|
| 1929 |
+
"2 6602.346475 2558.752556 871.179381 281.112640 45.266055 \n",
|
| 1930 |
+
"3 18281.792405 18049.709650 648.636953 115.135435 -37.372887 \n",
|
| 1931 |
+
"4 9834.379665 1522.983460 1088.038782 316.104680 -45.805628 \n",
|
| 1932 |
+
"\n",
|
| 1933 |
+
" missile_phase your_speed your_altitude your_maneuverability \\\n",
|
| 1934 |
+
"0 2 825.050818 6512.661972 1 \n",
|
| 1935 |
+
"1 0 751.777144 18782.156933 1 \n",
|
| 1936 |
+
"2 1 104.527357 3625.183143 2 \n",
|
| 1937 |
+
"3 0 617.267514 5759.552561 1 \n",
|
| 1938 |
+
"4 2 459.932597 28821.640152 2 \n",
|
| 1939 |
+
"\n",
|
| 1940 |
+
" enemy_altitude missile_speed missile_range enemy_generation \\\n",
|
| 1941 |
+
"0 3655.798699 857 35000 4.0 \n",
|
| 1942 |
+
"1 19508.648175 857 35000 4.0 \n",
|
| 1943 |
+
"2 29894.396018 857 35000 4.0 \n",
|
| 1944 |
+
"3 15002.849014 857 35000 4.0 \n",
|
| 1945 |
+
"4 4643.721244 857 35000 4.0 \n",
|
| 1946 |
+
"\n",
|
| 1947 |
+
" countermeasure_deployed evasion_time hit \n",
|
| 1948 |
+
"0 1 2.229421 0 \n",
|
| 1949 |
+
"1 0 2.772577 1 \n",
|
| 1950 |
+
"2 0 3.230825 0 \n",
|
| 1951 |
+
"3 1 32.140344 0 \n",
|
| 1952 |
+
"4 1 1.308767 0 "
|
| 1953 |
+
]
|
| 1954 |
+
},
|
| 1955 |
+
"execution_count": 29,
|
| 1956 |
+
"metadata": {},
|
| 1957 |
+
"output_type": "execute_result"
|
| 1958 |
+
}
|
| 1959 |
+
],
|
| 1960 |
+
"source": [
|
| 1961 |
+
"# df.head()"
|
| 1962 |
+
]
|
| 1963 |
+
},
|
| 1964 |
+
{
|
| 1965 |
+
"cell_type": "code",
|
| 1966 |
+
"execution_count": null,
|
| 1967 |
+
"id": "19f98851",
|
| 1968 |
+
"metadata": {},
|
| 1969 |
+
"outputs": [
|
| 1970 |
+
{
|
| 1971 |
+
"name": "stdout",
|
| 1972 |
+
"output_type": "stream",
|
| 1973 |
+
"text": [
|
| 1974 |
+
"Negative closure_rate: 0\n",
|
| 1975 |
+
"Negative evasion_rate: 0\n"
|
| 1976 |
+
]
|
| 1977 |
+
}
|
| 1978 |
+
],
|
| 1979 |
+
"source": [
|
| 1980 |
+
"# # CHecking for negatives values\n",
|
| 1981 |
+
"# print(f\"Negative closure_rate: {(df['closure_rate'] < 0).sum()}\")\n",
|
| 1982 |
+
"# print(f\"Negative evasion_rate: {(df['evasion_time'] < 0).sum()}\")"
|
| 1983 |
+
]
|
| 1984 |
+
},
|
| 1985 |
+
{
|
| 1986 |
+
"cell_type": "code",
|
| 1987 |
+
"execution_count": null,
|
| 1988 |
+
"id": "3b3d531f",
|
| 1989 |
+
"metadata": {},
|
| 1990 |
+
"outputs": [
|
| 1991 |
+
{
|
| 1992 |
+
"data": {
|
| 1993 |
+
"text/plain": [
|
| 1994 |
+
"missile_speed\n",
|
| 1995 |
+
"1372 473\n",
|
| 1996 |
+
"857 154\n",
|
| 1997 |
+
"686 100\n",
|
| 1998 |
+
"1715 97\n",
|
| 1999 |
+
"2058 58\n",
|
| 2000 |
+
"847 40\n",
|
| 2001 |
+
"1544 39\n",
|
| 2002 |
+
"750 20\n",
|
| 2003 |
+
"1475 19\n",
|
| 2004 |
+
"Name: count, dtype: int64"
|
| 2005 |
+
]
|
| 2006 |
+
},
|
| 2007 |
+
"execution_count": 32,
|
| 2008 |
+
"metadata": {},
|
| 2009 |
+
"output_type": "execute_result"
|
| 2010 |
+
}
|
| 2011 |
+
],
|
| 2012 |
+
"source": [
|
| 2013 |
+
"# df.missile_speed.value_counts() # From metadata csv"
|
| 2014 |
+
]
|
| 2015 |
+
},
|
| 2016 |
+
{
|
| 2017 |
+
"cell_type": "code",
|
| 2018 |
+
"execution_count": null,
|
| 2019 |
+
"id": "e7bd832a",
|
| 2020 |
+
"metadata": {},
|
| 2021 |
+
"outputs": [
|
| 2022 |
+
{
|
| 2023 |
+
"data": {
|
| 2024 |
+
"text/html": [
|
| 2025 |
+
"<div>\n",
|
| 2026 |
+
"<style scoped>\n",
|
| 2027 |
+
" .dataframe tbody tr th:only-of-type {\n",
|
| 2028 |
+
" vertical-align: middle;\n",
|
| 2029 |
+
" }\n",
|
| 2030 |
+
"\n",
|
| 2031 |
+
" .dataframe tbody tr th {\n",
|
| 2032 |
+
" vertical-align: top;\n",
|
| 2033 |
+
" }\n",
|
| 2034 |
+
"\n",
|
| 2035 |
+
" .dataframe thead th {\n",
|
| 2036 |
+
" text-align: right;\n",
|
| 2037 |
+
" }\n",
|
| 2038 |
+
"</style>\n",
|
| 2039 |
+
"<table border=\"1\" class=\"dataframe\">\n",
|
| 2040 |
+
" <thead>\n",
|
| 2041 |
+
" <tr style=\"text-align: right;\">\n",
|
| 2042 |
+
" <th></th>\n",
|
| 2043 |
+
" <th>0</th>\n",
|
| 2044 |
+
" <th>1</th>\n",
|
| 2045 |
+
" <th>2</th>\n",
|
| 2046 |
+
" <th>3</th>\n",
|
| 2047 |
+
" <th>4</th>\n",
|
| 2048 |
+
" <th>5</th>\n",
|
| 2049 |
+
" <th>6</th>\n",
|
| 2050 |
+
" <th>7</th>\n",
|
| 2051 |
+
" <th>8</th>\n",
|
| 2052 |
+
" <th>9</th>\n",
|
| 2053 |
+
" <th>...</th>\n",
|
| 2054 |
+
" <th>990</th>\n",
|
| 2055 |
+
" <th>991</th>\n",
|
| 2056 |
+
" <th>992</th>\n",
|
| 2057 |
+
" <th>993</th>\n",
|
| 2058 |
+
" <th>994</th>\n",
|
| 2059 |
+
" <th>995</th>\n",
|
| 2060 |
+
" <th>996</th>\n",
|
| 2061 |
+
" <th>997</th>\n",
|
| 2062 |
+
" <th>998</th>\n",
|
| 2063 |
+
" <th>999</th>\n",
|
| 2064 |
+
" </tr>\n",
|
| 2065 |
+
" </thead>\n",
|
| 2066 |
+
" <tbody>\n",
|
| 2067 |
+
" <tr>\n",
|
| 2068 |
+
" <th>launch_distance</th>\n",
|
| 2069 |
+
" <td>22570.893367</td>\n",
|
| 2070 |
+
" <td>2367.007196</td>\n",
|
| 2071 |
+
" <td>6602.346475</td>\n",
|
| 2072 |
+
" <td>18281.792405</td>\n",
|
| 2073 |
+
" <td>9834.379665</td>\n",
|
| 2074 |
+
" <td>12661.488033</td>\n",
|
| 2075 |
+
" <td>25901.724724</td>\n",
|
| 2076 |
+
" <td>8890.584630</td>\n",
|
| 2077 |
+
" <td>983.734396</td>\n",
|
| 2078 |
+
" <td>13859.562006</td>\n",
|
| 2079 |
+
" <td>...</td>\n",
|
| 2080 |
+
" <td>4981.478801</td>\n",
|
| 2081 |
+
" <td>32056.695360</td>\n",
|
| 2082 |
+
" <td>53419.689040</td>\n",
|
| 2083 |
+
" <td>19021.028127</td>\n",
|
| 2084 |
+
" <td>128594.277243</td>\n",
|
| 2085 |
+
" <td>64845.033514</td>\n",
|
| 2086 |
+
" <td>83532.658041</td>\n",
|
| 2087 |
+
" <td>146555.411805</td>\n",
|
| 2088 |
+
" <td>77971.657118</td>\n",
|
| 2089 |
+
" <td>2253.861997</td>\n",
|
| 2090 |
+
" </tr>\n",
|
| 2091 |
+
" <tr>\n",
|
| 2092 |
+
" <th>remaining_distance</th>\n",
|
| 2093 |
+
" <td>2961.980067</td>\n",
|
| 2094 |
+
" <td>1652.662358</td>\n",
|
| 2095 |
+
" <td>2558.752556</td>\n",
|
| 2096 |
+
" <td>18049.709650</td>\n",
|
| 2097 |
+
" <td>1522.983460</td>\n",
|
| 2098 |
+
" <td>3400.938832</td>\n",
|
| 2099 |
+
" <td>17955.494364</td>\n",
|
| 2100 |
+
" <td>7886.781225</td>\n",
|
| 2101 |
+
" <td>483.793860</td>\n",
|
| 2102 |
+
" <td>13391.098584</td>\n",
|
| 2103 |
+
" <td>...</td>\n",
|
| 2104 |
+
" <td>1605.088087</td>\n",
|
| 2105 |
+
" <td>4117.674334</td>\n",
|
| 2106 |
+
" <td>26058.452754</td>\n",
|
| 2107 |
+
" <td>3767.541060</td>\n",
|
| 2108 |
+
" <td>40310.301403</td>\n",
|
| 2109 |
+
" <td>60646.537540</td>\n",
|
| 2110 |
+
" <td>13843.586940</td>\n",
|
| 2111 |
+
" <td>94701.450863</td>\n",
|
| 2112 |
+
" <td>17925.445277</td>\n",
|
| 2113 |
+
" <td>1103.757518</td>\n",
|
| 2114 |
+
" </tr>\n",
|
| 2115 |
+
" <tr>\n",
|
| 2116 |
+
" <th>closure_rate</th>\n",
|
| 2117 |
+
" <td>1185.763992</td>\n",
|
| 2118 |
+
" <td>625.878279</td>\n",
|
| 2119 |
+
" <td>871.179381</td>\n",
|
| 2120 |
+
" <td>648.636953</td>\n",
|
| 2121 |
+
" <td>1088.038782</td>\n",
|
| 2122 |
+
" <td>725.990269</td>\n",
|
| 2123 |
+
" <td>844.960185</td>\n",
|
| 2124 |
+
" <td>1020.875119</td>\n",
|
| 2125 |
+
" <td>1066.347972</td>\n",
|
| 2126 |
+
" <td>386.272373</td>\n",
|
| 2127 |
+
" <td>...</td>\n",
|
| 2128 |
+
" <td>590.826598</td>\n",
|
| 2129 |
+
" <td>1312.773778</td>\n",
|
| 2130 |
+
" <td>1663.712597</td>\n",
|
| 2131 |
+
" <td>438.758129</td>\n",
|
| 2132 |
+
" <td>1394.591033</td>\n",
|
| 2133 |
+
" <td>1186.846963</td>\n",
|
| 2134 |
+
" <td>1211.445463</td>\n",
|
| 2135 |
+
" <td>1803.707956</td>\n",
|
| 2136 |
+
" <td>1636.533985</td>\n",
|
| 2137 |
+
" <td>405.627102</td>\n",
|
| 2138 |
+
" </tr>\n",
|
| 2139 |
+
" <tr>\n",
|
| 2140 |
+
" <th>azimuth</th>\n",
|
| 2141 |
+
" <td>61.005549</td>\n",
|
| 2142 |
+
" <td>212.466382</td>\n",
|
| 2143 |
+
" <td>281.112640</td>\n",
|
| 2144 |
+
" <td>115.135435</td>\n",
|
| 2145 |
+
" <td>316.104680</td>\n",
|
| 2146 |
+
" <td>259.107852</td>\n",
|
| 2147 |
+
" <td>211.731931</td>\n",
|
| 2148 |
+
" <td>283.740414</td>\n",
|
| 2149 |
+
" <td>349.132944</td>\n",
|
| 2150 |
+
" <td>176.945221</td>\n",
|
| 2151 |
+
" <td>...</td>\n",
|
| 2152 |
+
" <td>240.738983</td>\n",
|
| 2153 |
+
" <td>191.345225</td>\n",
|
| 2154 |
+
" <td>72.020487</td>\n",
|
| 2155 |
+
" <td>206.765055</td>\n",
|
| 2156 |
+
" <td>69.911001</td>\n",
|
| 2157 |
+
" <td>251.992973</td>\n",
|
| 2158 |
+
" <td>243.510094</td>\n",
|
| 2159 |
+
" <td>41.013277</td>\n",
|
| 2160 |
+
" <td>305.568549</td>\n",
|
| 2161 |
+
" <td>117.061738</td>\n",
|
| 2162 |
+
" </tr>\n",
|
| 2163 |
+
" <tr>\n",
|
| 2164 |
+
" <th>elevation</th>\n",
|
| 2165 |
+
" <td>-34.707563</td>\n",
|
| 2166 |
+
" <td>68.630275</td>\n",
|
| 2167 |
+
" <td>45.266055</td>\n",
|
| 2168 |
+
" <td>-37.372887</td>\n",
|
| 2169 |
+
" <td>-45.805628</td>\n",
|
| 2170 |
+
" <td>-2.007360</td>\n",
|
| 2171 |
+
" <td>-83.509892</td>\n",
|
| 2172 |
+
" <td>-13.091010</td>\n",
|
| 2173 |
+
" <td>-43.024865</td>\n",
|
| 2174 |
+
" <td>-55.574971</td>\n",
|
| 2175 |
+
" <td>...</td>\n",
|
| 2176 |
+
" <td>73.492841</td>\n",
|
| 2177 |
+
" <td>-85.265763</td>\n",
|
| 2178 |
+
" <td>-30.731328</td>\n",
|
| 2179 |
+
" <td>27.430184</td>\n",
|
| 2180 |
+
" <td>-76.994103</td>\n",
|
| 2181 |
+
" <td>-14.818047</td>\n",
|
| 2182 |
+
" <td>-43.173902</td>\n",
|
| 2183 |
+
" <td>-54.366817</td>\n",
|
| 2184 |
+
" <td>-32.225887</td>\n",
|
| 2185 |
+
" <td>-20.136454</td>\n",
|
| 2186 |
+
" </tr>\n",
|
| 2187 |
+
" <tr>\n",
|
| 2188 |
+
" <th>missile_phase</th>\n",
|
| 2189 |
+
" <td>2.000000</td>\n",
|
| 2190 |
+
" <td>0.000000</td>\n",
|
| 2191 |
+
" <td>1.000000</td>\n",
|
| 2192 |
+
" <td>0.000000</td>\n",
|
| 2193 |
+
" <td>2.000000</td>\n",
|
| 2194 |
+
" <td>2.000000</td>\n",
|
| 2195 |
+
" <td>0.000000</td>\n",
|
| 2196 |
+
" <td>0.000000</td>\n",
|
| 2197 |
+
" <td>1.000000</td>\n",
|
| 2198 |
+
" <td>0.000000</td>\n",
|
| 2199 |
+
" <td>...</td>\n",
|
| 2200 |
+
" <td>2.000000</td>\n",
|
| 2201 |
+
" <td>2.000000</td>\n",
|
| 2202 |
+
" <td>1.000000</td>\n",
|
| 2203 |
+
" <td>2.000000</td>\n",
|
| 2204 |
+
" <td>2.000000</td>\n",
|
| 2205 |
+
" <td>0.000000</td>\n",
|
| 2206 |
+
" <td>2.000000</td>\n",
|
| 2207 |
+
" <td>1.000000</td>\n",
|
| 2208 |
+
" <td>2.000000</td>\n",
|
| 2209 |
+
" <td>1.000000</td>\n",
|
| 2210 |
+
" </tr>\n",
|
| 2211 |
+
" <tr>\n",
|
| 2212 |
+
" <th>your_speed</th>\n",
|
| 2213 |
+
" <td>825.050818</td>\n",
|
| 2214 |
+
" <td>751.777144</td>\n",
|
| 2215 |
+
" <td>104.527357</td>\n",
|
| 2216 |
+
" <td>617.267514</td>\n",
|
| 2217 |
+
" <td>459.932597</td>\n",
|
| 2218 |
+
" <td>693.742453</td>\n",
|
| 2219 |
+
" <td>125.237980</td>\n",
|
| 2220 |
+
" <td>708.341294</td>\n",
|
| 2221 |
+
" <td>291.592189</td>\n",
|
| 2222 |
+
" <td>833.848608</td>\n",
|
| 2223 |
+
" <td>...</td>\n",
|
| 2224 |
+
" <td>685.281835</td>\n",
|
| 2225 |
+
" <td>731.899168</td>\n",
|
| 2226 |
+
" <td>451.183218</td>\n",
|
| 2227 |
+
" <td>527.764085</td>\n",
|
| 2228 |
+
" <td>292.249519</td>\n",
|
| 2229 |
+
" <td>619.546047</td>\n",
|
| 2230 |
+
" <td>493.576101</td>\n",
|
| 2231 |
+
" <td>982.046031</td>\n",
|
| 2232 |
+
" <td>537.593520</td>\n",
|
| 2233 |
+
" <td>656.393231</td>\n",
|
| 2234 |
+
" </tr>\n",
|
| 2235 |
+
" <tr>\n",
|
| 2236 |
+
" <th>your_altitude</th>\n",
|
| 2237 |
+
" <td>6512.661972</td>\n",
|
| 2238 |
+
" <td>18782.156933</td>\n",
|
| 2239 |
+
" <td>3625.183143</td>\n",
|
| 2240 |
+
" <td>5759.552561</td>\n",
|
| 2241 |
+
" <td>28821.640152</td>\n",
|
| 2242 |
+
" <td>9790.692112</td>\n",
|
| 2243 |
+
" <td>1510.779084</td>\n",
|
| 2244 |
+
" <td>14553.522284</td>\n",
|
| 2245 |
+
" <td>4020.288191</td>\n",
|
| 2246 |
+
" <td>15714.406817</td>\n",
|
| 2247 |
+
" <td>...</td>\n",
|
| 2248 |
+
" <td>4796.945631</td>\n",
|
| 2249 |
+
" <td>4000.690960</td>\n",
|
| 2250 |
+
" <td>7211.059032</td>\n",
|
| 2251 |
+
" <td>2657.273049</td>\n",
|
| 2252 |
+
" <td>16145.505669</td>\n",
|
| 2253 |
+
" <td>23094.841893</td>\n",
|
| 2254 |
+
" <td>24159.528537</td>\n",
|
| 2255 |
+
" <td>26605.676886</td>\n",
|
| 2256 |
+
" <td>22146.398479</td>\n",
|
| 2257 |
+
" <td>1624.780195</td>\n",
|
| 2258 |
+
" </tr>\n",
|
| 2259 |
+
" <tr>\n",
|
| 2260 |
+
" <th>your_maneuverability</th>\n",
|
| 2261 |
+
" <td>1.000000</td>\n",
|
| 2262 |
+
" <td>1.000000</td>\n",
|
| 2263 |
+
" <td>2.000000</td>\n",
|
| 2264 |
+
" <td>1.000000</td>\n",
|
| 2265 |
+
" <td>2.000000</td>\n",
|
| 2266 |
+
" <td>1.000000</td>\n",
|
| 2267 |
+
" <td>1.000000</td>\n",
|
| 2268 |
+
" <td>1.000000</td>\n",
|
| 2269 |
+
" <td>1.000000</td>\n",
|
| 2270 |
+
" <td>0.000000</td>\n",
|
| 2271 |
+
" <td>...</td>\n",
|
| 2272 |
+
" <td>0.000000</td>\n",
|
| 2273 |
+
" <td>2.000000</td>\n",
|
| 2274 |
+
" <td>1.000000</td>\n",
|
| 2275 |
+
" <td>0.000000</td>\n",
|
| 2276 |
+
" <td>0.000000</td>\n",
|
| 2277 |
+
" <td>1.000000</td>\n",
|
| 2278 |
+
" <td>2.000000</td>\n",
|
| 2279 |
+
" <td>1.000000</td>\n",
|
| 2280 |
+
" <td>1.000000</td>\n",
|
| 2281 |
+
" <td>0.000000</td>\n",
|
| 2282 |
+
" </tr>\n",
|
| 2283 |
+
" <tr>\n",
|
| 2284 |
+
" <th>enemy_altitude</th>\n",
|
| 2285 |
+
" <td>3655.798699</td>\n",
|
| 2286 |
+
" <td>19508.648175</td>\n",
|
| 2287 |
+
" <td>29894.396018</td>\n",
|
| 2288 |
+
" <td>15002.849014</td>\n",
|
| 2289 |
+
" <td>4643.721244</td>\n",
|
| 2290 |
+
" <td>26557.889641</td>\n",
|
| 2291 |
+
" <td>11515.140368</td>\n",
|
| 2292 |
+
" <td>4119.563278</td>\n",
|
| 2293 |
+
" <td>2974.936095</td>\n",
|
| 2294 |
+
" <td>28617.057195</td>\n",
|
| 2295 |
+
" <td>...</td>\n",
|
| 2296 |
+
" <td>13657.745440</td>\n",
|
| 2297 |
+
" <td>24585.421109</td>\n",
|
| 2298 |
+
" <td>28243.550517</td>\n",
|
| 2299 |
+
" <td>16566.700223</td>\n",
|
| 2300 |
+
" <td>15476.262795</td>\n",
|
| 2301 |
+
" <td>26693.600073</td>\n",
|
| 2302 |
+
" <td>17534.961728</td>\n",
|
| 2303 |
+
" <td>7711.296412</td>\n",
|
| 2304 |
+
" <td>4263.977523</td>\n",
|
| 2305 |
+
" <td>17091.003655</td>\n",
|
| 2306 |
+
" </tr>\n",
|
| 2307 |
+
" <tr>\n",
|
| 2308 |
+
" <th>missile_speed</th>\n",
|
| 2309 |
+
" <td>857.000000</td>\n",
|
| 2310 |
+
" <td>857.000000</td>\n",
|
| 2311 |
+
" <td>857.000000</td>\n",
|
| 2312 |
+
" <td>857.000000</td>\n",
|
| 2313 |
+
" <td>857.000000</td>\n",
|
| 2314 |
+
" <td>857.000000</td>\n",
|
| 2315 |
+
" <td>857.000000</td>\n",
|
| 2316 |
+
" <td>857.000000</td>\n",
|
| 2317 |
+
" <td>857.000000</td>\n",
|
| 2318 |
+
" <td>857.000000</td>\n",
|
| 2319 |
+
" <td>...</td>\n",
|
| 2320 |
+
" <td>686.000000</td>\n",
|
| 2321 |
+
" <td>1372.000000</td>\n",
|
| 2322 |
+
" <td>1544.000000</td>\n",
|
| 2323 |
+
" <td>857.000000</td>\n",
|
| 2324 |
+
" <td>1372.000000</td>\n",
|
| 2325 |
+
" <td>1372.000000</td>\n",
|
| 2326 |
+
" <td>1372.000000</td>\n",
|
| 2327 |
+
" <td>1372.000000</td>\n",
|
| 2328 |
+
" <td>1372.000000</td>\n",
|
| 2329 |
+
" <td>686.000000</td>\n",
|
| 2330 |
+
" </tr>\n",
|
| 2331 |
+
" <tr>\n",
|
| 2332 |
+
" <th>missile_range</th>\n",
|
| 2333 |
+
" <td>35000.000000</td>\n",
|
| 2334 |
+
" <td>35000.000000</td>\n",
|
| 2335 |
+
" <td>35000.000000</td>\n",
|
| 2336 |
+
" <td>35000.000000</td>\n",
|
| 2337 |
+
" <td>35000.000000</td>\n",
|
| 2338 |
+
" <td>35000.000000</td>\n",
|
| 2339 |
+
" <td>35000.000000</td>\n",
|
| 2340 |
+
" <td>35000.000000</td>\n",
|
| 2341 |
+
" <td>35000.000000</td>\n",
|
| 2342 |
+
" <td>35000.000000</td>\n",
|
| 2343 |
+
" <td>...</td>\n",
|
| 2344 |
+
" <td>8000.000000</td>\n",
|
| 2345 |
+
" <td>160000.000000</td>\n",
|
| 2346 |
+
" <td>105000.000000</td>\n",
|
| 2347 |
+
" <td>40000.000000</td>\n",
|
| 2348 |
+
" <td>160000.000000</td>\n",
|
| 2349 |
+
" <td>65000.000000</td>\n",
|
| 2350 |
+
" <td>160000.000000</td>\n",
|
| 2351 |
+
" <td>200000.000000</td>\n",
|
| 2352 |
+
" <td>110000.000000</td>\n",
|
| 2353 |
+
" <td>8000.000000</td>\n",
|
| 2354 |
+
" </tr>\n",
|
| 2355 |
+
" <tr>\n",
|
| 2356 |
+
" <th>enemy_generation</th>\n",
|
| 2357 |
+
" <td>4.000000</td>\n",
|
| 2358 |
+
" <td>4.000000</td>\n",
|
| 2359 |
+
" <td>4.000000</td>\n",
|
| 2360 |
+
" <td>4.000000</td>\n",
|
| 2361 |
+
" <td>4.000000</td>\n",
|
| 2362 |
+
" <td>4.000000</td>\n",
|
| 2363 |
+
" <td>4.000000</td>\n",
|
| 2364 |
+
" <td>4.000000</td>\n",
|
| 2365 |
+
" <td>4.000000</td>\n",
|
| 2366 |
+
" <td>4.000000</td>\n",
|
| 2367 |
+
" <td>...</td>\n",
|
| 2368 |
+
" <td>4.000000</td>\n",
|
| 2369 |
+
" <td>5.000000</td>\n",
|
| 2370 |
+
" <td>4.500000</td>\n",
|
| 2371 |
+
" <td>4.000000</td>\n",
|
| 2372 |
+
" <td>4.000000</td>\n",
|
| 2373 |
+
" <td>4.500000</td>\n",
|
| 2374 |
+
" <td>4.500000</td>\n",
|
| 2375 |
+
" <td>4.500000</td>\n",
|
| 2376 |
+
" <td>4.000000</td>\n",
|
| 2377 |
+
" <td>4.000000</td>\n",
|
| 2378 |
+
" </tr>\n",
|
| 2379 |
+
" <tr>\n",
|
| 2380 |
+
" <th>countermeasure_deployed</th>\n",
|
| 2381 |
+
" <td>1.000000</td>\n",
|
| 2382 |
+
" <td>0.000000</td>\n",
|
| 2383 |
+
" <td>0.000000</td>\n",
|
| 2384 |
+
" <td>1.000000</td>\n",
|
| 2385 |
+
" <td>1.000000</td>\n",
|
| 2386 |
+
" <td>0.000000</td>\n",
|
| 2387 |
+
" <td>0.000000</td>\n",
|
| 2388 |
+
" <td>1.000000</td>\n",
|
| 2389 |
+
" <td>0.000000</td>\n",
|
| 2390 |
+
" <td>1.000000</td>\n",
|
| 2391 |
+
" <td>...</td>\n",
|
| 2392 |
+
" <td>1.000000</td>\n",
|
| 2393 |
+
" <td>0.000000</td>\n",
|
| 2394 |
+
" <td>0.000000</td>\n",
|
| 2395 |
+
" <td>1.000000</td>\n",
|
| 2396 |
+
" <td>0.000000</td>\n",
|
| 2397 |
+
" <td>1.000000</td>\n",
|
| 2398 |
+
" <td>0.000000</td>\n",
|
| 2399 |
+
" <td>0.000000</td>\n",
|
| 2400 |
+
" <td>0.000000</td>\n",
|
| 2401 |
+
" <td>1.000000</td>\n",
|
| 2402 |
+
" </tr>\n",
|
| 2403 |
+
" <tr>\n",
|
| 2404 |
+
" <th>evasion_time</th>\n",
|
| 2405 |
+
" <td>2.229421</td>\n",
|
| 2406 |
+
" <td>2.772577</td>\n",
|
| 2407 |
+
" <td>3.230825</td>\n",
|
| 2408 |
+
" <td>32.140344</td>\n",
|
| 2409 |
+
" <td>1.308767</td>\n",
|
| 2410 |
+
" <td>4.599058</td>\n",
|
| 2411 |
+
" <td>23.375118</td>\n",
|
| 2412 |
+
" <td>8.922964</td>\n",
|
| 2413 |
+
" <td>0.453692</td>\n",
|
| 2414 |
+
" <td>40.040966</td>\n",
|
| 2415 |
+
" <td>...</td>\n",
|
| 2416 |
+
" <td>2.667103</td>\n",
|
| 2417 |
+
" <td>2.771440</td>\n",
|
| 2418 |
+
" <td>17.229116</td>\n",
|
| 2419 |
+
" <td>8.430119</td>\n",
|
| 2420 |
+
" <td>24.569035</td>\n",
|
| 2421 |
+
" <td>53.653813</td>\n",
|
| 2422 |
+
" <td>10.684553</td>\n",
|
| 2423 |
+
" <td>60.641844</td>\n",
|
| 2424 |
+
" <td>10.753401</td>\n",
|
| 2425 |
+
" <td>3.142886</td>\n",
|
| 2426 |
+
" </tr>\n",
|
| 2427 |
+
" <tr>\n",
|
| 2428 |
+
" <th>hit</th>\n",
|
| 2429 |
+
" <td>0.000000</td>\n",
|
| 2430 |
+
" <td>1.000000</td>\n",
|
| 2431 |
+
" <td>0.000000</td>\n",
|
| 2432 |
+
" <td>0.000000</td>\n",
|
| 2433 |
+
" <td>0.000000</td>\n",
|
| 2434 |
+
" <td>1.000000</td>\n",
|
| 2435 |
+
" <td>1.000000</td>\n",
|
| 2436 |
+
" <td>0.000000</td>\n",
|
| 2437 |
+
" <td>1.000000</td>\n",
|
| 2438 |
+
" <td>0.000000</td>\n",
|
| 2439 |
+
" <td>...</td>\n",
|
| 2440 |
+
" <td>0.000000</td>\n",
|
| 2441 |
+
" <td>1.000000</td>\n",
|
| 2442 |
+
" <td>0.000000</td>\n",
|
| 2443 |
+
" <td>0.000000</td>\n",
|
| 2444 |
+
" <td>1.000000</td>\n",
|
| 2445 |
+
" <td>0.000000</td>\n",
|
| 2446 |
+
" <td>0.000000</td>\n",
|
| 2447 |
+
" <td>1.000000</td>\n",
|
| 2448 |
+
" <td>1.000000</td>\n",
|
| 2449 |
+
" <td>0.000000</td>\n",
|
| 2450 |
+
" </tr>\n",
|
| 2451 |
+
" </tbody>\n",
|
| 2452 |
+
"</table>\n",
|
| 2453 |
+
"<p>16 rows × 1000 columns</p>\n",
|
| 2454 |
+
"</div>"
|
| 2455 |
+
],
|
| 2456 |
+
"text/plain": [
|
| 2457 |
+
" 0 1 2 \\\n",
|
| 2458 |
+
"launch_distance 22570.893367 2367.007196 6602.346475 \n",
|
| 2459 |
+
"remaining_distance 2961.980067 1652.662358 2558.752556 \n",
|
| 2460 |
+
"closure_rate 1185.763992 625.878279 871.179381 \n",
|
| 2461 |
+
"azimuth 61.005549 212.466382 281.112640 \n",
|
| 2462 |
+
"elevation -34.707563 68.630275 45.266055 \n",
|
| 2463 |
+
"missile_phase 2.000000 0.000000 1.000000 \n",
|
| 2464 |
+
"your_speed 825.050818 751.777144 104.527357 \n",
|
| 2465 |
+
"your_altitude 6512.661972 18782.156933 3625.183143 \n",
|
| 2466 |
+
"your_maneuverability 1.000000 1.000000 2.000000 \n",
|
| 2467 |
+
"enemy_altitude 3655.798699 19508.648175 29894.396018 \n",
|
| 2468 |
+
"missile_speed 857.000000 857.000000 857.000000 \n",
|
| 2469 |
+
"missile_range 35000.000000 35000.000000 35000.000000 \n",
|
| 2470 |
+
"enemy_generation 4.000000 4.000000 4.000000 \n",
|
| 2471 |
+
"countermeasure_deployed 1.000000 0.000000 0.000000 \n",
|
| 2472 |
+
"evasion_time 2.229421 2.772577 3.230825 \n",
|
| 2473 |
+
"hit 0.000000 1.000000 0.000000 \n",
|
| 2474 |
+
"\n",
|
| 2475 |
+
" 3 4 5 \\\n",
|
| 2476 |
+
"launch_distance 18281.792405 9834.379665 12661.488033 \n",
|
| 2477 |
+
"remaining_distance 18049.709650 1522.983460 3400.938832 \n",
|
| 2478 |
+
"closure_rate 648.636953 1088.038782 725.990269 \n",
|
| 2479 |
+
"azimuth 115.135435 316.104680 259.107852 \n",
|
| 2480 |
+
"elevation -37.372887 -45.805628 -2.007360 \n",
|
| 2481 |
+
"missile_phase 0.000000 2.000000 2.000000 \n",
|
| 2482 |
+
"your_speed 617.267514 459.932597 693.742453 \n",
|
| 2483 |
+
"your_altitude 5759.552561 28821.640152 9790.692112 \n",
|
| 2484 |
+
"your_maneuverability 1.000000 2.000000 1.000000 \n",
|
| 2485 |
+
"enemy_altitude 15002.849014 4643.721244 26557.889641 \n",
|
| 2486 |
+
"missile_speed 857.000000 857.000000 857.000000 \n",
|
| 2487 |
+
"missile_range 35000.000000 35000.000000 35000.000000 \n",
|
| 2488 |
+
"enemy_generation 4.000000 4.000000 4.000000 \n",
|
| 2489 |
+
"countermeasure_deployed 1.000000 1.000000 0.000000 \n",
|
| 2490 |
+
"evasion_time 32.140344 1.308767 4.599058 \n",
|
| 2491 |
+
"hit 0.000000 0.000000 1.000000 \n",
|
| 2492 |
+
"\n",
|
| 2493 |
+
" 6 7 8 \\\n",
|
| 2494 |
+
"launch_distance 25901.724724 8890.584630 983.734396 \n",
|
| 2495 |
+
"remaining_distance 17955.494364 7886.781225 483.793860 \n",
|
| 2496 |
+
"closure_rate 844.960185 1020.875119 1066.347972 \n",
|
| 2497 |
+
"azimuth 211.731931 283.740414 349.132944 \n",
|
| 2498 |
+
"elevation -83.509892 -13.091010 -43.024865 \n",
|
| 2499 |
+
"missile_phase 0.000000 0.000000 1.000000 \n",
|
| 2500 |
+
"your_speed 125.237980 708.341294 291.592189 \n",
|
| 2501 |
+
"your_altitude 1510.779084 14553.522284 4020.288191 \n",
|
| 2502 |
+
"your_maneuverability 1.000000 1.000000 1.000000 \n",
|
| 2503 |
+
"enemy_altitude 11515.140368 4119.563278 2974.936095 \n",
|
| 2504 |
+
"missile_speed 857.000000 857.000000 857.000000 \n",
|
| 2505 |
+
"missile_range 35000.000000 35000.000000 35000.000000 \n",
|
| 2506 |
+
"enemy_generation 4.000000 4.000000 4.000000 \n",
|
| 2507 |
+
"countermeasure_deployed 0.000000 1.000000 0.000000 \n",
|
| 2508 |
+
"evasion_time 23.375118 8.922964 0.453692 \n",
|
| 2509 |
+
"hit 1.000000 0.000000 1.000000 \n",
|
| 2510 |
+
"\n",
|
| 2511 |
+
" 9 ... 990 991 \\\n",
|
| 2512 |
+
"launch_distance 13859.562006 ... 4981.478801 32056.695360 \n",
|
| 2513 |
+
"remaining_distance 13391.098584 ... 1605.088087 4117.674334 \n",
|
| 2514 |
+
"closure_rate 386.272373 ... 590.826598 1312.773778 \n",
|
| 2515 |
+
"azimuth 176.945221 ... 240.738983 191.345225 \n",
|
| 2516 |
+
"elevation -55.574971 ... 73.492841 -85.265763 \n",
|
| 2517 |
+
"missile_phase 0.000000 ... 2.000000 2.000000 \n",
|
| 2518 |
+
"your_speed 833.848608 ... 685.281835 731.899168 \n",
|
| 2519 |
+
"your_altitude 15714.406817 ... 4796.945631 4000.690960 \n",
|
| 2520 |
+
"your_maneuverability 0.000000 ... 0.000000 2.000000 \n",
|
| 2521 |
+
"enemy_altitude 28617.057195 ... 13657.745440 24585.421109 \n",
|
| 2522 |
+
"missile_speed 857.000000 ... 686.000000 1372.000000 \n",
|
| 2523 |
+
"missile_range 35000.000000 ... 8000.000000 160000.000000 \n",
|
| 2524 |
+
"enemy_generation 4.000000 ... 4.000000 5.000000 \n",
|
| 2525 |
+
"countermeasure_deployed 1.000000 ... 1.000000 0.000000 \n",
|
| 2526 |
+
"evasion_time 40.040966 ... 2.667103 2.771440 \n",
|
| 2527 |
+
"hit 0.000000 ... 0.000000 1.000000 \n",
|
| 2528 |
+
"\n",
|
| 2529 |
+
" 992 993 994 \\\n",
|
| 2530 |
+
"launch_distance 53419.689040 19021.028127 128594.277243 \n",
|
| 2531 |
+
"remaining_distance 26058.452754 3767.541060 40310.301403 \n",
|
| 2532 |
+
"closure_rate 1663.712597 438.758129 1394.591033 \n",
|
| 2533 |
+
"azimuth 72.020487 206.765055 69.911001 \n",
|
| 2534 |
+
"elevation -30.731328 27.430184 -76.994103 \n",
|
| 2535 |
+
"missile_phase 1.000000 2.000000 2.000000 \n",
|
| 2536 |
+
"your_speed 451.183218 527.764085 292.249519 \n",
|
| 2537 |
+
"your_altitude 7211.059032 2657.273049 16145.505669 \n",
|
| 2538 |
+
"your_maneuverability 1.000000 0.000000 0.000000 \n",
|
| 2539 |
+
"enemy_altitude 28243.550517 16566.700223 15476.262795 \n",
|
| 2540 |
+
"missile_speed 1544.000000 857.000000 1372.000000 \n",
|
| 2541 |
+
"missile_range 105000.000000 40000.000000 160000.000000 \n",
|
| 2542 |
+
"enemy_generation 4.500000 4.000000 4.000000 \n",
|
| 2543 |
+
"countermeasure_deployed 0.000000 1.000000 0.000000 \n",
|
| 2544 |
+
"evasion_time 17.229116 8.430119 24.569035 \n",
|
| 2545 |
+
"hit 0.000000 0.000000 1.000000 \n",
|
| 2546 |
+
"\n",
|
| 2547 |
+
" 995 996 997 \\\n",
|
| 2548 |
+
"launch_distance 64845.033514 83532.658041 146555.411805 \n",
|
| 2549 |
+
"remaining_distance 60646.537540 13843.586940 94701.450863 \n",
|
| 2550 |
+
"closure_rate 1186.846963 1211.445463 1803.707956 \n",
|
| 2551 |
+
"azimuth 251.992973 243.510094 41.013277 \n",
|
| 2552 |
+
"elevation -14.818047 -43.173902 -54.366817 \n",
|
| 2553 |
+
"missile_phase 0.000000 2.000000 1.000000 \n",
|
| 2554 |
+
"your_speed 619.546047 493.576101 982.046031 \n",
|
| 2555 |
+
"your_altitude 23094.841893 24159.528537 26605.676886 \n",
|
| 2556 |
+
"your_maneuverability 1.000000 2.000000 1.000000 \n",
|
| 2557 |
+
"enemy_altitude 26693.600073 17534.961728 7711.296412 \n",
|
| 2558 |
+
"missile_speed 1372.000000 1372.000000 1372.000000 \n",
|
| 2559 |
+
"missile_range 65000.000000 160000.000000 200000.000000 \n",
|
| 2560 |
+
"enemy_generation 4.500000 4.500000 4.500000 \n",
|
| 2561 |
+
"countermeasure_deployed 1.000000 0.000000 0.000000 \n",
|
| 2562 |
+
"evasion_time 53.653813 10.684553 60.641844 \n",
|
| 2563 |
+
"hit 0.000000 0.000000 1.000000 \n",
|
| 2564 |
+
"\n",
|
| 2565 |
+
" 998 999 \n",
|
| 2566 |
+
"launch_distance 77971.657118 2253.861997 \n",
|
| 2567 |
+
"remaining_distance 17925.445277 1103.757518 \n",
|
| 2568 |
+
"closure_rate 1636.533985 405.627102 \n",
|
| 2569 |
+
"azimuth 305.568549 117.061738 \n",
|
| 2570 |
+
"elevation -32.225887 -20.136454 \n",
|
| 2571 |
+
"missile_phase 2.000000 1.000000 \n",
|
| 2572 |
+
"your_speed 537.593520 656.393231 \n",
|
| 2573 |
+
"your_altitude 22146.398479 1624.780195 \n",
|
| 2574 |
+
"your_maneuverability 1.000000 0.000000 \n",
|
| 2575 |
+
"enemy_altitude 4263.977523 17091.003655 \n",
|
| 2576 |
+
"missile_speed 1372.000000 686.000000 \n",
|
| 2577 |
+
"missile_range 110000.000000 8000.000000 \n",
|
| 2578 |
+
"enemy_generation 4.000000 4.000000 \n",
|
| 2579 |
+
"countermeasure_deployed 0.000000 1.000000 \n",
|
| 2580 |
+
"evasion_time 10.753401 3.142886 \n",
|
| 2581 |
+
"hit 1.000000 0.000000 \n",
|
| 2582 |
+
"\n",
|
| 2583 |
+
"[16 rows x 1000 columns]"
|
| 2584 |
+
]
|
| 2585 |
+
},
|
| 2586 |
+
"execution_count": 33,
|
| 2587 |
+
"metadata": {},
|
| 2588 |
+
"output_type": "execute_result"
|
| 2589 |
+
}
|
| 2590 |
+
],
|
| 2591 |
+
"source": [
|
| 2592 |
+
"# df.T"
|
| 2593 |
+
]
|
| 2594 |
+
},
|
| 2595 |
+
{
|
| 2596 |
+
"cell_type": "code",
|
| 2597 |
+
"execution_count": null,
|
| 2598 |
+
"id": "afbdd5d1",
|
| 2599 |
+
"metadata": {},
|
| 2600 |
+
"outputs": [
|
| 2601 |
+
{
|
| 2602 |
+
"name": "stdout",
|
| 2603 |
+
"output_type": "stream",
|
| 2604 |
+
"text": [
|
| 2605 |
+
" launch_distance remaining_distance closure_rate evasion_time \\\n",
|
| 2606 |
+
"count 1000.000000 1000.000000 1000.000000 1000.000000 \n",
|
| 2607 |
+
"mean 64915.002129 31821.659117 1266.905104 24.195931 \n",
|
| 2608 |
+
"std 74767.756596 45271.208405 482.164719 31.584890 \n",
|
| 2609 |
+
"min 669.396283 2.403273 4.027710 0.006151 \n",
|
| 2610 |
+
"25% 10515.861139 3803.500738 872.679155 4.102251 \n",
|
| 2611 |
+
"50% 39195.914463 14983.450620 1327.613182 11.872938 \n",
|
| 2612 |
+
"75% 93328.236924 40924.330595 1573.072050 31.334417 \n",
|
| 2613 |
+
"max 395478.660029 389130.451998 2858.542394 284.080232 \n",
|
| 2614 |
+
"\n",
|
| 2615 |
+
" your_speed your_altitude \n",
|
| 2616 |
+
"count 1000.000000 1000.000000 \n",
|
| 2617 |
+
"mean 521.111816 14957.021066 \n",
|
| 2618 |
+
"std 263.082784 8549.393126 \n",
|
| 2619 |
+
"min 62.358418 72.422424 \n",
|
| 2620 |
+
"25% 297.653412 7338.047586 \n",
|
| 2621 |
+
"50% 518.151709 15324.311391 \n",
|
| 2622 |
+
"75% 751.001456 22210.948597 \n",
|
| 2623 |
+
"max 982.046031 29963.768605 \n"
|
| 2624 |
+
]
|
| 2625 |
+
}
|
| 2626 |
+
],
|
| 2627 |
+
"source": [
|
| 2628 |
+
"# # Check feature ranges are within expected bounds\n",
|
| 2629 |
+
"# print(df[['launch_distance', 'remaining_distance', 'closure_rate', \n",
|
| 2630 |
+
"# 'evasion_time', 'your_speed', 'your_altitude']].describe())"
|
| 2631 |
+
]
|
| 2632 |
+
},
|
| 2633 |
+
{
|
| 2634 |
+
"cell_type": "code",
|
| 2635 |
+
"execution_count": null,
|
| 2636 |
+
"id": "ea4cf1bf",
|
| 2637 |
+
"metadata": {},
|
| 2638 |
+
"outputs": [
|
| 2639 |
+
{
|
| 2640 |
+
"name": "stdout",
|
| 2641 |
+
"output_type": "stream",
|
| 2642 |
+
"text": [
|
| 2643 |
+
"remaining > launch: 0\n"
|
| 2644 |
+
]
|
| 2645 |
+
}
|
| 2646 |
+
],
|
| 2647 |
+
"source": [
|
| 2648 |
+
"# # Check remaining_distance never exceeds launch_distance\n",
|
| 2649 |
+
"# print(f\"remaining > launch: {(df['remaining_distance'] > df['launch_distance']).sum()}\")"
|
| 2650 |
+
]
|
| 2651 |
+
},
|
| 2652 |
+
{
|
| 2653 |
+
"cell_type": "code",
|
| 2654 |
+
"execution_count": null,
|
| 2655 |
+
"id": "d16d1952",
|
| 2656 |
+
"metadata": {},
|
| 2657 |
+
"outputs": [
|
| 2658 |
+
{
|
| 2659 |
+
"name": "stdout",
|
| 2660 |
+
"output_type": "stream",
|
| 2661 |
+
"text": [
|
| 2662 |
+
"hit\n",
|
| 2663 |
+
"0 602\n",
|
| 2664 |
+
"1 398\n",
|
| 2665 |
+
"Name: count, dtype: int64\n",
|
| 2666 |
+
"hit\n",
|
| 2667 |
+
"0 0.602\n",
|
| 2668 |
+
"1 0.398\n",
|
| 2669 |
+
"Name: proportion, dtype: float64\n"
|
| 2670 |
+
]
|
| 2671 |
+
}
|
| 2672 |
+
],
|
| 2673 |
+
"source": [
|
| 2674 |
+
"# # Check hit label distribution\n",
|
| 2675 |
+
"# print(df['hit'].value_counts())\n",
|
| 2676 |
+
"# print(df['hit'].value_counts(normalize=True)) # It shows the distribution as percentages instead of counts."
|
| 2677 |
+
]
|
| 2678 |
+
},
|
| 2679 |
+
{
|
| 2680 |
+
"cell_type": "markdown",
|
| 2681 |
+
"id": "6955deb6",
|
| 2682 |
+
"metadata": {},
|
| 2683 |
+
"source": [
|
| 2684 |
+
"- `remaining > launch: 0` - physical constraint holds ✅\n",
|
| 2685 |
+
"- All ranges look sensible - speeds, altitudes, distances all within expected bounds ✅\n",
|
| 2686 |
+
"- evasion_time min is 0.006s (very close missile, terminal phase) - realistic ✅\n",
|
| 2687 |
+
"- **Hit distribution:** 60% miss, 40% hit - reasonable balance, not skewed ✅\n",
|
| 2688 |
+
"\n",
|
| 2689 |
+
"---"
|
| 2690 |
+
]
|
| 2691 |
+
},
|
| 2692 |
+
{
|
| 2693 |
+
"cell_type": "markdown",
|
| 2694 |
+
"id": "a56f6f02",
|
| 2695 |
+
"metadata": {},
|
| 2696 |
+
"source": [
|
| 2697 |
+
"## **Full Dataset Generation**\n",
|
| 2698 |
+
"\n",
|
| 2699 |
+
"Sanity check passed on **1,000** rows, no negatives, no constraint violations.\n",
|
| 2700 |
+
"\n",
|
| 2701 |
+
"Changing `N_ROWS` to `1_000_000` and running full generation."
|
| 2702 |
+
]
|
| 2703 |
+
},
|
| 2704 |
+
{
|
| 2705 |
+
"cell_type": "code",
|
| 2706 |
+
"execution_count": 40,
|
| 2707 |
+
"id": "c8722b7c",
|
| 2708 |
+
"metadata": {},
|
| 2709 |
+
"outputs": [],
|
| 2710 |
+
"source": [
|
| 2711 |
+
"import importlib\n",
|
| 2712 |
+
"import physics_generator\n",
|
| 2713 |
+
"\n",
|
| 2714 |
+
"# Reload the module to pick up any changes made to the script\n",
|
| 2715 |
+
"importlib.reload(physics_generator)\n",
|
| 2716 |
+
"from physics_generator import generate_dataset"
|
| 2717 |
+
]
|
| 2718 |
+
},
|
| 2719 |
+
{
|
| 2720 |
+
"cell_type": "code",
|
| 2721 |
+
"execution_count": 41,
|
| 2722 |
+
"id": "0ef8538e",
|
| 2723 |
+
"metadata": {},
|
| 2724 |
+
"outputs": [
|
| 2725 |
+
{
|
| 2726 |
+
"name": "stdout",
|
| 2727 |
+
"output_type": "stream",
|
| 2728 |
+
"text": [
|
| 2729 |
+
"Done. 1,000,000 rows saved to /mnt/d/Eakem/Learning ML and DS/Project_ATAS/ATAS_Project/data/synthetic_engagements.csv\n"
|
| 2730 |
+
]
|
| 2731 |
+
}
|
| 2732 |
+
],
|
| 2733 |
+
"source": [
|
| 2734 |
+
"# Generate the data with ~1M rows\n",
|
| 2735 |
+
"df_full = generate_dataset()"
|
| 2736 |
+
]
|
| 2737 |
+
},
|
| 2738 |
+
{
|
| 2739 |
+
"cell_type": "markdown",
|
| 2740 |
+
"id": "881b8593",
|
| 2741 |
+
"metadata": {},
|
| 2742 |
+
"source": [
|
| 2743 |
+
"**Now from here moving to Hit and ETA classifier notebooks**\n",
|
| 2744 |
+
"\n",
|
| 2745 |
+
"---"
|
| 2746 |
+
]
|
| 2747 |
+
}
|
| 2748 |
+
],
|
| 2749 |
+
"metadata": {
|
| 2750 |
+
"kernelspec": {
|
| 2751 |
+
"display_name": "env",
|
| 2752 |
+
"language": "python",
|
| 2753 |
+
"name": "python3"
|
| 2754 |
+
},
|
| 2755 |
+
"language_info": {
|
| 2756 |
+
"codemirror_mode": {
|
| 2757 |
+
"name": "ipython",
|
| 2758 |
+
"version": 3
|
| 2759 |
+
},
|
| 2760 |
+
"file_extension": ".py",
|
| 2761 |
+
"mimetype": "text/x-python",
|
| 2762 |
+
"name": "python",
|
| 2763 |
+
"nbconvert_exporter": "python",
|
| 2764 |
+
"pygments_lexer": "ipython3",
|
| 2765 |
+
"version": "3.11.15"
|
| 2766 |
+
}
|
| 2767 |
+
},
|
| 2768 |
+
"nbformat": 4,
|
| 2769 |
+
"nbformat_minor": 5
|
| 2770 |
+
}
|
src/classifier.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
classifier.py
|
| 3 |
+
-------------
|
| 4 |
+
Loads the trained EfficientNetV2-L aircraft classifier and runs inference
|
| 5 |
+
with Test Time Augmentation (TTA).
|
| 6 |
+
|
| 7 |
+
Responsibilities:
|
| 8 |
+
- Set TF_USE_LEGACY_KERAS before any TensorFlow import
|
| 9 |
+
- Load the saved .keras model from disk once at module import time
|
| 10 |
+
- Accept an image file path and return a predicted aircraft class name
|
| 11 |
+
- Apply TTA (N=15) using horizontal flip, vertical flip,
|
| 12 |
+
brightness and contrast jitter
|
| 13 |
+
|
| 14 |
+
Used by: main.py (FastAPI endpoint)
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
# Must be set before any TensorFlow or TF Hub import.
|
| 18 |
+
# The model was trained with tf_keras (Keras 2) and saved in that format.
|
| 19 |
+
# Without this flag, TF Hub's KerasLayer will fail to deserialize correctly
|
| 20 |
+
# on TF 2.13+ which defaults to Keras 3.
|
| 21 |
+
import os
|
| 22 |
+
os.environ["TF_USE_LEGACY_KERAS"] = "1"
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
import tensorflow as tf
|
| 26 |
+
import tensorflow_hub as hub
|
| 27 |
+
from src.schemas import MODEL_PATHS
|
| 28 |
+
import numpy as np
|
| 29 |
+
|
| 30 |
+
# Prevent TF from allocating all VRAM at startup — allocate as needed instead.
|
| 31 |
+
# Skipped automatically on CPU-only environments (e.g. Hugging Face Spaces).
|
| 32 |
+
gpus = tf.config.list_physical_devices('GPU')
|
| 33 |
+
if gpus:
|
| 34 |
+
tf.config.experimental.set_memory_growth(gpus[0], True)
|
| 35 |
+
|
| 36 |
+
# Load the Keras model
|
| 37 |
+
|
| 38 |
+
try:
|
| 39 |
+
classifier_model = tf.keras.models.load_model(
|
| 40 |
+
MODEL_PATHS['classifier'],
|
| 41 |
+
custom_objects={"KerasLayer": hub.KerasLayer}
|
| 42 |
+
)
|
| 43 |
+
print("Classifier model loaded successfully.")
|
| 44 |
+
except FileNotFoundError:
|
| 45 |
+
print(f"Error: model file not found at {MODEL_PATHS['classifier']}")
|
| 46 |
+
except Exception as e:
|
| 47 |
+
print(f"Error loading classifier model: {e}")
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
CLASS_NAMES = [
|
| 51 |
+
'A10', 'A400M', 'AG600', 'AH64', 'AKINCI', 'AV8B', 'An124', 'An22',
|
| 52 |
+
'An225', 'An72', 'B1', 'B2', 'B21', 'B52', 'Be200', 'C1', 'C130',
|
| 53 |
+
'C17', 'C2', 'C390', 'C5', 'CH47', 'CH53', 'CL415', 'E2', 'E7',
|
| 54 |
+
'EF2000', 'EMB314', 'F117', 'F14', 'F15', 'F16', 'F18', 'F2',
|
| 55 |
+
'F22', 'F35', 'F4', 'FCK1', 'H6', 'Il76', 'J10', 'J20', 'J35',
|
| 56 |
+
'J36', 'J50', 'JAS39', 'JF17', 'JH7', 'KAAN', 'KC135', 'KF21',
|
| 57 |
+
'KIZILELMA', 'KJ600', 'Ka27', 'Ka52', 'MQ20', 'MQ25', 'MQ28',
|
| 58 |
+
'MQ9', 'Mi24', 'Mi26', 'Mi28', 'Mi8', 'Mig29', 'Mig31',
|
| 59 |
+
'Mirage2000', 'NH90', 'P3', 'RQ4', 'Rafale', 'SR71', 'Su24',
|
| 60 |
+
'Su25', 'Su34', 'Su47', 'Su57', 'T50', 'TB001', 'TB2', 'Tejas',
|
| 61 |
+
'Tornado', 'Tu160', 'Tu22M', 'Tu95', 'U2', 'UH60', 'US2', 'V22',
|
| 62 |
+
'V280', 'Vulcan', 'WZ10', 'WZ7', 'WZ9', 'X29', 'X32', 'XB70',
|
| 63 |
+
'XQ58', 'Y20', 'YF23', 'Z10', 'Z19'
|
| 64 |
+
]
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
# Create a function to preprocess the image
|
| 68 |
+
def _process_image(image_path:str, image_size=(480, 480)):
|
| 69 |
+
"""
|
| 70 |
+
Load and preprocess a single image from disk.
|
| 71 |
+
- Reads raw bytes from the filepath
|
| 72 |
+
- Decodes into an RGB tensor
|
| 73 |
+
- Resizes to the target image size
|
| 74 |
+
- Normalizes pixel values to [0, 1]
|
| 75 |
+
|
| 76 |
+
Args:
|
| 77 |
+
image_path: Path to the image file (string)
|
| 78 |
+
image_size: Target image size
|
| 79 |
+
|
| 80 |
+
Returns:
|
| 81 |
+
Preprocessed image tensor
|
| 82 |
+
"""
|
| 83 |
+
|
| 84 |
+
# Read the image as raw bytes from the filepath
|
| 85 |
+
image = tf.io.read_file(image_path)
|
| 86 |
+
|
| 87 |
+
# Decode into an RGB tensor
|
| 88 |
+
image = tf.image.decode_jpeg(image, channels=3)
|
| 89 |
+
|
| 90 |
+
# Convert pixel values from 0-255 to 0-1
|
| 91 |
+
image = tf.image.convert_image_dtype(image, tf.float32)
|
| 92 |
+
|
| 93 |
+
# Resize the image to the desired shape
|
| 94 |
+
image = tf.image.resize(image, image_size)
|
| 95 |
+
|
| 96 |
+
return image
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
# Function to apply augmentation to Validation or Test images
|
| 101 |
+
def _tta_augment(image):
|
| 102 |
+
"""
|
| 103 |
+
Applies random augmentations to a single image at inference time.
|
| 104 |
+
Matches training augmentation exactly: flips, brightness, contrast.
|
| 105 |
+
Used during TTA to generate N augmented versions of the same image.
|
| 106 |
+
|
| 107 |
+
Args:
|
| 108 |
+
image: preprocessed image tensor, shape (Hight, Width, 3 channels)
|
| 109 |
+
|
| 110 |
+
Returns:
|
| 111 |
+
Augmented image tensor, same shape as input.
|
| 112 |
+
"""
|
| 113 |
+
|
| 114 |
+
image = tf.image.random_flip_left_right(image)
|
| 115 |
+
image = tf.image.random_flip_up_down(image)
|
| 116 |
+
image = tf.image.random_brightness(image, max_delta=0.1)
|
| 117 |
+
image = tf.image.random_contrast(image, lower=0.9, upper=1.1)
|
| 118 |
+
|
| 119 |
+
return image
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
# Function to do prediction and apply augmentaion during predictions
|
| 123 |
+
|
| 124 |
+
def _tta_predict(model, image_path, n_augments=15):
|
| 125 |
+
"""
|
| 126 |
+
Performs Test-Time Augmentation (TTA) prediction on a single image.
|
| 127 |
+
|
| 128 |
+
This function loads an image, generates multiple augmented versions of it
|
| 129 |
+
(creating an on-the-fly batch), and passes the entire batch through the
|
| 130 |
+
model in a single forward pass. The final output is the average of the
|
| 131 |
+
softmax probabilities across all versions, which improves prediction robustness.
|
| 132 |
+
|
| 133 |
+
Args:
|
| 134 |
+
model (tf.keras.Model): The loaded aircraft classifier model.
|
| 135 |
+
image_path (str or Path): The file path to the input image.
|
| 136 |
+
n_augments (int, optional): The total number of image variations to
|
| 137 |
+
evaluate (1 original + N-1 augmentations). Defaults to 7.
|
| 138 |
+
|
| 139 |
+
Returns:
|
| 140 |
+
numpy.ndarray: A 1D array containing the averaged softmax probability
|
| 141 |
+
vector for the target image.
|
| 142 |
+
"""
|
| 143 |
+
# 1. Load and preprocess the image
|
| 144 |
+
image = _process_image(image_path=image_path)
|
| 145 |
+
|
| 146 |
+
# 2. Build batch of N versions
|
| 147 |
+
versions = [image]
|
| 148 |
+
for _ in range(n_augments - 1):
|
| 149 |
+
versions.append(_tta_augment(image))
|
| 150 |
+
|
| 151 |
+
# 3. Stack into one batch (N, H, W, 3) - default batch size 7
|
| 152 |
+
batch = tf.stack(versions, axis=0)
|
| 153 |
+
|
| 154 |
+
# 4. One predict call
|
| 155 |
+
# FAST INFERENCE: Call the model directly instead of .predict()
|
| 156 |
+
# training=False ensures layers like Dropout and BatchNorm behave correctly for inference (Using a trained model to make predictions on new data)
|
| 157 |
+
predictions = model(batch, training=False).numpy()
|
| 158 |
+
|
| 159 |
+
# predictions = model.predict(batch, verbose=2)
|
| 160 |
+
|
| 161 |
+
# 5. Average the N Softmax vectors
|
| 162 |
+
avg_pred = np.mean(predictions, axis=0)
|
| 163 |
+
|
| 164 |
+
return avg_pred
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
# Function to predict the class name of the aircraft
|
| 170 |
+
def predict_aircraft(image_path:str):
|
| 171 |
+
"""
|
| 172 |
+
Run TTA inference on a single image and return the predicted aircraft class name.
|
| 173 |
+
|
| 174 |
+
Args:
|
| 175 |
+
image_path (str): Path to the input image file.
|
| 176 |
+
|
| 177 |
+
Returns:
|
| 178 |
+
str: Predicted aircraft class name (e.g. 'F22', 'Rafale').
|
| 179 |
+
"""
|
| 180 |
+
|
| 181 |
+
# Make the predictions
|
| 182 |
+
average_tta_predictions = _tta_predict(model=classifier_model,
|
| 183 |
+
image_path=image_path,
|
| 184 |
+
n_augments=15)
|
| 185 |
+
|
| 186 |
+
# Get the maximum probaility class index
|
| 187 |
+
class_label_idx = np.argmax(average_tta_predictions)
|
| 188 |
+
class_label = CLASS_NAMES[class_label_idx]
|
| 189 |
+
|
| 190 |
+
return class_label
|
src/decision.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
decision.py — Tactical Decision Layer
|
| 3 |
+
---------------------------------------
|
| 4 |
+
Receives ETA and hit probability from models.py and converts them into
|
| 5 |
+
a human-readable threat level and tactical recommendation.
|
| 6 |
+
|
| 7 |
+
This module knows nothing about models, physics, or metadata.
|
| 8 |
+
Its only job is: predictions in, decision out.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
from src.schemas import DECISION_THRESHOLDS
|
| 12 |
+
|
| 13 |
+
def get_recommendation(eta_seconds, hit_probability, no_aa_capability, enemy_generation, friendly_generation):
|
| 14 |
+
"""
|
| 15 |
+
Converts model predictions and platform data into a tactical recommendation.
|
| 16 |
+
|
| 17 |
+
Args:
|
| 18 |
+
eta_seconds (float): Predicted evasion time in seconds, clipped at 0.
|
| 19 |
+
hit_probability (float): Probability of hit after evasion maneuvers (0.0 - 1.0).
|
| 20 |
+
no_aa_capability (int): 1 if enemy has no air-to-air capability, 0 otherwise.
|
| 21 |
+
enemy_generation (float): Enemy aircraft generation (e.g. 4.0, 4.5, 5.0).
|
| 22 |
+
friendly_generation (float): Friendly aircraft generation (e.g. 4.0, 4.5, 5.0).
|
| 23 |
+
|
| 24 |
+
Returns:
|
| 25 |
+
str: Tactical recommendation string for the pilot.
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
# Check first air-to-air capability
|
| 29 |
+
if no_aa_capability == 1:
|
| 30 |
+
return "NON-THREAT PLATFORM - MONITOR"
|
| 31 |
+
|
| 32 |
+
# Calculate the generational gap
|
| 33 |
+
gen_gap = friendly_generation - enemy_generation
|
| 34 |
+
|
| 35 |
+
# Define combat posture
|
| 36 |
+
if gen_gap >= DECISION_THRESHOLDS['gen_advantage_threshold']:
|
| 37 |
+
combat_posture = "Friendly advantage"
|
| 38 |
+
elif gen_gap <= -DECISION_THRESHOLDS['gen_disadvantage_threshold']:
|
| 39 |
+
combat_posture = "Enemy advantage"
|
| 40 |
+
else:
|
| 41 |
+
combat_posture = "Parity"
|
| 42 |
+
|
| 43 |
+
# Get the ETA status
|
| 44 |
+
if eta_seconds <= DECISION_THRESHOLDS['eta_critical']:
|
| 45 |
+
eta_status = "Low ETA"
|
| 46 |
+
else:
|
| 47 |
+
eta_status = "High ETA"
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
# Get the Hit probability status
|
| 51 |
+
if hit_probability >= DECISION_THRESHOLDS['hit_prob_high']:
|
| 52 |
+
hit_status = "High hit probability"
|
| 53 |
+
elif DECISION_THRESHOLDS['hit_prob_medium'] <= hit_probability <= DECISION_THRESHOLDS['hit_prob_high']:
|
| 54 |
+
hit_status = "Medium hit probability"
|
| 55 |
+
else:
|
| 56 |
+
hit_status = "Low hit probability"
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
# Get the recommendation based on decisions
|
| 60 |
+
if eta_status == "Low ETA":
|
| 61 |
+
if hit_status == "High hit probability":
|
| 62 |
+
# Critical threat - missile close, high chance of hit
|
| 63 |
+
if combat_posture == "Friendly advantage":
|
| 64 |
+
return "BREAK HARD + DEPLOY CM + COUNTER-ENGAGE"
|
| 65 |
+
elif combat_posture == "Parity":
|
| 66 |
+
return "BREAK HARD + DEPLOY CM"
|
| 67 |
+
else:
|
| 68 |
+
# Outmatched - survive first, disengage
|
| 69 |
+
return "BREAK HARD + DEPLOY CM + DISENGAGE IMMEDIATELY"
|
| 70 |
+
|
| 71 |
+
elif hit_status == "Medium hit probability":
|
| 72 |
+
# Elevated threat - missile close, uncertain outcome
|
| 73 |
+
if combat_posture == "Friendly advantage":
|
| 74 |
+
return "DEPLOY CM + DEFENSIVE MANEUVERS + ENGAGE"
|
| 75 |
+
elif combat_posture == "Parity":
|
| 76 |
+
return "DEPLOY CM + DEFENSIVE MANEUVERS"
|
| 77 |
+
else:
|
| 78 |
+
# Disadvantaged - don't risk engagement
|
| 79 |
+
return "DEPLOY CM + DEFENSIVE MANEUVERS + DISENGAGE"
|
| 80 |
+
|
| 81 |
+
else:
|
| 82 |
+
# Missile close but likely evadable - redirect and reassess
|
| 83 |
+
if combat_posture == "Friendly advantage":
|
| 84 |
+
return "DEPLOY CM + CHANGE FLIGHT PATH + ENGAGE"
|
| 85 |
+
elif combat_posture == "Parity":
|
| 86 |
+
return "DEPLOY CM + CHANGE FLIGHT PATH"
|
| 87 |
+
else:
|
| 88 |
+
return "DEPLOY CM + CHANGE FLIGHT PATH + DISENGAGE"
|
| 89 |
+
|
| 90 |
+
else:
|
| 91 |
+
if hit_status == "High hit probability":
|
| 92 |
+
# Time available but threat is serious - prepare now
|
| 93 |
+
if combat_posture == "Friendly advantage":
|
| 94 |
+
return "DEFENSIVE MANEUVERS + PREPARE CM + COUNTER-ENGAGE"
|
| 95 |
+
elif combat_posture == "Parity":
|
| 96 |
+
return "DEFENSIVE MANEUVERS + PREPARE CM"
|
| 97 |
+
else:
|
| 98 |
+
# Outmatched - use the time to create distance
|
| 99 |
+
return "DEFENSIVE MANEUVERS + PREPARE CM + MAINTAIN DISTANCE"
|
| 100 |
+
|
| 101 |
+
elif hit_status == "Medium hit probability":
|
| 102 |
+
# Uncertain threat with time to monitor - stay ready
|
| 103 |
+
if combat_posture == "Friendly advantage":
|
| 104 |
+
return "PREPARE CM + MONITOR + ENGAGE WHEN READY"
|
| 105 |
+
elif combat_posture == "Parity":
|
| 106 |
+
return "PREPARE CM + MONITOR TRAJECTORY"
|
| 107 |
+
else:
|
| 108 |
+
return "PREPARE CM + MAINTAIN DISTANCE"
|
| 109 |
+
|
| 110 |
+
else:
|
| 111 |
+
# Low threat - missile unlikely to hit, time available
|
| 112 |
+
if combat_posture == "Friendly advantage":
|
| 113 |
+
return "STAY SHARP + CLOSE AND ENGAGE"
|
| 114 |
+
elif combat_posture == "Parity":
|
| 115 |
+
return "STAY SHARP + MONITOR TRAJECTORY"
|
| 116 |
+
else:
|
| 117 |
+
# Disadvantaged even in low threat - keep distance
|
| 118 |
+
return "STAY SHARP + MAINTAIN DISTANCE"
|
src/metadata.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
What is metadata.py
|
| 3 |
+
|
| 4 |
+
When the classifier identifies an aircraft say "F22", the pipeline needs its specs: missile speed, missile range, enemy generation, maneuverability. Those live in your aircraft_metadata.csv.
|
| 5 |
+
metadata.py has one job: take an aircraft name, look it up in the CSV, return its specs as a dictionary.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import pandas as pd
|
| 9 |
+
from src.schemas import BASE_DIR
|
| 10 |
+
|
| 11 |
+
CSV_PATH = BASE_DIR / "data" / "aircraft_metadata.csv"
|
| 12 |
+
|
| 13 |
+
# Load the aircraft metadata
|
| 14 |
+
aircraft_df = pd.read_csv(CSV_PATH)
|
| 15 |
+
|
| 16 |
+
# Function to get the Aircraft specs
|
| 17 |
+
def get_aircraft_metadata(aircraft_name:str) -> dict:
|
| 18 |
+
"""
|
| 19 |
+
Look up aircraft specifications from the metadata CSV by aircraft name.
|
| 20 |
+
|
| 21 |
+
Args:
|
| 22 |
+
aircraft_name (str): Aircraft name (case-insensitive). e.g. "F22", "rafale"
|
| 23 |
+
|
| 24 |
+
Returns:
|
| 25 |
+
dict: All CSV columns for that aircraft. Empty dict if not found.
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
# Upper case everything to search
|
| 29 |
+
aircraft_name = aircraft_name.upper()
|
| 30 |
+
|
| 31 |
+
# Find the row
|
| 32 |
+
aircraft_row = aircraft_df[aircraft_df['aircraft'].str.upper() == aircraft_name]
|
| 33 |
+
|
| 34 |
+
# Handling egde cases
|
| 35 |
+
if not aircraft_row.empty:
|
| 36 |
+
return aircraft_row.iloc[0].to_dict()
|
| 37 |
+
else:
|
| 38 |
+
return {}
|
| 39 |
+
|
| 40 |
+
|
src/models.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
models.py — Inference Layer
|
| 3 |
+
----------------------------
|
| 4 |
+
Loads the trained ETA regressor and hit classifier from disk once at module
|
| 5 |
+
import time. Exposes a single function that accepts a feature dict, converts
|
| 6 |
+
it to an ordered numpy array using FEATURE_COLUMNS, runs both models, and
|
| 7 |
+
returns ETA in seconds and hit probability as a float.
|
| 8 |
+
|
| 9 |
+
This module knows nothing about physics, metadata, or decisions.
|
| 10 |
+
Its only job is: feature array in, predictions out.
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
from src.schemas import FEATURE_COLUMNS, MODEL_PATHS
|
| 15 |
+
from joblib import load
|
| 16 |
+
import numpy as np
|
| 17 |
+
|
| 18 |
+
# Empty dictionary to hold the active loaded model
|
| 19 |
+
loaded_model = {}
|
| 20 |
+
|
| 21 |
+
# Load the model once in the memory
|
| 22 |
+
for model_name, path in MODEL_PATHS.items():
|
| 23 |
+
if model_name in ('eta', 'hit'):
|
| 24 |
+
try:
|
| 25 |
+
loaded_model[model_name] = load(path)
|
| 26 |
+
print(f"Successfully loaded: {model_name} model")
|
| 27 |
+
except FileNotFoundError:
|
| 28 |
+
print(f"Error: The file at {path} could not be found.")
|
| 29 |
+
except Exception as e:
|
| 30 |
+
print(f"An error occurred while loading model {model_name}: {e}")
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
# Function to make predictions using loaded models
|
| 35 |
+
def make_predictions(feature_dict):
|
| 36 |
+
"""
|
| 37 |
+
Converts a feature dict to a numpy array and runs both models.
|
| 38 |
+
|
| 39 |
+
Args:
|
| 40 |
+
feature_dict (dict): 14-feature dict in FEATURE_COLUMNS order,
|
| 41 |
+
produced by build_feature_array().
|
| 42 |
+
|
| 43 |
+
Returns:
|
| 44 |
+
dict: {
|
| 45 |
+
"eta_seconds": float, # predicted evasion time, clipped at 0
|
| 46 |
+
"hit_probability": float # probability of hit after evasion (0.0 - 1.0)
|
| 47 |
+
}
|
| 48 |
+
"""
|
| 49 |
+
|
| 50 |
+
# Convert the dictionary into ndarray using the order of FEATURE_COLUMNS
|
| 51 |
+
feature_values = np.array([feature_dict[col] for col in FEATURE_COLUMNS])
|
| 52 |
+
|
| 53 |
+
# Reshape the the feature_values into (1, 14) as a line of table
|
| 54 |
+
feature_values = feature_values.reshape(1, -1)
|
| 55 |
+
|
| 56 |
+
# Performing predictions
|
| 57 |
+
for model_name, model in loaded_model.items():
|
| 58 |
+
if model_name == 'eta':
|
| 59 |
+
eta_prediction = model.predict(feature_values)
|
| 60 |
+
# Clip the negative time value to 0
|
| 61 |
+
eta_prediction = np.maximum(eta_prediction, 0)[0]
|
| 62 |
+
|
| 63 |
+
else:
|
| 64 |
+
hit_prediction = model.predict_proba(feature_values)[0][1] # [probability of miss, probability of hit]
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
return {
|
| 68 |
+
"eta_seconds": float(eta_prediction),
|
| 69 |
+
"hit_probability": float(hit_prediction)
|
| 70 |
+
}
|
| 71 |
+
|
src/physics_generator.py
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Importing the required libraries
|
| 2 |
+
import numpy as np
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import math
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
# ── Paths ─────────────────────────────────────────────────────────────────────
|
| 8 |
+
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
| 9 |
+
DATA_ROOT = PROJECT_ROOT / "data"
|
| 10 |
+
OUTPUT_PATH = DATA_ROOT / "synthetic_engagements.csv"
|
| 11 |
+
METADATA_PATH = DATA_ROOT / "aircraft_metadata.csv"
|
| 12 |
+
|
| 13 |
+
# ── Generation config ─────────────────────────────────────────────────────────
|
| 14 |
+
N_ROWS = 1_000_000 # Python ignores underscores in numbers, just readability
|
| 15 |
+
|
| 16 |
+
# ── Feature ranges ────────────────────────────────────────────────────────────
|
| 17 |
+
# Launch distance: how far the missile was fired from (metres)
|
| 18 |
+
# Floor is 500m — below this, guns are more effective than missiles and
|
| 19 |
+
# the missile doesn't have enough distance to arm and track properly
|
| 20 |
+
# Upper bound is capped to each aircraft's missile_range at row generation
|
| 21 |
+
LAUNCH_DISTANCE_MIN = 500
|
| 22 |
+
|
| 23 |
+
# Your aircraft speed range - 61 m/s (TB2 drone) → 983 m/s (SR-71), full metadata range
|
| 24 |
+
YOUR_SPEED_RANGE = (61, 983)
|
| 25 |
+
|
| 26 |
+
# Your aircraft altitude (metres)
|
| 27 |
+
# 0 = ground level, 30,000m = upper combat/operational ceiling
|
| 28 |
+
# SR-71 operates at ~24,000m, most fighters top out around 20,000m
|
| 29 |
+
# 30,000m gives headroom for all 102 aircraft in metadata
|
| 30 |
+
YOUR_ALTITUDE_RANGE = (0, 30_000)
|
| 31 |
+
|
| 32 |
+
# Enemy aircraft altitude (metres) — same envelope as your own aircraft
|
| 33 |
+
# Determines vertical geometry of the engagement alongside elevation angle
|
| 34 |
+
ENEMY_ALTITUDE_RANGE = (0, 30_000)
|
| 35 |
+
|
| 36 |
+
# Azimuth: horizontal angle of incoming threat, clockwise from North
|
| 37 |
+
# 0° = head-on, 90° = right side, 180° = tail-chase, 270° = left side
|
| 38 |
+
AZIMUTH_RANGE = (0, 360)
|
| 39 |
+
|
| 40 |
+
# Elevation: vertical angle of incoming threat
|
| 41 |
+
# 0° = same altitude, +90° = directly above, -90° = directly below
|
| 42 |
+
ELEVATION_RANGE = (-90, 90)
|
| 43 |
+
|
| 44 |
+
# Maneuverability: 0 = low (bomber/transport), 1 = medium (older jets),
|
| 45 |
+
# 2 = high (modern fighters)
|
| 46 |
+
MANEUVERABILITY_VALUES = [0, 1, 2]
|
| 47 |
+
|
| 48 |
+
# Countermeasure: 0 = not deployed, 1 = deployed (flares/chaff)
|
| 49 |
+
COUNTERMEASURE_VALUES = [0, 1]
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
# ── Runs once — loads combat-capable aircraft from metadata CSV ───────────────
|
| 53 |
+
def _load_metadata():
|
| 54 |
+
"""
|
| 55 |
+
Loads combat-capable aircraft from the metadata CSV (no_aa_capability == 0).
|
| 56 |
+
|
| 57 |
+
Returns:
|
| 58 |
+
pd.DataFrame: Filtered metadata containing only aircraft with air-to-air missile capability.
|
| 59 |
+
"""
|
| 60 |
+
|
| 61 |
+
if not METADATA_PATH.exists():
|
| 62 |
+
raise FileNotFoundError(f"Metadata CSV not found: {METADATA_PATH}")
|
| 63 |
+
metadata_df = pd.read_csv(METADATA_PATH)
|
| 64 |
+
metadata_df = metadata_df[metadata_df["no_aa_capability"]==0]
|
| 65 |
+
return metadata_df
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
# ── Derives missile phase from how far it has already traveled ────────────────
|
| 69 |
+
# phase 0 = boost (just launched), 1 = mid-course, 2 = terminal (final approach)
|
| 70 |
+
def _derive_missile_phase(remaining_distance, launch_distance):
|
| 71 |
+
"""
|
| 72 |
+
Derives missile flight phase from how much of its journey it has completed.
|
| 73 |
+
|
| 74 |
+
Args:
|
| 75 |
+
remaining_distance (float): Distance remaining between missile and target (metres).
|
| 76 |
+
launch_distance (float): Total distance at the moment of launch (metres).
|
| 77 |
+
|
| 78 |
+
Returns:
|
| 79 |
+
int: 0 = boost, 1 = mid-course, 2 = terminal.
|
| 80 |
+
|
| 81 |
+
Phase 0 — engine burning, accelerating, just launched
|
| 82 |
+
Phase 1 — flying toward your predicted position, guided but not actively tracking you yet
|
| 83 |
+
Phase 2 — active seeker on, tracking you specifically, hardest to fool
|
| 84 |
+
"""
|
| 85 |
+
|
| 86 |
+
traveled_distance = launch_distance - remaining_distance
|
| 87 |
+
ratio = traveled_distance / launch_distance
|
| 88 |
+
|
| 89 |
+
if ratio < 0.33:
|
| 90 |
+
return 0
|
| 91 |
+
elif 0.33 <= ratio < 0.66:
|
| 92 |
+
return 1
|
| 93 |
+
else:
|
| 94 |
+
return 2
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
# ── Derives closure rate using 3D geometry (azimuth + elevation + speeds) ─────
|
| 98 |
+
# How fast the gap between you and the missile is closing, in m/s
|
| 99 |
+
# Enemy altitude adds the vertical dimension to the engagement geometry
|
| 100 |
+
def _derive_closure_rate(missile_speed, your_speed,
|
| 101 |
+
azimuth, elevation):
|
| 102 |
+
"""
|
| 103 |
+
Derives the closure rate - how fast the gap between the missile and the target is closing (m/s).
|
| 104 |
+
|
| 105 |
+
Uses 3D geometry: azimuth accounts for horizontal approach angle,
|
| 106 |
+
elevation accounts for vertical approach angle.
|
| 107 |
+
|
| 108 |
+
Args:
|
| 109 |
+
missile_speed (float): Speed of the incoming missile (m/s).
|
| 110 |
+
your_speed (float): Speed of the friendly aircraft (m/s).
|
| 111 |
+
azimuth (float): Horizontal angle of incoming threat in degrees (0° = head-on).
|
| 112 |
+
elevation (float): Vertical angle of incoming threat in degrees (0° = same altitude).
|
| 113 |
+
|
| 114 |
+
Returns:
|
| 115 |
+
float: Closure rate in m/s.
|
| 116 |
+
"""
|
| 117 |
+
|
| 118 |
+
# Convert the angles into radians and prepare for cosine
|
| 119 |
+
azimuth = math.radians(azimuth)
|
| 120 |
+
elevation = math.radians(elevation)
|
| 121 |
+
|
| 122 |
+
# Extract the closure rate
|
| 123 |
+
closure_rate = missile_speed + (your_speed * (math.cos(azimuth) * math.cos(elevation)))
|
| 124 |
+
|
| 125 |
+
return closure_rate
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
"""
|
| 129 |
+
Note: enemy_speed is not included as a feature. This is a known simplification, in real engagements,
|
| 130 |
+
enemy aircraft velocity at launch contributes to effective missile speed. This can be added in a future iteration
|
| 131 |
+
of the physics generator.
|
| 132 |
+
"""
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def _derive_evasion_time(remaining_distance, closure_rate,
|
| 136 |
+
missile_phase, enemy_generation,
|
| 137 |
+
your_speed, your_altitude, enemy_altitude):
|
| 138 |
+
"""
|
| 139 |
+
Derives the minimum evasion time - seconds before the missile reaches you.
|
| 140 |
+
|
| 141 |
+
Base calculation is pure kinematics: remaining_distance / closure_rate.
|
| 142 |
+
Four modifiers are applied to account for factors the base formula cannot capture.
|
| 143 |
+
|
| 144 |
+
Modifiers:
|
| 145 |
+
- missile_phase == 2 (terminal): seeker has locked on, countermeasures
|
| 146 |
+
need 2-3s overhead to be effective. Shrinks window by 15%. (x 0.85)
|
| 147 |
+
- enemy_generation == 5: HOBS (High Off-Boresight)
|
| 148 |
+
seeker + ECCM make the missile harder to
|
| 149 |
+
defeat, compressing effective reaction time by ~10%. (x 0.90)
|
| 150 |
+
- your_speed > 522 m/s (above median): high energy state gives more
|
| 151 |
+
lateral geometry per second during evasion. Slight expansion. (x 1.05)
|
| 152 |
+
- abs(your_altitude - enemy_altitude) > 5000m: large altitude gap pushes
|
| 153 |
+
engagement toward edge of missile performance envelope, degrading
|
| 154 |
+
terminal accuracy. Slight expansion. (x 1.10)
|
| 155 |
+
|
| 156 |
+
Args:
|
| 157 |
+
remaining_distance (float): Distance between missile and you right now (metres).
|
| 158 |
+
closure_rate (float): Combined closing speed from _derive_closure_rate() (m/s).
|
| 159 |
+
missile_phase (int): 0 = boost, 1 = mid-course, 2 = terminal.
|
| 160 |
+
enemy_generation (float): Enemy aircraft generation (3.5, 4, 4.5, or 5).
|
| 161 |
+
your_speed (float): Your current airspeed (m/s).
|
| 162 |
+
your_altitude (float): Your current altitude (metres).
|
| 163 |
+
enemy_altitude (float): Enemy aircraft altitude (metres).
|
| 164 |
+
|
| 165 |
+
Returns:
|
| 166 |
+
float: Evasion time in seconds.
|
| 167 |
+
"""
|
| 168 |
+
|
| 169 |
+
# Calculate the evasion time
|
| 170 |
+
evasion_time = remaining_distance / closure_rate
|
| 171 |
+
|
| 172 |
+
# Terminal phase - seeker locked on, countermeasures need 2–3s overhead
|
| 173 |
+
if missile_phase == 2:
|
| 174 |
+
evasion_time *= 0.85
|
| 175 |
+
|
| 176 |
+
# Gen 5 aircraft missile (HOBS + ECCM) compresses effective reaction time
|
| 177 |
+
if enemy_generation == 5:
|
| 178 |
+
evasion_time *= 0.90
|
| 179 |
+
|
| 180 |
+
# High speed — more room to maneuver
|
| 181 |
+
if your_speed > 522:
|
| 182 |
+
evasion_time *= 1.05
|
| 183 |
+
|
| 184 |
+
# Large altitude gap - missile at edge of performance envelope
|
| 185 |
+
if abs(your_altitude - enemy_altitude) > 5000:
|
| 186 |
+
evasion_time *= 1.10
|
| 187 |
+
|
| 188 |
+
return evasion_time
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
def _derive_hit_label(countermeasure_deployed, your_maneuverability,
|
| 193 |
+
azimuth, elevation, missile_phase, enemy_generation,
|
| 194 |
+
your_altitude, enemy_altitude):
|
| 195 |
+
"""
|
| 196 |
+
Derives whether the missile hits after an evasion attempt.
|
| 197 |
+
|
| 198 |
+
Starts from a neutral survival score of 0.5. Modifiers push it up (more
|
| 199 |
+
likely to hit) or down (more likely to miss). Final label is 1 if the
|
| 200 |
+
missile hits, 0 if it misses.
|
| 201 |
+
|
| 202 |
+
This is where azimuth and elevation belong — they affect survival after
|
| 203 |
+
evasion, not when the missile arrives (that is handled by closure_rate
|
| 204 |
+
in _derive_evasion_time).
|
| 205 |
+
|
| 206 |
+
Modifiers:
|
| 207 |
+
- countermeasure_deployed == 1: active countermeasures significantly
|
| 208 |
+
reduce hit probability. Score drops.
|
| 209 |
+
- your_maneuverability == 2: high maneuverability makes evasion more
|
| 210 |
+
effective. Score drops.
|
| 211 |
+
- azimuth near 0° (head-on): least time and geometry to evade.
|
| 212 |
+
Score rises.
|
| 213 |
+
- missile_phase == 2 (terminal): seeker locked on, hardest to defeat.
|
| 214 |
+
Score rises.
|
| 215 |
+
- enemy_generation == 5: HOBS + ECCM make the missile more lethal.
|
| 216 |
+
Score rises.
|
| 217 |
+
- elevation far from 0°: steep approach angle reduces evasion options.
|
| 218 |
+
Score rises.
|
| 219 |
+
- large altitude differential > 5000m: pushes missile toward edge of
|
| 220 |
+
performance envelope. Score drops.
|
| 221 |
+
|
| 222 |
+
Args:
|
| 223 |
+
countermeasure_deployed (int): 0 = not deployed, 1 = deployed.
|
| 224 |
+
your_maneuverability (int): 0 = low, 1 = medium, 2 = high.
|
| 225 |
+
azimuth (float): Horizontal angle of incoming threat in degrees (0° = head-on).
|
| 226 |
+
elevation (float): Vertical angle of incoming threat in degrees (0° = level).
|
| 227 |
+
missile_phase (int): 0 = boost, 1 = mid-course, 2 = terminal.
|
| 228 |
+
enemy_generation (float): Enemy aircraft generation (3.5, 4, 4.5, or 5).
|
| 229 |
+
your_altitude (float): Your current altitude (metres).
|
| 230 |
+
enemy_altitude (float): Enemy aircraft altitude (metres).
|
| 231 |
+
|
| 232 |
+
Returns:
|
| 233 |
+
int: 1 if missile hits, 0 if missile misses.
|
| 234 |
+
"""
|
| 235 |
+
|
| 236 |
+
# Neutral starting score - modifiers push it toward hit or miss
|
| 237 |
+
score = 0.5
|
| 238 |
+
|
| 239 |
+
# Countermeasures active - significantly reduces hit chance
|
| 240 |
+
if countermeasure_deployed == 1:
|
| 241 |
+
score -= 0.20
|
| 242 |
+
|
| 243 |
+
# High maneuverability - evasion more effective
|
| 244 |
+
if your_maneuverability == 2:
|
| 245 |
+
score -= 0.10
|
| 246 |
+
|
| 247 |
+
# Head-on approach - least time and geometry to evade
|
| 248 |
+
if azimuth < 30:
|
| 249 |
+
score += 0.15
|
| 250 |
+
|
| 251 |
+
# Terminal phase - seeker locked on, hardest to defeat
|
| 252 |
+
if missile_phase == 2:
|
| 253 |
+
score += 0.15
|
| 254 |
+
|
| 255 |
+
# Gen 5 missile - HOBS + ECCM make it more lethal
|
| 256 |
+
if enemy_generation == 5:
|
| 257 |
+
score += 0.10
|
| 258 |
+
|
| 259 |
+
# Steep approach angle - reduces evasion options
|
| 260 |
+
if abs(elevation) > 45:
|
| 261 |
+
score += 0.10
|
| 262 |
+
|
| 263 |
+
# Large altitude gap - missile at edge of performance envelope
|
| 264 |
+
if abs(your_altitude - enemy_altitude) > 5000:
|
| 265 |
+
score -= 0.10
|
| 266 |
+
|
| 267 |
+
# Hit if score crosses 0.5
|
| 268 |
+
return 1 if score >= 0.5 else 0
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
# ── Generates one complete engagement scenario as a dict ─────────────────────
|
| 272 |
+
def _generate_row(aircraft_row):
|
| 273 |
+
"""
|
| 274 |
+
Generates one synthetic engagement scenario for a given aircraft.
|
| 275 |
+
|
| 276 |
+
Samples random values for all situational features, derives computed
|
| 277 |
+
features using physics, and returns a complete row with labels.
|
| 278 |
+
|
| 279 |
+
Args:
|
| 280 |
+
aircraft_row (pd.Series): One row from the metadata CSV.
|
| 281 |
+
|
| 282 |
+
Returns:
|
| 283 |
+
dict: All 14 features plus evasion_time and hit labels.
|
| 284 |
+
"""
|
| 285 |
+
|
| 286 |
+
# Sample situational features
|
| 287 |
+
your_speed = np.random.uniform(*YOUR_SPEED_RANGE)
|
| 288 |
+
your_altitude = np.random.uniform(*YOUR_ALTITUDE_RANGE)
|
| 289 |
+
enemy_altitude = np.random.uniform(*ENEMY_ALTITUDE_RANGE)
|
| 290 |
+
azimuth = np.random.uniform(*AZIMUTH_RANGE)
|
| 291 |
+
elevation = np.random.uniform(*ELEVATION_RANGE)
|
| 292 |
+
|
| 293 |
+
# Sample categorical features
|
| 294 |
+
your_maneuverability = np.random.choice(MANEUVERABILITY_VALUES)
|
| 295 |
+
countermeasure_deployed = np.random.choice(COUNTERMEASURE_VALUES)
|
| 296 |
+
|
| 297 |
+
# Pull threat specs from metadata
|
| 298 |
+
missile_speed = aircraft_row["missile_speed"]
|
| 299 |
+
missile_range = aircraft_row["missile_range"]
|
| 300 |
+
enemy_generation = aircraft_row["enemy_generation"]
|
| 301 |
+
|
| 302 |
+
# Sample engagement distances
|
| 303 |
+
launch_distance = np.random.uniform(LAUNCH_DISTANCE_MIN, missile_range)
|
| 304 |
+
remaining_distance = np.random.uniform(0, launch_distance) # remaining_distance <= launch_distance always
|
| 305 |
+
|
| 306 |
+
# Derive computed features
|
| 307 |
+
missile_phase = _derive_missile_phase(remaining_distance, launch_distance)
|
| 308 |
+
closure_rate = _derive_closure_rate(missile_speed, your_speed,
|
| 309 |
+
azimuth, elevation)
|
| 310 |
+
|
| 311 |
+
# Derive labels
|
| 312 |
+
evasion_time = _derive_evasion_time(remaining_distance, closure_rate,
|
| 313 |
+
missile_phase, enemy_generation,
|
| 314 |
+
your_speed, your_altitude, enemy_altitude)
|
| 315 |
+
hit = _derive_hit_label(countermeasure_deployed, your_maneuverability,
|
| 316 |
+
azimuth, elevation, missile_phase, enemy_generation,
|
| 317 |
+
your_altitude, enemy_altitude)
|
| 318 |
+
|
| 319 |
+
# Return complete row
|
| 320 |
+
return {
|
| 321 |
+
"launch_distance": launch_distance,
|
| 322 |
+
"remaining_distance": remaining_distance,
|
| 323 |
+
"closure_rate": closure_rate,
|
| 324 |
+
"azimuth": azimuth,
|
| 325 |
+
"elevation": elevation,
|
| 326 |
+
"missile_phase": missile_phase,
|
| 327 |
+
"your_speed": your_speed,
|
| 328 |
+
"your_altitude": your_altitude,
|
| 329 |
+
"your_maneuverability": your_maneuverability,
|
| 330 |
+
"enemy_altitude": enemy_altitude,
|
| 331 |
+
"missile_speed": missile_speed,
|
| 332 |
+
"missile_range": missile_range,
|
| 333 |
+
"enemy_generation": enemy_generation,
|
| 334 |
+
"countermeasure_deployed": countermeasure_deployed,
|
| 335 |
+
"evasion_time": evasion_time,
|
| 336 |
+
"hit": hit
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
|
| 341 |
+
# ── Saves completed DataFrame to CSV ─────────────────────────────────────────
|
| 342 |
+
def _save_dataset(df):
|
| 343 |
+
"""
|
| 344 |
+
Saves the generated dataset to a CSV file.
|
| 345 |
+
|
| 346 |
+
Args:
|
| 347 |
+
df (pd.DataFrame): The complete synthetic engagement dataset.
|
| 348 |
+
"""
|
| 349 |
+
|
| 350 |
+
# Create data directory if it doesn't exist
|
| 351 |
+
OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True)
|
| 352 |
+
|
| 353 |
+
# Save to CSV
|
| 354 |
+
df.to_csv(OUTPUT_PATH, index=False)
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
# ── Public entry point — the only function the notebook calls ─────────────────
|
| 358 |
+
def generate_dataset():
|
| 359 |
+
"""
|
| 360 |
+
Generates 1,000,000 synthetic engagement scenarios and saves to CSV.
|
| 361 |
+
|
| 362 |
+
Loads combat-capable aircraft from metadata, samples each aircraft
|
| 363 |
+
equally across all rows, derives all features and labels using physics,
|
| 364 |
+
and writes the final dataset to disk.
|
| 365 |
+
|
| 366 |
+
Only valid threat scenarios are included (closure_rate > 0).
|
| 367 |
+
|
| 368 |
+
Returns:
|
| 369 |
+
pd.DataFrame: The complete synthetic engagement dataset.
|
| 370 |
+
"""
|
| 371 |
+
metadata = _load_metadata()
|
| 372 |
+
|
| 373 |
+
rows_per_aircraft = N_ROWS // len(metadata) # 1_000_000 // 56 → 17,857 rows per aircraft
|
| 374 |
+
rows = []
|
| 375 |
+
|
| 376 |
+
for _, aircraft_row in metadata.iterrows():
|
| 377 |
+
count = 0
|
| 378 |
+
while count < rows_per_aircraft:
|
| 379 |
+
row = _generate_row(aircraft_row)
|
| 380 |
+
if row["closure_rate"] > 0:
|
| 381 |
+
rows.append(row)
|
| 382 |
+
count += 1
|
| 383 |
+
|
| 384 |
+
# Fill remaining rows to hit exactly N_ROWS
|
| 385 |
+
remaining_rows = N_ROWS - len(rows)
|
| 386 |
+
if remaining_rows:
|
| 387 |
+
count = 0
|
| 388 |
+
while count < remaining_rows:
|
| 389 |
+
row = _generate_row(metadata.sample(1).iloc[0])
|
| 390 |
+
if row["closure_rate"] > 0:
|
| 391 |
+
rows.append(row)
|
| 392 |
+
count += 1
|
| 393 |
+
|
| 394 |
+
df = pd.DataFrame(rows)
|
| 395 |
+
_save_dataset(df)
|
| 396 |
+
print(f"Done. {len(rows):,} rows saved to {OUTPUT_PATH}")
|
| 397 |
+
|
| 398 |
+
|
| 399 |
+
|
| 400 |
+
# Inference only - assembles 14-feature array from HUD inputs and metadata for model prediction
|
| 401 |
+
def build_feature_array(
|
| 402 |
+
# From HUD sliders
|
| 403 |
+
launch_distance, remaining_distance,
|
| 404 |
+
azimuth, elevation,
|
| 405 |
+
your_speed, your_altitude, enemy_altitude,
|
| 406 |
+
countermeasure_deployed,
|
| 407 |
+
# From metadata lookup (enemy aircraft)
|
| 408 |
+
missile_speed, missile_range, enemy_generation,
|
| 409 |
+
# From metadata lookup (friendly aircraft)
|
| 410 |
+
your_maneuverability
|
| 411 |
+
):
|
| 412 |
+
"""
|
| 413 |
+
Assembles the 14-feature array required by the ETA and hit models at inference time.
|
| 414 |
+
|
| 415 |
+
All inputs arrive pre-collected from the HUD sliders and metadata lookups.
|
| 416 |
+
Closure rate and missile phase are derived here from those inputs using the
|
| 417 |
+
same physics helpers used during training. The returned dict is in the exact
|
| 418 |
+
column order the models were trained on.
|
| 419 |
+
|
| 420 |
+
Args:
|
| 421 |
+
launch_distance (float): Distance at moment of missile launch (metres).
|
| 422 |
+
remaining_distance (float): Distance remaining between missile and target (metres).
|
| 423 |
+
azimuth (float): Horizontal angle of incoming threat in degrees (0 = head-on).
|
| 424 |
+
elevation (float): Vertical angle of incoming threat in degrees (0 = level).
|
| 425 |
+
your_speed (float): Friendly aircraft airspeed (m/s).
|
| 426 |
+
your_altitude (float): Friendly aircraft altitude (metres).
|
| 427 |
+
enemy_altitude (float): Enemy aircraft altitude (metres).
|
| 428 |
+
countermeasure_deployed (int): 0 = not deployed, 1 = deployed.
|
| 429 |
+
missile_speed (float): Incoming missile speed (m/s), from enemy metadata.
|
| 430 |
+
missile_range (float): Missile maximum effective range (metres), from enemy metadata.
|
| 431 |
+
enemy_generation (float): Enemy aircraft generation, from enemy metadata.
|
| 432 |
+
your_maneuverability (int): Friendly aircraft maneuverability, from friendly metadata.
|
| 433 |
+
0 = low, 1 = medium, 2 = high. Maneuverability is an aircraft property
|
| 434 |
+
and follows the same metadata lookup pattern for both friendly and enemy platforms.
|
| 435 |
+
|
| 436 |
+
Returns:
|
| 437 |
+
dict: 14 features in training column order, ready for model inference.
|
| 438 |
+
"""
|
| 439 |
+
|
| 440 |
+
# Derive the two features that are computed from inputs rather than sourced directly
|
| 441 |
+
missile_phase = _derive_missile_phase(remaining_distance, launch_distance)
|
| 442 |
+
closure_rate = _derive_closure_rate(missile_speed, your_speed,
|
| 443 |
+
azimuth, elevation)
|
| 444 |
+
|
| 445 |
+
# Assemble and return all 14 features in exact training column order
|
| 446 |
+
return {
|
| 447 |
+
"launch_distance": launch_distance,
|
| 448 |
+
"remaining_distance": remaining_distance,
|
| 449 |
+
"closure_rate": closure_rate,
|
| 450 |
+
"azimuth": azimuth,
|
| 451 |
+
"elevation": elevation,
|
| 452 |
+
"missile_phase": missile_phase,
|
| 453 |
+
"your_speed": your_speed,
|
| 454 |
+
"your_altitude": your_altitude,
|
| 455 |
+
"your_maneuverability": your_maneuverability,
|
| 456 |
+
"enemy_altitude": enemy_altitude,
|
| 457 |
+
"missile_speed": missile_speed,
|
| 458 |
+
"missile_range": missile_range,
|
| 459 |
+
"enemy_generation": enemy_generation,
|
| 460 |
+
"countermeasure_deployed": countermeasure_deployed
|
| 461 |
+
}
|
src/schemas.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
What is schemas.py
|
| 3 |
+
|
| 4 |
+
A single Python file that holds shared constants — column names, their order, and any fixed values the pipeline depends on.
|
| 5 |
+
|
| 6 |
+
Why it exists
|
| 7 |
+
|
| 8 |
+
Three files will each build or consume the 14-feature array: physics_generator.py, models.py, and main.py. If each one hardcodes its own column list, one typo in one file breaks the whole pipeline silently.
|
| 9 |
+
schemas.py means they all import from the same list. One source of truth.
|
| 10 |
+
|
| 11 |
+
What goes inside it
|
| 12 |
+
|
| 13 |
+
Three things:
|
| 14 |
+
FEATURE_COLUMNS - the 14 feature names in exact training order
|
| 15 |
+
MODEL_PATHS - file paths to your two saved .joblib models
|
| 16 |
+
DECISION_THRESHOLDS - the ETA and hit probability cutoffs your decision layer will use
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
from pathlib import Path
|
| 21 |
+
|
| 22 |
+
# Path pointing towards project ATAS main folder
|
| 23 |
+
BASE_DIR = Path(__file__).parent.parent
|
| 24 |
+
# Path pointing towards saved models
|
| 25 |
+
MODEL_DIR = BASE_DIR / "release_models"
|
| 26 |
+
|
| 27 |
+
# Feature columns to be feed to models
|
| 28 |
+
FEATURE_COLUMNS = [
|
| 29 |
+
'launch_distance', 'remaining_distance', 'closure_rate', 'azimuth', 'elevation',
|
| 30 |
+
'missile_phase', 'your_speed', 'your_altitude', 'your_maneuverability',
|
| 31 |
+
'enemy_altitude', 'missile_speed', 'missile_range', 'enemy_generation',
|
| 32 |
+
'countermeasure_deployed'
|
| 33 |
+
]
|
| 34 |
+
|
| 35 |
+
# Model paths as dict to use them to make predictions
|
| 36 |
+
MODEL_PATHS = {
|
| 37 |
+
'classifier' : MODEL_DIR / "aircraft_classifier" / "atas_final_fine_tuned_aircraft_classifier_model.keras",
|
| 38 |
+
'eta' : MODEL_DIR / "eta" / "atas_final_eta_regressor_model.joblib",
|
| 39 |
+
'hit' : MODEL_DIR / "hit" / "atas_final_hit_classifier_model.joblib"
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
# Cutoffs used by decision.py to convert model outputs into pilot recommendations
|
| 43 |
+
DECISION_THRESHOLDS = {
|
| 44 |
+
'hit_prob_high': 0.75,
|
| 45 |
+
'hit_prob_medium': 0.60,
|
| 46 |
+
'eta_critical': 10,
|
| 47 |
+
'gen_advantage_threshold': 0.5, # friendly gen must be this much higher to be considered advantaged
|
| 48 |
+
'gen_disadvantage_threshold': 0.5 # enemy gen must be this much higher to be considered disadvantaged
|
| 49 |
+
}
|