From 053d362245f562d91b60bee0a0bae738916645c5 Mon Sep 17 00:00:00 2001 From: karim Date: Tue, 2 Jun 2026 06:01:47 +0200 Subject: [PATCH] G1000: VNAV descent profile + designated-altitude flight plan colouring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CURRENT VNV PROFILE panel on the MFD flight-plan page: active VNV waypoint + target altitude, VS TGT (−3° path), VS REQ, V DEV, FPA, TIME TO TOD (manual S.64 / S.107) - enriched the VNAV computation (vsTgt / vDev / FPA / time-to-TOD) shared by the PFD VnavBox - flight-plan ALT column now shows designated (VNAV) altitudes in blue (S.105) - new Audio Panel + earlier manual-alignment batch already in this branch Co-Authored-By: Claude Opus 4.8 --- web/src/components/FplPage.jsx | 38 +++++++++++++++++++++++++++++++++- web/src/components/PFD.jsx | 15 ++++++++++++-- web/src/styles.css | 12 ++++++++++- 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/web/src/components/FplPage.jsx b/web/src/components/FplPage.jsx index 5fafbb3..a82d9fc 100644 --- a/web/src/components/FplPage.jsx +++ b/web/src/components/FplPage.jsx @@ -64,6 +64,29 @@ export default function FplPage({ xp, full = false, onClose }) { const gs = num(values.groundspeed) * 1.94384; const ete = gs > 30 ? total / gs : null; + // CURRENT VNV PROFILE: descent to the next waypoint with a lower target + // altitude (manual S.64/107). VS TGT for a -3° path, VS REQ to make it, V DEV + // from the path, time-to-top-of-descent. + const alt = num(values.altitude); + let vnav = null; + if (gs > 40) { + let c = 0, pl = num(values.lat), po = num(values.lon); + for (let i = Math.max(1, active); i < wps.length; i++) { + c += distNm({ lat: pl, lon: po }, wps[i]); pl = wps[i].lat; po = wps[i].lon; + const t = num(wps[i].alt); + if (t > 0 && t < alt - 50) { + const tan = Math.tan((3 * Math.PI) / 180); + const vsTgt = -gs * tan * 101.27; + const vsReq = c > 0 ? (t - alt) / (c / gs * 60) : 0; + const vDev = alt - (t + c * 6076.12 * tan); + const todNm = c - (alt - t) / (6076.12 * tan); + vnav = { wptId: wps[i].id, tgtAlt: t, vsTgt, vsReq, vDev, fpa: 3.0, todSec: todNm > 0 ? (todNm / gs) * 3600 : 0 }; + break; + } + } + } + const fmtSec = (s) => { const m = Math.floor(s / 60), ss = Math.round(s % 60); return `${m}:${String(ss).padStart(2, '0')}`; }; + return (
@@ -83,11 +106,24 @@ export default function FplPage({ xp, full = false, onClose }) { {dtk == null ? '___' : `${String(dtk).padStart(3, '0')}°`} {orig ? '—' : d.toFixed(1)} {orig ? '—' : cum.toFixed(0)} - {w.alt ? `${w.alt}` : '_____'} + {w.alt ? `${w.alt}FT` : '_____'}
))}
+ {full && ( +
+
CURRENT VNV PROFILE
+ {vnav ? ( +
+ ACTIVE VNV WPT{vnav.tgtAlt}FT at {vnav.wptId} + VS TGT{Math.round(vnav.vsTgt)}FPMFPA{vnav.fpa.toFixed(1)}° + VS REQ{Math.round(vnav.vsReq)}FPMTIME TO TOD{fmtSec(vnav.todSec)} + V DEV{vnav.vDev >= 0 ? '+' : ''}{Math.round(vnav.vDev)}FT +
+ ) :
— no active VNAV profile —
} +
+ )}
{hits.length > 0 && (
diff --git a/web/src/components/PFD.jsx b/web/src/components/PFD.jsx index 1e0a397..a5bc5b6 100644 --- a/web/src/components/PFD.jsx +++ b/web/src/components/PFD.jsx @@ -58,6 +58,10 @@ function fmtEte(s) { } // VNAV: nearest downstream waypoint with a lower altitude constraint, and the // vertical speed required to meet it at the current groundspeed. +// VNAV descent profile to the next waypoint that has a (lower) target altitude: +// target VS for a -3° flight path, required VS to make the restriction, vertical +// deviation from that path, and time-to-top-of-descent. (Manual S.64 / S.107.) +const VNAV_FPA = 3.0; // default flight-path angle (degrees) function vnavInfo(V, fp) { const wps = fp?.waypoints || []; const ai = Math.max(1, Math.min(wps.length - 1, fp?.activeLeg ?? 1)); @@ -71,9 +75,16 @@ function vnavInfo(V, fp) { prevLat = wps[i].lat; prevLon = wps[i].lon; const tgt = num(wps[i].alt); if (tgt > 0 && tgt < alt - 50) { + const tan = Math.tan((VNAV_FPA * Math.PI) / 180); const tMin = (cum / gs) * 60; - const vsReq = tMin > 0 ? (tgt - alt) / tMin : 0; - return { wptId: wps[i].id, tgtAlt: tgt, dist: cum, vsReq }; + const vsReq = tMin > 0 ? (tgt - alt) / tMin : 0; // fpm to make the fix + const vsTgt = -gs * tan * 101.27; // fpm for the FPA at this GS + const desiredAltNow = tgt + cum * 6076.12 * tan; // path altitude at present position + const vDev = alt - desiredAltNow; // + = above path + const descentNm = (alt - tgt) / (6076.12 * tan); // distance the descent itself takes + const todNm = cum - descentNm; // distance ahead until TOD + const todSec = todNm > 0 ? (todNm / gs) * 3600 : 0; + return { wptId: wps[i].id, tgtAlt: tgt, dist: cum, vsReq, vsTgt, vDev, fpa: VNAV_FPA, todSec }; } } return null; diff --git a/web/src/styles.css b/web/src/styles.css index e2fece0..3e617e5 100644 --- a/web/src/styles.css +++ b/web/src/styles.css @@ -247,7 +247,17 @@ body { .r-wpt { color: #0ff; font-weight: 700; } .r-wpt i { color: #0a8; font-style: normal; font-size: 10px; margin-left: 6px; } .r-wpt b { font-weight: 700; } .r-wpt b.cur { background: #19b8e6; color: #042230; padding: 0 4px; border-radius: 1px; } .r-dtk, .r-dis, .r-cum { color: #e7edf2; text-align: right; } -.r-alt { color: #0ff; text-align: right; } +.r-alt { color: #6f808d; text-align: right; } +.r-alt.dsgn { color: #4fa8ff; } /* designated (VNAV) altitude = blue, per manual S.105 */ +/* CURRENT VNV PROFILE panel (MFD flight-plan page) */ +.fpl-vnav { border-top: 1px solid #2c343c; padding: 6px 12px 8px; font-family: 'Roboto Mono', monospace; } +.fpl-vnav-h { color: #36d2ff; font-size: 11px; letter-spacing: 1px; margin-bottom: 5px; } +.fpl-vnav-grid { display: grid; grid-template-columns: auto 1fr auto 1fr; gap: 3px 10px; align-items: baseline; } +.fpl-vnav-grid b { color: #6f808d; font-weight: normal; font-size: 11px; } +.fpl-vnav-grid span { color: #fff; font-size: 14px; } +.fpl-vnav-grid span u { color: #6f808d; font-size: 9px; text-decoration: none; margin-left: 1px; } +.fpl-vnav-grid .vwpt { color: #4fa8ff; } +.fpl-vnav-none { color: #6f808d; font-size: 12px; } /* ORIG / DEST subtitle (PFD window) */ .fpl-od { color: #36d2ff; text-align: center; font-family: 'Roboto Mono', monospace; font-size: 14px; padding: 3px 0; border-bottom: 1px solid #1c242c; letter-spacing: 1px; } /* compact window: DTK/DIS only (drop CUM/ALT), no editor — like the real FPL window */