import type { FlightPoint, CalculatedPointInfo, AircraftParams } from './types' export function newGuid(): string { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = (Math.random() * 16) | 0 const v = c === 'x' ? r : (r & 0x3) | 0x8 return v.toString(16) }) } export function calculateDistance( point1: FlightPoint, point2: FlightPoint, aircraftType: string, initialAltitude: number, downang: number, upang: number, ): number { if (!point1?.position || !point2?.position) return 0 const R = 6371 const { lat: lat1, lng: lon1 } = point1.position const { lat: lat2, lng: lon2 } = point2.position const alt1 = point1.altitude || 0 const alt2 = point2.altitude || 0 const toRad = (value: number) => (value * Math.PI) / 180 const dLat = toRad(lat2 - lat1) const dLon = toRad(lon2 - lon1) const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2) const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) const horizontalDistance = R * c const initialAltitudeKm = initialAltitude / 1000 const altitude1Km = alt1 / 1000 const altitude2Km = alt2 / 1000 const descentAngleRad = toRad(downang || 0.01) const ascentAngleRad = toRad(upang || 0.01) if (aircraftType === 'Plane') { const ascentDist = Math.max(0, (initialAltitudeKm - altitude1Km) / Math.sin(ascentAngleRad)) const descentDist = Math.max(0, (initialAltitudeKm - altitude2Km) / Math.sin(descentAngleRad)) const hAscent = Math.max(0, ascentDist * Math.cos(ascentAngleRad)) const hDescent = Math.max(0, descentDist * Math.cos(descentAngleRad)) return horizontalDistance - (hDescent + hAscent) + Math.max(0, descentDist) + Math.max(0, ascentDist) } const ascentVertical = Math.abs(initialAltitudeKm - altitude1Km) const descentVertical = Math.abs(initialAltitudeKm - altitude2Km) return ascentVertical + horizontalDistance + descentVertical } export async function getWeatherData(lat: number, lon: number) { const apiKey = '335799082893fad97fa36118b131f919' const url = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${apiKey}&units=metric` try { const res = await fetch(url) const data = await res.json() return { windSpeed: data.wind.speed as number, windAngle: data.wind.deg as number } } catch { return null } } export async function calculateBatteryPercentUsed( groundSpeed: number, time: number, position: { lat: number; lon: number }, aircraft: AircraftParams, ): Promise { const weatherData = await getWeatherData(position.lat, position.lon) const airDensity = 1.05 const groundSpeedMs = groundSpeed / 3.6 const headwind = (weatherData?.windSpeed ?? 0) * Math.cos((Math.PI / 180) * (weatherData?.windAngle ?? 0)) const effectiveAirspeed = groundSpeedMs + headwind const drag = 0.5 * airDensity * (effectiveAirspeed ** 2) * aircraft.dragCoefficient * aircraft.frontalArea const adjustedDrag = drag + aircraft.weight * 9.8 * 0.05 let watts = aircraft.thrustWatts[aircraft.thrustWatts.length - 1].watts for (const item of aircraft.thrustWatts) { const thrustN = (item.thrust / 1000) * 9.8 if (thrustN > adjustedDrag) { watts = item.watts; break } } const power = watts / aircraft.propellerEfficiency const energyUsed = power * time return Math.min((energyUsed / aircraft.batteryCapacity) * 100, 100) } export async function calculateAllPoints( points: FlightPoint[], aircraft: AircraftParams, initialAltitude: number, ): Promise { const infos: CalculatedPointInfo[] = [{ bat: 100, time: 0 }] for (let i = 1; i < points.length; i++) { const p1 = points[i - 1], p2 = points[i] const dist = calculateDistance(p1, p2, aircraft.type, initialAltitude, aircraft.downang, aircraft.upang) const time = dist / aircraft.speed const midPos = { lat: (p1.position.lat + p2.position.lat) / 2, lon: (p1.position.lng + p2.position.lng) / 2 } const pct = await calculateBatteryPercentUsed(aircraft.speed, time, midPos, aircraft) infos.push({ bat: infos[i - 1].bat - pct, time: infos[i - 1].time + time }) } return infos } export function parseCoordinates(input: string): { lat: number; lng: number } | null { const cleaned = input.trim().replace(/[°NSEW]/gi, '') const parts = cleaned.split(/[,\s]+/).filter(Boolean) if (parts.length >= 2) { const lat = parseFloat(parts[0]) const lng = parseFloat(parts[1]) if (!isNaN(lat) && !isNaN(lng) && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180) { return { lat, lng } } } return null } export function getMockAircraftParams(): AircraftParams { return { type: 'Plane', downang: 40, upang: 45, weight: 3.4, speed: 80, frontalArea: 0.12, dragCoefficient: 0.45, batteryCapacity: 315, thrustWatts: [ { thrust: 500, watts: 55.5 }, { thrust: 750, watts: 91.02 }, { thrust: 1000, watts: 137.64 }, { thrust: 1250, watts: 191 }, { thrust: 1500, watts: 246 }, { thrust: 1750, watts: 308 }, { thrust: 2000, watts: 381 }, ], propellerEfficiency: 0.95, } }