G1000: VNAV descent profile + designated-altitude flight plan colouring

- 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 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 06:01:47 +02:00
parent 033a9d406a
commit 053d362245
3 changed files with 61 additions and 4 deletions
+37 -1
View File
@@ -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 (
<div className={`fpl ${full ? 'full' : 'win'}`}>
<div className="fpl-head">
@@ -83,11 +106,24 @@ export default function FplPage({ xp, full = false, onClose }) {
<span className="r-dtk">{dtk == null ? '___' : `${String(dtk).padStart(3, '0')}°`}</span>
<span className="r-dis">{orig ? '—' : d.toFixed(1)}</span>
<span className="r-cum">{orig ? '—' : cum.toFixed(0)}</span>
<span className="r-alt">{w.alt ? `${w.alt}` : '_____'}</span>
<span className={`r-alt ${w.alt ? 'dsgn' : ''}`}>{w.alt ? `${w.alt}FT` : '_____'}</span>
<button className="r-del" onClick={(e) => { e.stopPropagation(); fp.remove(i); }}></button>
</div>
))}
</div>
{full && (
<div className="fpl-vnav">
<div className="fpl-vnav-h">CURRENT VNV PROFILE</div>
{vnav ? (
<div className="fpl-vnav-grid">
<b>ACTIVE VNV WPT</b><span className="vwpt">{vnav.tgtAlt}<u>FT</u> at {vnav.wptId}</span>
<b>VS TGT</b><span>{Math.round(vnav.vsTgt)}<u>FPM</u></span><b>FPA</b><span>{vnav.fpa.toFixed(1)}°</span>
<b>VS REQ</b><span>{Math.round(vnav.vsReq)}<u>FPM</u></span><b>TIME TO TOD</b><span>{fmtSec(vnav.todSec)}</span>
<b>V DEV</b><span>{vnav.vDev >= 0 ? '+' : ''}{Math.round(vnav.vDev)}<u>FT</u></span>
</div>
) : <div className="fpl-vnav-none"> no active VNAV profile </div>}
</div>
)}
<div className="fpl-entry">
{hits.length > 0 && (
<div className="fpl-hits">
+13 -2
View File
@@ -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;