Citation: match PFD/MFD size (portrait DU-870), Nav Source switch, manual audit
- MFD reworked to the same portrait DU-870 format as the PFD (800x940) so both tubes are identical size side-by-side in the PFD+MFD view, like the real panel. - Nav Source Selector now on the PFD bezel (sits under the PFD per manual p24): NAV (VOR1/VOR2) / FMS buttons drive HSI_source_select; the HSI course pointer, CDI and source label colour by source — FMS magenta, VOR green (Honeywell convention). MFD source label (FMS1/VOR) follows the same coupling. - Added the airspeed trend vector (PFD #3, was missing): smoothed acceleration projected 10 s, magenta, on the speed tape. - Removed dead MFD soft-keys per manual: PFD SETUP → IN/HPA baro unit; EICAS SYS → FUEL-HYD/ELEC/APU/ENG sub-set readout (#11/#14) with RTN. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,7 @@ const hz2mhz = (hz) => (num(hz) / 100).toFixed(2);
|
||||
// Citation X operating speeds (manual p80): Vso 115 · Vs1 136 · Vfe 180 ·
|
||||
// Vmo 270 (SL-8000') / 350 (above) · Mmo 0.935.
|
||||
const VSO = 115, VS1 = 136, VFE = 180;
|
||||
function SpeedTape({ ias, mach, bug, alt }) {
|
||||
function SpeedTape({ ias, mach, bug, alt, trendKt }) {
|
||||
const H = 560, mid = H / 2, pxkt = 3.4; // 3.4 px per knot
|
||||
const y = (s) => mid + (ias - s) * pxkt;
|
||||
const vmo = alt > 8000 ? 350 : 270;
|
||||
@@ -49,6 +49,13 @@ function SpeedTape({ ias, mach, bug, alt }) {
|
||||
{/* Vfe flap-limit marker */}
|
||||
<line x1={0} y1={y(VFE)} x2={12} y2={y(VFE)} stroke="#fff" strokeWidth="3" />
|
||||
{marks}
|
||||
{/* airspeed trend vector (#3): magenta line from the index up/down */}
|
||||
{Math.abs(trendKt) > 1 && (
|
||||
<g>
|
||||
<line x1={71} y1={mid} x2={71} y2={mid - trendKt * pxkt} stroke="#d24bd2" strokeWidth="3" />
|
||||
<polygon points={`71,${mid - trendKt * pxkt} 67,${mid - trendKt * pxkt + (trendKt > 0 ? 8 : -8)} 75,${mid - trendKt * pxkt + (trendKt > 0 ? 8 : -8)}`} fill="#d24bd2" />
|
||||
</g>
|
||||
)}
|
||||
</g>
|
||||
<defs>
|
||||
<pattern id="barber" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
|
||||
@@ -208,7 +215,7 @@ function Attitude({ pitch, roll, slip, fdP, fdR, fdOn }) {
|
||||
}
|
||||
|
||||
// ── HSI (rotating compass with CDI + bearing pointers) ─────────────────────────
|
||||
function HSI({ hdg, trk, crs, hdgBug, cdi, toFrom, brg1, brg2, srcLabel }) {
|
||||
function HSI({ hdg, trk, crs, hdgBug, cdi, toFrom, brg1, brg2, srcLabel, srcColor }) {
|
||||
const R = 150;
|
||||
const card = [];
|
||||
for (let d = 0; d < 360; d += 5) {
|
||||
@@ -237,15 +244,15 @@ function HSI({ hdg, trk, crs, hdgBug, cdi, toFrom, brg1, brg2, srcLabel }) {
|
||||
<g>{card}</g>
|
||||
{/* heading bug (magenta) */}
|
||||
<g transform={`rotate(${mod360(hdgBug - hdg)})`}><polygon points={`0,${-R} -9,${-R + 14} 9,${-R + 14}`} fill="#d24bd2" /></g>
|
||||
{/* course pointer + CDI deviation (cyan), #5 + #15 */}
|
||||
{/* course pointer + CDI deviation (#5 + #15) — FMS magenta / VOR green */}
|
||||
<g transform={`rotate(${mod360(crs - hdg)})`}>
|
||||
<line x1={0} y1={-R + 18} x2={0} y2={-50} stroke="#19c3e0" strokeWidth="3" />
|
||||
<polygon points={`0,${-R + 6} -8,${-R + 22} 8,${-R + 22}`} fill="#19c3e0" />
|
||||
<line x1={0} y1={50} x2={0} y2={R - 18} stroke="#19c3e0" strokeWidth="3" />
|
||||
<line x1={0} y1={-R + 18} x2={0} y2={-50} stroke={srcColor} strokeWidth="3" />
|
||||
<polygon points={`0,${-R + 6} -8,${-R + 22} 8,${-R + 22}`} fill={srcColor} />
|
||||
<line x1={0} y1={50} x2={0} y2={R - 18} stroke={srcColor} strokeWidth="3" />
|
||||
{/* CDI bar */}
|
||||
<line x1={clamp(cdi, -2, 2) * 30} y1={-46} x2={clamp(cdi, -2, 2) * 30} y2={46} stroke="#19c3e0" strokeWidth="3.5" />
|
||||
<line x1={clamp(cdi, -2, 2) * 30} y1={-46} x2={clamp(cdi, -2, 2) * 30} y2={46} stroke={srcColor} strokeWidth="3.5" />
|
||||
{[-2, -1, 1, 2].map((d) => <circle key={d} cx={d * 30} cy={0} r="3" fill="none" stroke="#9aa6ad" strokeWidth="1.2" />)}
|
||||
{toFrom !== 0 && <polygon points={toFrom > 0 ? '0,-14 -8,2 8,2' : '0,14 -8,-2 8,-2'} fill="#19c3e0" />}
|
||||
{toFrom !== 0 && <polygon points={toFrom > 0 ? '0,-14 -8,2 8,2' : '0,14 -8,-2 8,-2'} fill={srcColor} />}
|
||||
</g>
|
||||
{/* bearing pointers — #6 secondary NAV (cyan circle = BRG1, white diamond = BRG2) */}
|
||||
{brg1 != null && ptr(brg1, '#19c3e0', false)}
|
||||
@@ -253,7 +260,7 @@ function HSI({ hdg, trk, crs, hdgBug, cdi, toFrom, brg1, brg2, srcLabel }) {
|
||||
{/* fixed lubber line + aircraft */}
|
||||
<polygon points={`0,${-R - 2} -8,${-R - 16} 8,${-R - 16}`} fill="#fff" />
|
||||
<text x={0} y={-R - 22} fontSize="13" fill="#fff" textAnchor="middle" fontWeight="700">{String(Math.round(mod360(hdg))).padStart(3, '0')}</text>
|
||||
<text x={0} y={6} fontSize="12" fill="#13e000" textAnchor="middle">{srcLabel}</text>
|
||||
<text x={0} y={6} fontSize="12" fill={srcColor} textAnchor="middle">{srcLabel}</text>
|
||||
</g>
|
||||
);
|
||||
}
|
||||
@@ -289,8 +296,20 @@ export default function CitPFD({ xp }) {
|
||||
// bearing pointers only when a station is received (finite, nonzero)
|
||||
const brg1 = (num(V.nav1Dme) > 0 || num(V.nav1Brg) > 0) ? brg1e : null;
|
||||
const brg2 = (num(V.nav2Dme) > 0 || num(V.nav2Brg) > 0) ? brg2e : null;
|
||||
const srcLabel = num(V.cdiSrc) === 2 ? 'FMS1' : num(V.cdiSrc) === 1 ? 'VOR2' : 'VOR1';
|
||||
const dme = num(V.cdiSrc) === 1 ? num(V.nav2Dme) : num(V.nav1Dme);
|
||||
// Nav source (Nav Source Selector, manual p24): 0 VOR1 · 1 VOR2 · 2 FMS.
|
||||
// FMS course is magenta, VOR course is green (Honeywell convention).
|
||||
const cdiSrc = num(V.cdiSrc);
|
||||
const srcLabel = cdiSrc === 2 ? 'FMS1' : cdiSrc === 1 ? 'VOR2' : 'VOR1';
|
||||
const srcColor = cdiSrc === 2 ? '#d24bd2' : '#13e000';
|
||||
const cycleNav = () => xp.setDataref('cdiSrc', cdiSrc === 1 ? 0 : 1); // toggle VOR1↔VOR2
|
||||
const setFms = () => xp.setDataref('cdiSrc', 2);
|
||||
const dme = cdiSrc === 1 ? num(V.nav2Dme) : num(V.nav1Dme);
|
||||
// airspeed trend vector (#3): smoothed acceleration projected 10 s ahead
|
||||
const t = trend.current, nowS = (typeof performance !== 'undefined' ? performance.now() : Date.now()) / 1000;
|
||||
const dt = Math.min(0.5, Math.max(0.001, nowS - (t.t || nowS)));
|
||||
const rate = (ias - (t.ias != null ? t.ias : ias)) / dt; // kt/s
|
||||
t.s = (t.s || 0) * 0.92 + rate * 0.08; t.ias = ias; t.t = nowS;
|
||||
const trendKt = clamp(t.s * 10, -45, 45);
|
||||
|
||||
return (
|
||||
<div className="cit-screen">
|
||||
@@ -299,7 +318,7 @@ export default function CitPFD({ xp }) {
|
||||
{/* attitude */}
|
||||
<g transform="translate(400 270)"><Attitude pitch={pitch} roll={roll} slip={slip} fdP={num(V.fdPitch)} fdR={num(V.fdRoll)} fdOn={fdOn} /></g>
|
||||
{/* speed tape (#2,#3) */}
|
||||
<g transform="translate(96 90)"><SpeedTape ias={ias} mach={mach} bug={num(V.apSpdBug)} alt={alt} /></g>
|
||||
<g transform="translate(96 90)"><SpeedTape ias={ias} mach={mach} bug={num(V.apSpdBug)} alt={alt} trendKt={trendKt} /></g>
|
||||
<text x={120} y={78} fontSize="14" fill="#9aa6ad" textAnchor="middle">KIAS</text>
|
||||
{/* AOA index (#manual p22) */}
|
||||
<g transform="translate(48 600)"><AoaIndex alpha={aoa} /></g>
|
||||
@@ -308,7 +327,7 @@ export default function CitPFD({ xp }) {
|
||||
{/* VSI (#13,#14) */}
|
||||
<g transform="translate(716 130)"><VSI vs={vs} /></g>
|
||||
{/* HSI (#10) */}
|
||||
<g transform="translate(400 690)"><HSI hdg={hdg} trk={trk} crs={crs} hdgBug={hdgBug} cdi={cdi} toFrom={toFrom} brg1={brg1} brg2={brg2} srcLabel={srcLabel} /></g>
|
||||
<g transform="translate(400 690)"><HSI hdg={hdg} trk={trk} crs={crs} hdgBug={hdgBug} cdi={cdi} toFrom={toFrom} brg1={brg1} brg2={brg2} srcLabel={srcLabel} srcColor={srcColor} /></g>
|
||||
|
||||
{/* CRS / HDG digital (#5,#7) */}
|
||||
<g fontSize="15" fontWeight="700">
|
||||
@@ -334,8 +353,14 @@ export default function CitPFD({ xp }) {
|
||||
<text x={760} y={84} fontSize="13" fill="#9aa6ad" textAnchor="end">FT</text>
|
||||
</svg>
|
||||
|
||||
{/* bezel buttons — MINIMUMS rotary (#8), RA/BARO (#9), STD (#11), BARO SET (#12) */}
|
||||
{/* bezel buttons — Nav Source Selector (p24, sits under the PFD), MINIMUMS
|
||||
rotary (#8), RA/BARO (#9), STD (#11), BARO SET (#12), CRS, HDG */}
|
||||
<div className="cit-bezel">
|
||||
<div className="cit-bz-group">
|
||||
<span className="cit-bz-lbl">NAV SRC</span>
|
||||
<button className={`cit-bz-btn ${cdiSrc !== 2 ? 'on' : ''}`} onClick={cycleNav}>{cdiSrc === 1 ? 'VOR2' : 'VOR1'}</button>
|
||||
<button className={`cit-bz-btn ${cdiSrc === 2 ? 'on' : ''}`} onClick={setFms}>FMS</button>
|
||||
</div>
|
||||
<div className="cit-bz-group">
|
||||
<span className="cit-bz-lbl">MINIMUMS</span>
|
||||
<button className="cit-bz-knob" onClick={() => setMin((m) => ({ ...m, on: true, ft: m.ft - 50 }))}>‹</button>
|
||||
|
||||
Reference in New Issue
Block a user