import type { Aircraft, Ship } from '../types'; // T0 = main strike moment const T0 = new Date('2026-03-01T12:01:00Z').getTime(); const HOUR = 3600_000; const DEG2RAD = Math.PI / 180; // ── Waypoint system ────────────────────────────────── // Each waypoint: [hoursFromT0, lat, lng] type WP = [number, number, number]; // Interpolate position along waypoints at a given time function interpWaypoints(wps: WP[], hoursFromT0: number): { lat: number; lng: number; heading: number } { // Clamp to waypoint range if (hoursFromT0 <= wps[0][0]) { const next = wps.length > 1 ? wps[1] : wps[0]; return { lat: wps[0][1], lng: wps[0][2], heading: calcHeading(wps[0][1], wps[0][2], next[1], next[2]) }; } if (hoursFromT0 >= wps[wps.length - 1][0]) { const prev = wps.length > 1 ? wps[wps.length - 2] : wps[wps.length - 1]; const last = wps[wps.length - 1]; return { lat: last[1], lng: last[2], heading: calcHeading(prev[1], prev[2], last[1], last[2]) }; } // Find segment for (let i = 0; i < wps.length - 1; i++) { const [t0, lat0, lng0] = wps[i]; const [t1, lat1, lng1] = wps[i + 1]; if (hoursFromT0 >= t0 && hoursFromT0 <= t1) { const frac = (hoursFromT0 - t0) / (t1 - t0); const lat = lat0 + (lat1 - lat0) * frac; const lng = lng0 + (lng1 - lng0) * frac; const heading = calcHeading(lat0, lng0, lat1, lng1); return { lat, lng, heading }; } } // Fallback return { lat: wps[0][1], lng: wps[0][2], heading: 0 }; } function calcHeading(lat1: number, lng1: number, lat2: number, lng2: number): number { const dLng = (lng2 - lng1) * DEG2RAD; const y = Math.sin(dLng) * Math.cos(lat2 * DEG2RAD); const x = Math.cos(lat1 * DEG2RAD) * Math.sin(lat2 * DEG2RAD) - Math.sin(lat1 * DEG2RAD) * Math.cos(lat2 * DEG2RAD) * Math.cos(dLng); return ((Math.atan2(y, x) / DEG2RAD) + 360) % 360; } // Generate trail by sampling recent positions function generateTrail(wps: WP[], hoursFromT0: number, count: number, intervalMin: number): [number, number][] { const trail: [number, number][] = []; for (let i = count; i >= 0; i--) { const t = hoursFromT0 - (i * intervalMin) / 60; const pos = interpWaypoints(wps, t); trail.push([pos.lat, pos.lng]); } return trail; } // ── Aircraft Flight Plans ──────────────────────────── // Waypoints: [hoursFromT0, lat, lng] const FLIGHT_PLANS: Record = { // ── SURVEILLANCE / ISR ── // FORTE12 (RQ-4B Global Hawk) - 24h ISR racetrack over Iraq/Iran border 'ae1461': [ [-12, 33.0, 42.0], [-10, 34.0, 44.0], [-8, 33.0, 46.0], [-6, 34.0, 44.0], [-4, 33.0, 42.0], [-2, 34.0, 44.0], [0, 33.5, 45.0], [2, 33.0, 42.0], [4, 34.0, 44.0], [6, 33.0, 46.0], [8, 34.0, 44.0], [10, 33.0, 42.0], [12, 34.0, 44.0], ], // FORTE13 (MQ-4C Triton) - Maritime ISR racetrack over Persian Gulf 'ae1462': [ [-10, 27.0, 54.0], [-8, 26.5, 56.0], [-6, 27.5, 55.0], [-4, 26.5, 56.5], [-2, 27.0, 54.0], [0, 26.5, 56.0], [2, 27.5, 55.0], [4, 26.5, 56.5], [6, 27.0, 54.0], [8, 26.5, 56.0], [10, 27.5, 55.0], ], // TOXIN31 (RC-135V) - SIGINT orbit from Crete over Eastern Med 'ae5420': [ [-10, 35.5, 26.0], [-8, 35.0, 30.0], [-6, 35.5, 34.0], [-4, 35.0, 30.0], [-2, 35.5, 26.0], [0, 35.0, 30.0], [2, 35.5, 34.0], [4, 35.0, 30.0], [6, 35.5, 26.0], [8, 35.0, 30.0], ], // SNTRY60 (E-3G AWACS) - AEW orbit over northern Iraq 'ae0005': [ [-6, 35.0, 43.0], [-4, 34.5, 44.5], [-2, 35.0, 43.0], [-1, 34.5, 44.5], [0, 35.0, 43.5], [1, 34.5, 44.5], [2, 35.0, 43.0], [4, 34.5, 44.5], [6, 35.0, 43.0], [8, 34.5, 44.5], [10, 35.0, 43.0], ], // DRAGON01 (U-2S) - Ultra-high altitude recon over Iran 'ae0006': [ [-8, 24.2, 54.7], [-6, 28.0, 52.0], [-4, 31.0, 53.0], [-2, 33.0, 52.0], [0, 30.5, 52.0], [2, 28.0, 53.0], [4, 25.0, 54.0], ], // REAPER41 (MQ-9A) - Armed ISR racetrack over western Iraq 'ae0007': [ [-6, 33.0, 42.0], [-4, 32.0, 41.0], [-2, 33.0, 42.5], [0, 32.0, 41.5], [2, 33.0, 42.0], [4, 32.0, 41.0], [6, 33.0, 42.5], [8, 32.0, 41.5], ], // REAPER42 (MQ-9A) - Armed ISR over Strait of Hormuz 'ae0008': [ [-4, 26.5, 56.5], [-2, 27.0, 55.5], [0, 26.5, 56.0], [2, 27.0, 55.5], [4, 26.5, 56.5], [6, 27.0, 55.5], [8, 26.5, 56.0], [10, 27.0, 55.5], ], // ── TANKERS ── // ETHYL71 (KC-135R) - Refueling orbit over western Iraq 'ae0001': [ [-8, 32.5, 40.0], [-6, 31.5, 41.5], [-4, 32.5, 40.0], [-2, 31.5, 41.5], [-1, 32.0, 40.5], [0, 31.5, 41.5], [1, 32.5, 40.0], [3, 31.5, 41.5], [5, 32.5, 40.0], [6, 32.0, 40.5], ], // STEEL55 (KC-46A) - B-2 tanker support orbit over northern Gulf 'ae0009': [ [-10, 28.0, 49.0], [-8, 29.0, 50.5], [-6, 28.0, 49.0], [-4, 29.0, 50.5], [-2, 28.0, 49.0], [0, 29.0, 50.5], [2, 28.0, 49.0], [4, 28.5, 50.0], ], // PACK22 (KC-135R) - Refueling over eastern Jordan (Israeli strikes) 'ae0003': [ [-3, 32.0, 37.5], [-1.5, 33.0, 39.0], [0, 32.0, 37.5], [1.5, 33.0, 39.0], [3, 32.0, 37.5], [4, 32.5, 38.0], ], // NCHO45 (KC-10A) - Refueling orbit over Kuwait/northern Gulf 'ae0002': [ [-4, 29.5, 47.0], [-2, 30.2, 48.5], [0, 29.5, 47.0], [1, 30.2, 48.5], [3, 29.5, 47.0], [5, 30.2, 48.5], ], // ── BOMBERS ── // DEATH11 (B-2A Spirit) - Ingress from south, strike Tehran/Isfahan, egress 'ae2001': [ [-6, 22.0, 58.0], [-4, 25.0, 55.0], [-2, 28.0, 53.0], [-1, 30.0, 52.0], [0, 32.5, 51.5], [1, 30.0, 53.0], [2, 26.0, 56.0], ], // DEATH12 (B-2A Spirit) - Second bomber, offset route 'ae2002': [ [-6, 21.5, 59.0], [-4, 24.5, 56.0], [-2, 27.5, 53.5], [-1, 29.5, 52.5], [0, 32.0, 52.0], [1, 29.5, 53.5], [2, 25.5, 57.0], ], // ── US FIGHTERS ── // RAGE01 (F-22A) - Al Udeid scramble, air superiority sweep 'ae3001': [ [-2, 25.2, 51.4], [-1.5, 27.0, 50.0], [-1, 29.0, 48.5], [0, 31.0, 47.0], [1, 32.5, 48.0], [2, 31.0, 47.0], [3, 28.0, 49.0], [4, 25.5, 51.0], ], // RAGE02 (F-22A) - Wingman 'ae3002': [ [-2, 25.0, 51.2], [-1.5, 26.8, 49.8], [-1, 28.8, 48.3], [0, 30.8, 46.8], [1, 32.3, 47.8], [2, 30.8, 46.8], [3, 27.8, 48.8], [4, 25.3, 50.8], ], // VIPER11 (F-35A) - Deep strike into Iran from Al Udeid 'ae3003': [ [-3, 25.2, 51.4], [-2, 28.0, 50.0], [-1, 31.0, 49.0], [0, 33.0, 50.0], [1, 31.0, 49.0], [2, 28.0, 50.0], [3, 25.2, 51.4], ], // VIPER12 (F-35A) - Wingman 'ae3004': [ [-3, 25.0, 51.2], [-2, 27.8, 49.8], [-1, 30.8, 48.8], [0, 32.8, 49.8], [1, 30.8, 48.8], [2, 27.8, 49.8], [3, 25.0, 51.2], ], // IRON41 (F-15E) - Strike Eagles, deep strike 'ae3005': [ [-4, 24.2, 54.7], [-3, 27.0, 53.0], [-2, 29.5, 51.0], [-1, 31.5, 50.0], [0, 33.5, 51.5], [1, 31.0, 50.5], [2, 27.0, 53.0], ], // IRON42 (F-15E) - Wingman 'ae3006': [ [-4, 24.0, 54.5], [-3, 26.8, 52.8], [-2, 29.3, 50.8], [-1, 31.3, 49.8], [0, 33.3, 51.3], [1, 30.8, 50.3], [2, 26.8, 52.8], ], // NAVY51 (F/A-18F) - Launch from USS Abraham Lincoln, strike & return 'ae3007': [ [-1, 23.5, 62.0], [-0.5, 24.5, 59.0], [0, 26.0, 57.0], [1, 27.5, 55.5], [2, 25.0, 58.0], [3, 23.5, 62.0], ], // NAVY52 (F/A-18E) - Lincoln CAP 'ae3008': [ [-1, 23.0, 62.5], [-0.5, 24.0, 60.0], [0, 24.5, 59.0], [1, 24.0, 60.5], [2, 23.5, 61.5], [3, 23.0, 62.5], ], // ── ISRAELI FIGHTERS ── // IRON33 (F-35I Adir) - Nevatim → Iran strike → return 'ae0012': [ [-2, 31.2, 34.9], [-1.5, 31.8, 36.0], [-1, 32.5, 38.0], [-0.5, 33.0, 42.0], [0, 33.5, 48.0], [0.5, 33.0, 45.0], [1, 32.5, 40.0], [1.5, 32.0, 37.0], [2, 31.5, 35.5], [3, 31.2, 34.9], ], // IRON34 (F-35I Adir) - Wingman 'ae4002': [ [-2, 31.0, 35.2], [-1.5, 31.6, 36.2], [-1, 32.3, 38.2], [-0.5, 32.8, 42.2], [0, 33.3, 47.8], [0.5, 32.8, 44.8], [1, 32.3, 39.8], [1.5, 31.8, 37.2], [2, 31.3, 35.7], [3, 31.0, 35.2], ], // RAGE22 (F-15I Ra'am) - Ramon → eastern Iraq standoff launch → return 'ae0011': [ [-1.5, 30.0, 34.8], [-1, 30.5, 36.0], [-0.5, 31.0, 38.5], [0, 31.5, 40.0], [0.5, 31.0, 38.5], [1, 30.5, 36.0], [1.5, 30.2, 35.0], [2, 30.0, 34.8], ], // RAGE23 (F-15I Ra'am) - Wingman 'ae4004': [ [-1.5, 29.8, 35.0], [-1, 30.3, 36.2], [-0.5, 30.8, 38.7], [0, 31.3, 40.2], [0.5, 30.8, 38.7], [1, 30.3, 36.2], [1.5, 30.0, 35.2], [2, 29.8, 35.0], ], // VIPER01 (F-16I Sufa) - CAP orbit over Negev/Golan 'ae0010': [ [-2, 30.8, 34.7], [-1.5, 31.5, 35.5], [-1, 32.5, 35.8], [-0.5, 33.0, 35.5], [0, 32.5, 35.8], [0.5, 31.5, 35.5], [1, 30.8, 34.7], [1.5, 31.5, 35.5], [2, 32.5, 35.8], [3, 31.5, 35.0], ], // ── CARGO ── // RCH882 (C-17A) - Al Udeid → Al Asad 'ae0004': [ [-6, 25.1, 51.3], [-4.5, 27.0, 49.0], [-3, 29.0, 47.0], [-1.5, 31.5, 44.5], [-1, 33.8, 42.4], ], // RCH445 (C-17A) - Ramstein → Gulf post-strike resupply 'ae0013': [ [2, 36.0, 40.0], [4, 33.0, 43.0], [6, 30.0, 46.0], [8, 27.0, 49.0], [9, 25.1, 51.3], ], // RCH901 (C-5M) - Ramstein → Al Udeid heavy lift 'ae5001': [ [-3, 37.0, 30.0], [-1.5, 35.0, 35.0], [0, 33.0, 40.0], [2, 30.0, 45.0], [4, 27.0, 49.0], [5, 25.1, 51.3], ], // ── CIVILIAN ── // QTR8101 - Doha → Amman '738012': [ [-11, 25.3, 51.6], [-9, 27.5, 48.0], [-7, 30.0, 43.0], [-5, 31.9, 36.0], ], // EK412 - Dubai → Istanbul '710104': [ [-8, 25.3, 55.3], [-6, 28.0, 50.0], [-4, 32.0, 44.0], [-2, 37.0, 38.0], ], // QTR306 - Doha → London (post-strike, southern routing to avoid Iranian airspace) '738020': [ [3, 25.3, 51.6], [5, 26.0, 45.0], [7, 30.0, 38.0], [9, 34.0, 32.0], [10, 38.0, 28.0], ], // ETD55 - Abu Dhabi → Cairo (restricted routing post-strike) '710110': [ [3, 24.4, 54.7], [5, 26.0, 48.0], [7, 28.5, 40.0], [9, 30.1, 31.4], ], }; // ── Aircraft Propagation ───────────────────────────── export function propagateAircraft( baseAircraft: Aircraft[], currentTime: number, ): Aircraft[] { const hoursFromT0 = (currentTime - T0) / HOUR; // Filter to only aircraft active at this time const active = baseAircraft.filter(ac => { if (ac.activeStart != null && currentTime < ac.activeStart) return false; if (ac.activeEnd != null && currentTime > ac.activeEnd) return false; return true; }); return active.map(ac => { const flightPlan = FLIGHT_PLANS[ac.icao24]; if (flightPlan) { // Waypoint-based interpolation const pos = interpWaypoints(flightPlan, hoursFromT0); const trail = generateTrail(flightPlan, hoursFromT0, 5, 10); // 5 points, 10min intervals return { ...ac, lat: pos.lat, lng: pos.lng, heading: pos.heading, trail, }; } // No flight plan - keep static position (for API-sourced aircraft) return ac; }); } // ── Ship Waypoints ─────────────────────────────────── const SHIP_PLANS: Record = { // USS Abraham Lincoln CSG - Arabian Sea patrol '369970072': [ [-12, 23.0, 62.0], [-8, 23.5, 61.0], [-4, 23.0, 62.0], [0, 23.5, 61.5], [4, 23.0, 62.0], [8, 23.5, 61.0], [12, 23.0, 62.0], ], // USS Frank E. Petersen Jr DDG-121 - escort ahead '369970121': [ [-12, 23.3, 61.8], [-8, 23.8, 60.8], [-4, 23.3, 61.8], [0, 23.8, 61.3], [4, 23.3, 61.8], [8, 23.8, 60.8], [12, 23.3, 61.8], ], // USS Spruance DDG-111 - screen '369970111': [ [-12, 22.7, 62.3], [-8, 23.2, 61.3], [-4, 22.7, 62.3], [0, 23.2, 61.8], [4, 22.7, 62.3], [8, 23.2, 61.3], [12, 22.7, 62.3], ], // USS Michael Murphy DDG-112 - screen '369970112': [ [-12, 23.5, 62.5], [-8, 24.0, 61.5], [-4, 23.5, 62.5], [0, 24.0, 62.0], [4, 23.5, 62.5], [8, 24.0, 61.5], [12, 23.5, 62.5], ], // USS Gerald R. Ford CVN-78 - Red Sea '369970078': [ [-12, 18.5, 39.0], [-8, 19.0, 38.5], [-4, 18.5, 39.0], [0, 19.0, 38.5], [4, 18.5, 39.0], [8, 19.0, 38.5], [12, 18.5, 39.0], ], // USS McFaul DDG-74 - Arabian Sea '369970074': [ [-12, 22.0, 60.0], [-8, 22.5, 59.5], [-4, 22.0, 60.0], [0, 22.5, 59.5], [4, 22.0, 60.0], [8, 22.5, 59.5], [12, 22.0, 60.0], ], // USS John Finn DDG-113 - Arabian Sea '369970113': [ [-12, 24.0, 59.0], [-8, 24.5, 58.5], [-4, 24.0, 59.0], [0, 24.5, 58.5], [4, 24.0, 59.0], [8, 24.5, 58.5], [12, 24.0, 59.0], ], // USS Milius DDG-69 - Northern Arabian Sea '369970069': [ [-12, 25.0, 58.0], [-8, 25.5, 57.5], [-4, 25.0, 58.0], [0, 25.5, 57.5], [4, 25.0, 58.0], [8, 25.5, 57.5], [12, 25.0, 58.0], ], // USS Delbert D. Black DDG-119 - Aegis BMD station '369970119': [ [-12, 26.0, 57.0], [-8, 26.5, 56.5], [-4, 26.0, 57.0], [0, 26.5, 56.5], [4, 26.0, 57.0], [8, 26.5, 56.5], [12, 26.0, 57.0], ], // USS Pinckney DDG-91 - Arabian Sea patrol '369970091': [ [-12, 21.0, 61.0], [-8, 21.5, 60.5], [-4, 21.0, 61.0], [0, 21.5, 60.5], [4, 21.0, 61.0], [8, 21.5, 60.5], [12, 21.0, 61.0], ], // USS Mitscher DDG-57 - Aegis BMD station '369970057': [ [-12, 25.5, 59.0], [-8, 26.0, 58.5], [-4, 25.5, 59.0], [0, 26.0, 58.5], [4, 25.5, 59.0], [8, 26.0, 58.5], [12, 25.5, 59.0], ], // USS Canberra LCS-30 - Persian Gulf patrol (central Gulf, away from Qatar coast) '369970030': [ [-12, 27.2, 51.5], [-8, 27.5, 51.0], [-4, 27.2, 51.5], [0, 27.5, 51.0], [4, 27.2, 51.5], [8, 27.5, 51.0], [12, 27.2, 51.5], ], // USS Tulsa LCS-16 - Persian Gulf patrol (east of Qatar, open water) '369970016': [ [-12, 26.2, 53.0], [-8, 26.5, 52.5], [-4, 26.2, 53.0], [0, 26.5, 52.5], [4, 26.2, 53.0], [8, 26.5, 52.5], [12, 26.2, 53.0], ], // HMS Diamond D34 - re-deployed to Gulf '232001034': [ [-6, 25.0, 57.0], [-3, 25.5, 56.5], [0, 26.0, 56.5], [3, 26.2, 56.0], [6, 25.8, 56.5], [9, 26.2, 56.0], [12, 25.8, 56.5], ], // HMS Middleton M34 - minesweeper, leaving early (Gulf of Oman → Arabian Sea) '232001041': [ [-12, 26.0, 56.8], [-8, 25.5, 57.0], [-4, 25.0, 57.5], [0, 24.5, 58.0], ], // FS Languedoc D653 - off Cyprus '227001653': [ [-8, 34.8, 33.0], [-4, 34.5, 33.5], [0, 34.2, 33.0], [4, 34.5, 33.5], [8, 34.2, 33.0], [12, 34.5, 33.5], ], // FS Charles de Gaulle R91 - 크레타 → 동부 지중해 진입 (T0+4h~) '227001091': [ [4, 35.0, 28.0], [6, 34.8, 29.5], [8, 34.5, 31.0], [10, 34.2, 32.5], [12, 34.0, 33.5], ], // IRGCN Fast Attack 1 - Strait of Hormuz '422001001': [ [-10, 26.2, 54.5], [-6, 26.5, 54.0], [-3, 26.8, 53.5], [0, 26.5, 54.0], [2, 26.8, 54.5], [4, 27.0, 55.0], ], // IRGCN Fast Attack 2 '422001002': [ [-10, 27.0, 54.0], [-6, 26.8, 53.5], [-3, 26.5, 53.0], [0, 26.8, 53.5], [2, 27.0, 53.8], ], // IRGCN Fast Attack 3 '422001003': [ [-10, 26.0, 55.0], [-6, 26.3, 54.5], [-3, 26.6, 54.0], [0, 26.3, 54.5], [2, 26.0, 55.0], ], // IRIS Dena F75 - 호르무즈 해협 인근 이란 해군 초계 '422010001': [ [-12, 25.8, 57.2], [-8, 25.5, 57.5], [-4, 25.2, 57.8], [0, 25.5, 58.2], [4, 25.8, 57.8], [8, 26.0, 57.5], [12, 25.5, 57.2], ], // Eagle Vellore - Korean VLCC escaping Hormuz before blockade // Fixed: start from Al Basra oil terminal (sea), route through Persian Gulf → Hormuz → Arabian Sea '440107001': [ [-6, 29.8, 48.8], [-5, 28.5, 49.5], [-4, 27.2, 51.5], [-3, 26.8, 53.0], [-2, 26.5, 55.0], [-1, 26.0, 56.3], [0, 25.5, 57.5], [2, 25.0, 58.5], [4, 24.0, 60.0], [8, 22.0, 62.0], [12, 20.0, 64.0], ], // ROKS Choi Young DDH-981 - Cheonghae Unit, Hormuz area '440001981': [ [-12, 25.5, 57.0], [-8, 26.0, 56.5], [-4, 25.5, 57.0], [0, 26.0, 56.5], [4, 25.5, 57.0], [8, 26.0, 56.5], [12, 25.5, 57.0], ], }; // ── Ship Propagation ───────────────────────────────── export function propagateShips( baseShips: Ship[], currentTime: number, isLive = false, ): Ship[] { const hoursFromT0 = (currentTime - T0) / HOUR; const active = baseShips.filter(ship => { // In live mode, skip time-window filtering — show all ships from API if (isLive) return true; if (ship.activeStart != null && currentTime < ship.activeStart) return false; if (ship.activeEnd != null && currentTime > ship.activeEnd) return false; return true; }); return active.map(ship => { const plan = SHIP_PLANS[ship.mmsi]; if (plan) { const pos = interpWaypoints(plan, hoursFromT0); const trail = generateTrail(plan, hoursFromT0, 4, 30); // 4 points, 30min intervals return { ...ship, lat: pos.lat, lng: pos.lng, heading: pos.heading, trail, }; } return ship; }); }