Manual audit fixes A/E/F: fuel totalizer, Victor/Jet airways, TIME TO BOD
A — Fuel totalizer (SYSTEM key, renamed from ENGINE): DEC/INC/RST FUEL had no handler. Now adjust the fuel_totalizer_sum_kg dataref (±1 gal, RST→max fuel) and the EIS shows the calculated remaining/used. cdiSrc-style writable + demo echo. E — AIRWAYS: was a single on/off. earth_awy.dat field 8 (airway layer) is now parsed (1=Victor/low, 2=Jet/high; name-prefix fallback), the bbox returns it, and the AIRWAYS softkey cycles off→all→Victor→Jet with an AIRWY-LO/-HI label. F — CURRENT VNV PROFILE now shows TIME TO BOD (bottom of descent) once past the top of descent, instead of only TIME TO TOD. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -28,10 +28,11 @@ const PFD_MENU = {
|
||||
// page; TOPO/TERRAIN/OSM switch the base map; BACK returns. (OSM is our tuned
|
||||
// extra layer in an otherwise-empty slot.)
|
||||
const MFD_MENU = {
|
||||
root: ['ENGINE', 'MAP', '', '', '', '', '', '', '', 'DCLTR', '', ''],
|
||||
root: ['SYSTEM', 'MAP', '', '', '', '', '', '', '', 'DCLTR', '', ''],
|
||||
mapopt: ['TRAFFIC', 'PROFILE', 'TOPO', 'TERRAIN', 'AIRWAYS', 'AIRSPACE', 'NEXRAD', 'OSM', '', '', 'BACK', ''],
|
||||
engine: ['DEC FUEL', 'INC FUEL', 'RST FUEL', '', '', '', '', '', '', '', 'BACK', ''],
|
||||
system: ['DEC FUEL', 'INC FUEL', 'RST FUEL', '', '', '', '', '', '', '', 'BACK', ''],
|
||||
};
|
||||
const KG_PER_GAL = 2.72; // fuel totalizer steps in US gallons (matches the EIS readout)
|
||||
|
||||
// autopilot_state bitfield (best-effort; tweak per aircraft)
|
||||
const AP_BITS = { fd: 1 << 0, hdg: 1 << 1, vs: 1 << 4, flc: 1 << 6, nav: 1 << 8, apr: 1 << 9, vnav: 1 << 11, altHold: 1 << 14, bc: 1 << 18 };
|
||||
@@ -67,13 +68,16 @@ export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, inset,
|
||||
const cycleDcltr = (setter) => setter && setter((m) => ({ ...m, dcltr: (((m.dcltr || 0) + 1) % 4) }));
|
||||
if (variant === 'mfd') {
|
||||
if (label === 'MAP') setPage('mapopt');
|
||||
else if (label === 'ENGINE') setPage('engine');
|
||||
else if (label === 'SYSTEM') setPage('system');
|
||||
else if (label === 'BACK') setPage('root');
|
||||
else if (label === 'DEC FUEL' && xp) xp.setDataref('fuelTot', Math.max(0, num(xp.values?.fuelTot) - KG_PER_GAL));
|
||||
else if (label === 'INC FUEL' && xp) xp.setDataref('fuelTot', num(xp.values?.fuelTot) + KG_PER_GAL);
|
||||
else if (label === 'RST FUEL' && xp) xp.setDataref('fuelTot', num(xp.values?.fuelMax) || num(xp.values?.fuelTot));
|
||||
else if (label === 'TOPO') setBase('topo'); // relief on/off
|
||||
else if (label === 'TERRAIN') onMapMode && onMapMode((m) => ({ ...m, terrain: !m.terrain })); // awareness overlay (independent)
|
||||
else if (label === 'OSM') setBase('osm');
|
||||
else if (label === 'DCLTR') cycleDcltr(onMapMode);
|
||||
else if (label === 'AIRWAYS') onMapMode && onMapMode((m) => ({ ...m, airways: !m.airways }));
|
||||
else if (label === 'AIRWAYS') onMapMode && onMapMode((m) => ({ ...m, airways: (((m.airways | 0) + 1) % 4) })); // off→all→Victor→Jet
|
||||
else if (label === 'AIRSPACE') onMapMode && onMapMode((m) => ({ ...m, airspace: !m.airspace }));
|
||||
} else {
|
||||
if (label === 'PFD') setPage('pfd');
|
||||
@@ -149,7 +153,9 @@ export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, inset,
|
||||
<div className="sk-labels">
|
||||
{keys.map((s, i) => (
|
||||
<span key={i} className={`skl ${s ? '' : 'empty'} ${s === 'CAUTION' ? 'caution' : ''} ${isOn(s) ? 'on' : ''}`}>{
|
||||
(s === 'DCLTR' && (mapMode?.dcltr || insetMode?.dcltr)) ? `DCLTR-${mapMode?.dcltr || insetMode?.dcltr}` : s
|
||||
(s === 'DCLTR' && (mapMode?.dcltr || insetMode?.dcltr)) ? `DCLTR-${mapMode?.dcltr || insetMode?.dcltr}`
|
||||
: (s === 'AIRWAYS' && (mapMode?.airways | 0) >= 2) ? (mapMode.airways === 2 ? 'AIRWY-LO' : 'AIRWY-HI')
|
||||
: s
|
||||
}</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -80,7 +80,11 @@ export default function FplPage({ xp, full = false, onClose }) {
|
||||
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 };
|
||||
// Before TOD: time until the descent path is intercepted. After TOD (already
|
||||
// descending): time to Bottom of Descent = reaching the target waypoint.
|
||||
const todSec = todNm > 0 ? (todNm / gs) * 3600 : 0;
|
||||
const bodSec = todNm > 0 ? 0 : (c / gs) * 3600;
|
||||
vnav = { wptId: wps[i].id, tgtAlt: t, vsTgt, vsReq, vDev, fpa: 3.0, todSec, bodSec };
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -124,7 +128,10 @@ export default function FplPage({ xp, full = false, onClose }) {
|
||||
<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>VS REQ</b><span>{Math.round(vnav.vsReq)}<u>FPM</u></span>
|
||||
{vnav.todSec > 0
|
||||
? <><b>TIME TO TOD</b><span>{fmtSec(vnav.todSec)}</span></>
|
||||
: <><b>TIME TO BOD</b><span>{fmtSec(vnav.bodSec)}</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>}
|
||||
|
||||
@@ -155,6 +155,17 @@ function EisStrip({ V }) {
|
||||
<Bar y={284} label="VAC" min={0} max={10} value={5}
|
||||
zones={[{ from: 4.5, to: 5.5, c: '#0c0' }]} />
|
||||
<FuelBar y={330} left={fuelL} right={fuelR} />
|
||||
{/* fuel totalizer (SYSTEM → DEC/INC/RST FUEL): pilot-set remaining + used */}
|
||||
{(() => {
|
||||
const rem = num(V.fuelTot) / KG_PER_GAL;
|
||||
const used = Math.max(0, (num(V.fuelMax) - num(V.fuelTot)) / KG_PER_GAL);
|
||||
return (<>
|
||||
<text x="8" y="392" fill="#39d3c0" fontSize="11">CALC</text>
|
||||
<text x="118" y="392" fill="#fff" fontSize="14" textAnchor="end">{rem.toFixed(1)}</text>
|
||||
<text x="124" y="392" fill="#7f8c97" fontSize="10">GAL</text>
|
||||
<text x="182" y="392" fill="#9aa" fontSize="10" textAnchor="end">USED {used.toFixed(0)}</text>
|
||||
</>);
|
||||
})()}
|
||||
<text x="8" y="412" fill="#39d3c0" fontSize="12">ENG</text>
|
||||
<text x="182" y="412" fill="#fff" fontSize="14" textAnchor="end">{engHrs.toFixed(1)} HRS</text>
|
||||
<text x="95" y="438" fill="#39d3c0" fontSize="12" textAnchor="middle">– ELECTRICAL –</text>
|
||||
|
||||
@@ -100,7 +100,7 @@ export default function MapView({ values, flightPlan, fp, inset = false, hud = t
|
||||
const track = num(values.track);
|
||||
const gs = num(values.groundspeed) * 1.94384; // m/s -> kt
|
||||
const base = mapMode?.base || 'topo';
|
||||
const airways = !!mapMode?.airways;
|
||||
const airways = (mapMode?.airways | 0); // 0 off · 1 all · 2 Victor (low) · 3 Jet (high)
|
||||
const airspace = !!mapMode?.airspace;
|
||||
aspOnRef.current = airspace;
|
||||
const dcltrRef = useRef(dcltr);
|
||||
@@ -154,8 +154,11 @@ export default function MapView({ values, flightPlan, fp, inset = false, hud = t
|
||||
const segs = await res.json();
|
||||
layer.clearLayers();
|
||||
const labels = map.getZoom() >= 8;
|
||||
const mode = awyOnRef.current; // 1 all · 2 Victor (low) · 3 Jet (high)
|
||||
const seen = new Set();
|
||||
for (const sg of segs) {
|
||||
if (mode === 2 && sg.lyr !== 1) continue; // Victor-only: low-altitude airways
|
||||
if (mode === 3 && sg.lyr !== 2) continue; // Jet-only: high-altitude airways
|
||||
L.polyline([[sg.la1, sg.lo1], [sg.la2, sg.lo2]], { color: '#5db4e6', weight: 1.2, opacity: 0.7, interactive: false }).addTo(layer);
|
||||
if (labels && sg.name && !seen.has(sg.name)) {
|
||||
seen.add(sg.name);
|
||||
|
||||
Reference in New Issue
Block a user