import React, { useState, useEffect } from 'react'; import { num, navSearch } from '../api/useXplane.js'; const R_NM = 3440.065; const rad = (d) => (d * Math.PI) / 180; const deg = (r) => (r * 180) / Math.PI; function distNm(a, b) { const dLat = rad(b.lat - a.lat), dLon = rad(b.lon - a.lon); const s = Math.sin(dLat / 2) ** 2 + Math.cos(rad(a.lat)) * Math.cos(rad(b.lat)) * Math.sin(dLon / 2) ** 2; return 2 * R_NM * Math.asin(Math.min(1, Math.sqrt(s))); } function bearing(a, b) { const y = Math.sin(rad(b.lon - a.lon)) * Math.cos(rad(b.lat)); const x = Math.cos(rad(a.lat)) * Math.sin(rad(b.lat)) - Math.sin(rad(a.lat)) * Math.cos(rad(b.lat)) * Math.cos(rad(b.lon - a.lon)); return (deg(Math.atan2(y, x)) + 360) % 360; } export default function FMS({ xp }) { const { flightPlan, fp, values, exportMsg } = xp; const wps = flightPlan.waypoints || []; const [entry, setEntry] = useState(''); const [hits, setHits] = useState([]); // live ident search against X-Plane's nav database useEffect(() => { const q = entry.trim(); if (q.length < 2 || /[,\s]/.test(q)) { setHits([]); return; } let alive = true; navSearch(q).then((r) => alive && setHits(r.slice(0, 6))); return () => { alive = false; }; }, [entry]); const add = (id) => { fp.add(id || entry.trim()); setEntry(''); setHits([]); }; let total = 0; const rows = wps.map((w, i) => { const prev = wps[i - 1]; const d = prev ? distNm(prev, w) : 0; const brg = prev ? bearing(prev, w) : null; total += d; return { w, i, d, brg }; }); const gs = num(values.groundspeed) * 1.94384; const ete = gs > 20 ? total / gs : null; // hours const active = Math.max(1, Math.min(wps.length - 1, flightPlan?.activeLeg ?? 1)); return (