"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var TacticalService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.TacticalService = void 0; const common_1 = require("@nestjs/common"); const redis_service_1 = require("../common/redis.service"); let TacticalService = TacticalService_1 = class TacticalService { redisService; logger = new common_1.Logger(TacticalService_1.name); constructor(redisService) { this.redisService = redisService; } async calculateArtilleryFireMission(dto) { const { gunLat, gunLng, targetLat, targetLng, gunElevationOffset = 2, targetElevationOffset = 0, muzzleVelocity = 827, caliber = '155mm Howitzer' } = dto; const distanceMeters = this.haversineDistance(gunLat, gunLng, targetLat, targetLng); const azimuthDegrees = this.calculateBearing(gunLat, gunLng, targetLat, targetLng); const gunGroundElev = this.estimateElevation(gunLat, gunLng); const targetGroundElev = this.estimateElevation(targetLat, targetLng); const gunTotalElev = gunGroundElev + gunElevationOffset; const targetTotalElev = targetGroundElev + targetElevationOffset; const heightDiff = targetTotalElev - gunTotalElev; const g = 9.80665; const v0 = muzzleVelocity; const term = Math.pow(v0, 4) - g * (g * Math.pow(distanceMeters, 2) + 2 * heightDiff * Math.pow(v0, 2)); let lowAngleRad = 0; let highAngleRad = 0; let maxRange = (Math.pow(v0, 2) / g); let isInRange = term >= 0 && distanceMeters <= maxRange; if (isInRange) { const sqrtTerm = Math.sqrt(term); lowAngleRad = Math.atan((Math.pow(v0, 2) - sqrtTerm) / (g * distanceMeters)); highAngleRad = Math.atan((Math.pow(v0, 2) + sqrtTerm) / (g * distanceMeters)); } else { lowAngleRad = (45 * Math.PI) / 180; highAngleRad = (60 * Math.PI) / 180; } const lowAngleDeg = (lowAngleRad * 180) / Math.PI; const lowAngleMils = (lowAngleDeg * (6400 / 360)); const highAngleDeg = (highAngleRad * 180) / Math.PI; const highAngleMils = (highAngleDeg * (6400 / 360)); const timeOfFlightSeconds = distanceMeters / (v0 * Math.cos(lowAngleRad)); const apexHeightMeters = gunTotalElev + (Math.pow(v0 * Math.sin(lowAngleRad), 2) / (2 * g)); const trajectoryPoints = []; const samples = 50; let hasCrestClearance = true; let criticalObstacle = null; for (let i = 0; i <= samples; i++) { const frac = i / samples; const d = distanceMeters * frac; const lat = gunLat + (targetLat - gunLat) * frac; const lng = gunLng + (targetLng - gunLng) * frac; const t = frac * timeOfFlightSeconds; const y = (v0 * Math.sin(lowAngleRad) * t) - (0.5 * g * Math.pow(t, 2)); const projectileAlt = gunTotalElev + y; const terrainElev = this.estimateElevation(lat, lng); const clearance = projectileAlt - terrainElev; if (clearance <= 0 && i > 1 && i < samples) { hasCrestClearance = false; if (!criticalObstacle || clearance < criticalObstacle.clearance) { criticalObstacle = { distanceMeters: Math.round(d), terrainElevMeters: Math.round(terrainElev), projectileAltMeters: Math.round(projectileAlt), deficitMeters: Math.round(Math.abs(clearance)), lat, lng }; } } trajectoryPoints.push({ distanceMeters: Math.round(d), lat, lng, terrainElevation: Math.round(terrainElev), projectileAltitude: Math.round(projectileAlt), clearanceMeters: Math.round(clearance) }); } return { fireMissionId: `FM-${Date.now().toString().slice(-6)}`, caliber, muzzleVelocityMs: muzzleVelocity, distanceMeters: Math.round(distanceMeters), distanceKm: Math.round((distanceMeters / 1000) * 100) / 100, azimuthDegrees: Math.round(azimuthDegrees * 10) / 10, azimuthMils: Math.round((azimuthDegrees * (6400 / 360)) * 10) / 10, gunElevationMeters: Math.round(gunTotalElev), targetElevationMeters: Math.round(targetTotalElev), apexAltitudeMeters: Math.round(apexHeightMeters), timeOfFlightSeconds: Math.round(timeOfFlightSeconds * 10) / 10, lowAngle: { degrees: Math.round(lowAngleDeg * 100) / 100, mils: Math.round(lowAngleMils * 10) / 10, }, highAngle: { degrees: Math.round(highAngleDeg * 100) / 100, mils: Math.round(highAngleMils * 10) / 10, }, hasCrestClearance, criticalObstacle, trajectoryPoints }; } async assessHelicopterLandingZones(lat, lng, radiusMeters = 3000) { const candidates = []; const samples = 16; for (let i = 0; i < samples; i++) { const angle = (i * 2 * Math.PI) / samples; const r = (radiusMeters * 0.3) + Math.random() * (radiusMeters * 0.6); const dLat = (r / 6371000) * (180 / Math.PI) * Math.cos(angle); const dLng = (r / (6371000 * Math.cos((lat * Math.PI) / 180))) * (180 / Math.PI) * Math.sin(angle); const hlzLat = lat + dLat; const hlzLng = lng + dLng; const centerElev = this.estimateElevation(hlzLat, hlzLng); const northElev = this.estimateElevation(hlzLat + 0.0005, hlzLng); const eastElev = this.estimateElevation(hlzLat, hlzLng + 0.0005); const slopeDeg = Math.round(Math.atan(Math.max(Math.abs(northElev - centerElev), Math.abs(eastElev - centerElev)) / 50) * (180 / Math.PI) * 10) / 10; const isSuitable = slopeDeg <= 7.0; candidates.push({ id: `HLZ-${i + 1}`, lat: hlzLat, lng: hlzLng, elevationMeters: Math.round(centerElev), distanceMeters: Math.round(r), slopeDegrees: slopeDeg, suitability: isSuitable ? 'EXCELLENT' : slopeDeg <= 12 ? 'MARGINAL' : 'UNSUITABLE', suitability_ar: isSuitable ? 'ممتاز (مستوي وخالي من العوائق)' : slopeDeg <= 12 ? 'مقبول بحذر' : 'غير صالح (شديد الانحدار)', maxRotorDiameterMeters: isSuitable ? 25 : 15, windApproachBearingDeg: Math.round(Math.random() * 360) }); } return { centerLat: lat, centerLng: lng, searchRadiusMeters: radiusMeters, totalAssessed: candidates.length, suitableCount: candidates.filter(c => c.suitability === 'EXCELLENT').length, zones: candidates }; } async saveScenario(name, symbols) { const key = `tactical:scenario:${name}`; await this.redisService.set(key, JSON.stringify(symbols), 86400 * 7); return { success: true, count: symbols.length, name }; } async getScenario(name) { const key = `tactical:scenario:${name}`; const data = await this.redisService.get(key); return data || []; } haversineDistance(lat1, lon1, lat2, lon2) { const R = 6371000; const dLat = (lat2 - lat1) * (Math.PI / 180); const dLon = (lon2 - lon1) * (Math.PI / 180); const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); } calculateBearing(lat1, lon1, lat2, lon2) { const phi1 = (lat1 * Math.PI) / 180; const phi2 = (lat2 * Math.PI) / 180; const deltaLambda = ((lon2 - lon1) * Math.PI) / 180; const y = Math.sin(deltaLambda) * Math.cos(phi2); const x = Math.cos(phi1) * Math.sin(phi2) - Math.sin(phi1) * Math.cos(phi2) * Math.cos(deltaLambda); return (Math.atan2(y, x) * (180 / Math.PI) + 360) % 360; } estimateElevation(lat, lng) { if (lng < 35.6 && lat < 32.2 && lat > 31.0) { return -400 + Math.abs(lng - 35.5) * 3000; } if (lat >= 32.1 && lng < 36.0) { return 850 + Math.sin(lat * 50) * 250 + Math.cos(lng * 40) * 150; } if (lat >= 31.8 && lat < 32.1 && lng >= 35.8 && lng < 36.2) { return 900 + Math.sin((lat - 31.95) * 100) * 120 + Math.cos((lng - 35.9) * 100) * 100; } if (lat < 31.5 && lat > 30.0 && lng < 35.7) { return 1100 + Math.sin(lat * 30) * 350; } return 650 + (lng - 36.0) * 30; } }; exports.TacticalService = TacticalService; exports.TacticalService = TacticalService = TacticalService_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [redis_service_1.RedisService]) ], TacticalService); //# sourceMappingURL=tactical.service.js.map