G1000: manual-accurate radios, baro units, declutter, minimums, OBS, audio panel
Aligned to the official X-Plane 1000 manual: - NAV radio: active RIGHT / standby LEFT (boxed) per S.12 (COM already correct) - ALT UNIT softkey (IN / HPA) in the PFD submenu, baro readout converts (S.20) - DCLTR cycles 3 levels (land / +NDB / flight-plan only) with DCLTR-n label (S.56) - TOPO and TERRAIN are now independent toggles (relief vs awareness overlay) (S.57) - Barometric MINIMUMS: BARO MIN bug + readout on the altimeter, amber "MINIMUMS" annunciation at/below the decision altitude; set via TMR/REF (lifted to App) - OBS mode: HSI course follows the CRS knob (magenta "OBS"), sequencing suspended - New Audio Panel tab (COM mic/receive, MKR/DME/ADF, intercom, Display Backup) (S.91) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,9 @@ import { num, systemAlerts } from '../api/useXplane.js';
|
||||
// SYN TERR toggles the 3D synthetic-vision terrain on/off.
|
||||
const PFD_MENU = {
|
||||
root: ['', 'INSET', '', 'PFD', '', 'CDI', 'DME', 'XPDR', 'IDENT', 'TMR/REF', 'NRST', 'CAUTION'],
|
||||
pfd: ['PATHWAY', 'SYN TERR', 'HRZN HDG', 'APTSIGNS', '', '', '', '', '', '', '', 'BACK'],
|
||||
pfd: ['PATHWAY', 'SYN TERR', 'HRZN HDG', 'APTSIGNS', 'ALT UNIT', '', '', '', '', '', '', 'BACK'],
|
||||
// ALT UNIT submenu: barometric pressure units (inHg / hectopascal), like the manual.
|
||||
altunit: ['IN', 'HPA', '', '', '', '', '', '', '', '', '', 'BACK'],
|
||||
// XPDR submenu: standby/on/alt modes, VFR (1200), CODE entry, IDENT.
|
||||
xpdr: ['STBY', 'ON', 'ALT', 'VFR', '', 'CODE', 'IDENT', '', '', '', '', 'BACK'],
|
||||
// CODE entry turns the softkeys into the octal squawk keypad (digits 0–7).
|
||||
@@ -34,12 +36,11 @@ const MFD_MENU = {
|
||||
// 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 };
|
||||
|
||||
export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, inset, onSetInset, insetMode, onInsetMode, nrst, onToggleNrst, tmr, onToggleTmr, dme, onToggleDme, alerts, onToggleAlerts, onDirect, onProc, onFpl, onFms, mapMode, onMapMode, knobMode = 'arrows', children }) {
|
||||
export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, inset, onSetInset, insetMode, onInsetMode, nrst, onToggleNrst, tmr, onToggleTmr, dme, onToggleDme, alerts, onToggleAlerts, onDirect, onProc, onFpl, onClr, onFms, mapMode, onMapMode, altHpa, onAltUnit, obs, onObs, knobMode = 'arrows', children }) {
|
||||
const u = variant === 'mfd' ? 'mfd' : 'pfd'; // command prefix
|
||||
const fire = (suffix) => xp && xp.command(`${u}_${suffix}`);
|
||||
const [page, setPage] = useState('root'); // softkey menu page
|
||||
const [squawk, setSquawk] = useState(''); // XPDR code being typed
|
||||
const [obs, setObs] = useState(false); // OBS (suspend) mode
|
||||
|
||||
const menu = variant === 'mfd' ? MFD_MENU : PFD_MENU;
|
||||
let keys = menu[page] || menu.root;
|
||||
@@ -62,31 +63,36 @@ export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, inset,
|
||||
|
||||
const onSoftkey = (i, label) => {
|
||||
fire(`softkey${i + 1}`); // mirror to the in-sim G1000
|
||||
// declutter cycles through 4 levels (0=all … 3=flight plan only), like the manual
|
||||
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 === 'BACK') setPage('root');
|
||||
else if (label === 'TOPO') setBase('topo');
|
||||
else if (label === 'TERRAIN') setBase('terrain');
|
||||
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') onMapMode && onMapMode((m) => ({ ...m, dcltr: m.dcltr ? 0 : 1 }));
|
||||
else if (label === 'DCLTR') cycleDcltr(onMapMode);
|
||||
else if (label === 'AIRWAYS') onMapMode && onMapMode((m) => ({ ...m, airways: !m.airways }));
|
||||
} else {
|
||||
if (label === 'PFD') setPage('pfd');
|
||||
else if (label === 'BACK') setPage(page === 'xpdrcode' ? 'xpdr' : 'root');
|
||||
else if (label === 'BACK') setPage({ xpdrcode: 'xpdr', altunit: 'pfd' }[page] || 'root');
|
||||
else if (label === 'SYN TERR') onToggleSvt && onToggleSvt();
|
||||
else if (label === 'ALT UNIT') setPage('altunit');
|
||||
else if (label === 'IN') { onAltUnit && onAltUnit(false); setPage('pfd'); }
|
||||
else if (label === 'HPA') { onAltUnit && onAltUnit(true); setPage('pfd'); }
|
||||
else if (label === 'INSET') {
|
||||
if (page === 'root') { onSetInset && onSetInset(true); setPage('inset'); }
|
||||
else onSetInset && onSetInset(!inset); // toggle from within the submenu
|
||||
}
|
||||
else if (label === 'OFF') { onSetInset && onSetInset(false); setPage('root'); }
|
||||
else if (label === 'DCLTR') onInsetMode && onInsetMode((m) => ({ ...m, dcltr: m.dcltr ? 0 : 1 }));
|
||||
else if (label === 'TOPO') onInsetMode && onInsetMode((m) => ({ ...m, base: 'topo' }));
|
||||
else if (label === 'TERRAIN') onInsetMode && onInsetMode((m) => ({ ...m, base: 'terrain' }));
|
||||
else if (label === 'DCLTR') cycleDcltr(onInsetMode);
|
||||
else if (label === 'TOPO') onInsetMode && onInsetMode((m) => ({ ...m, base: m.base === 'topo' ? 'dark' : 'topo' }));
|
||||
else if (label === 'TERRAIN') onInsetMode && onInsetMode((m) => ({ ...m, terrain: !m.terrain }));
|
||||
else if (label === 'NRST') onToggleNrst && onToggleNrst();
|
||||
else if (label === 'TMR/REF') onToggleTmr && onToggleTmr();
|
||||
else if (label === 'DME') onToggleDme && onToggleDme();
|
||||
else if (label === 'OBS') setObs((v) => !v); // suspend / OBS mode (also fires the sim softkey above)
|
||||
else if (label === 'OBS') onObs && onObs(); // suspend / OBS mode (also fires the sim softkey above)
|
||||
else if (label === 'CAUTION') onToggleAlerts && onToggleAlerts();
|
||||
else if (label === 'XPDR') setPage('xpdr');
|
||||
else if (label === 'STBY') setMode(1);
|
||||
@@ -102,13 +108,14 @@ export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, inset,
|
||||
// which softkey is "lit" right now
|
||||
const isOn = (label) => {
|
||||
if (variant === 'mfd') return (label === 'TOPO' && mapMode?.base === 'topo')
|
||||
|| (label === 'TERRAIN' && mapMode?.base === 'terrain') || (label === 'OSM' && mapMode?.base === 'osm')
|
||||
|| (label === 'TERRAIN' && mapMode?.terrain) || (label === 'OSM' && mapMode?.base === 'osm')
|
||||
|| (label === 'DCLTR' && mapMode?.dcltr > 0) || (label === 'AIRWAYS' && mapMode?.airways);
|
||||
return (label === 'SYN TERR' && svt3d) || (label === 'INSET' && inset) || (label === 'NRST' && nrst) || (label === 'TMR/REF' && tmr)
|
||||
|| (label === 'DME' && dme) || (label === 'OBS' && obs) || (label === 'CAUTION' && (alerts || hasAlerts))
|
||||
|| (label === 'STBY' && xpdrMode === 1) || (label === 'ON' && xpdrMode === 2) || (label === 'ALT' && xpdrMode === 3)
|
||||
|| (label === 'IN' && !altHpa) || (label === 'HPA' && altHpa)
|
||||
|| (page === 'inset' && label === 'TOPO' && insetMode?.base === 'topo')
|
||||
|| (page === 'inset' && label === 'TERRAIN' && insetMode?.base === 'terrain')
|
||||
|| (page === 'inset' && label === 'TERRAIN' && insetMode?.terrain)
|
||||
|| (label === 'DCLTR' && insetMode?.dcltr > 0);
|
||||
};
|
||||
|
||||
@@ -135,7 +142,9 @@ export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, inset,
|
||||
{/* softkey LABELS on the display (lowest line), like the real G1000 */}
|
||||
<div className="sk-labels">
|
||||
{keys.map((s, i) => (
|
||||
<span key={i} className={`skl ${s ? '' : 'empty'} ${s === 'CAUTION' ? 'caution' : ''} ${isOn(s) ? 'on' : ''}`}>{s}</span>
|
||||
<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
|
||||
}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@@ -159,7 +168,7 @@ export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, inset,
|
||||
<div className="bezel-grid">
|
||||
<BtnG fire={fire} mode={knobMode} cmd="direct" onClick={onDirect}>D→</BtnG><BtnG fire={fire} mode={knobMode} cmd="menu">MENU</BtnG>
|
||||
<BtnG fire={fire} mode={knobMode} cmd="fpl" onClick={onFpl}>FPL</BtnG><BtnG fire={fire} mode={knobMode} cmd="proc" onClick={onProc}>PROC</BtnG>
|
||||
<BtnG fire={fire} mode={knobMode} cmd="clr">CLR</BtnG><BtnG fire={fire} mode={knobMode} cmd="ent">ENT</BtnG>
|
||||
<BtnG fire={fire} mode={knobMode} cmd="clr" onClick={onClr}>CLR</BtnG><BtnG fire={fire} mode={knobMode} cmd="ent">ENT</BtnG>
|
||||
</div>
|
||||
<Knob label="FMS" sub="PUSH CRSR" big fire={fire} mode={knobMode}
|
||||
outer={['fms_outer_up', 'fms_outer_down']} inner={['fms_inner_up', 'fms_inner_down']} push="cursor"
|
||||
|
||||
Reference in New Issue
Block a user