From 9a6958628c9f588e472792eea6e92a42c591a82a Mon Sep 17 00:00:00 2001 From: Michael Corcoran Date: Mon, 5 Dec 2022 22:44:29 +1300 Subject: [PATCH] feat(nd): true ref Still work to be done around radio nav indications. --- src/fmgc/src/guidance/lnav/LnavDriver.ts | 5 +- src/instruments/src/Common/utils.tsx | 6 +- .../src/ND/elements/ApproachMessage.tsx | 26 ------- .../src/ND/elements/RadioNavInfo.tsx | 2 + .../src/ND/elements/ToWaypointIndicator.tsx | 8 +- .../src/ND/elements/TopMessages.tsx | 76 +++++++++++++++++++ src/instruments/src/ND/pages/ArcMode.tsx | 40 +++++----- src/instruments/src/ND/pages/PlanMode.tsx | 16 ++-- src/instruments/src/ND/pages/RoseMode.tsx | 44 ++++++----- src/instruments/src/ND/styles.scss | 8 +- 10 files changed, 153 insertions(+), 78 deletions(-) delete mode 100644 src/instruments/src/ND/elements/ApproachMessage.tsx create mode 100644 src/instruments/src/ND/elements/TopMessages.tsx diff --git a/src/fmgc/src/guidance/lnav/LnavDriver.ts b/src/fmgc/src/guidance/lnav/LnavDriver.ts index 104bba241d39..2ca914590aab 100644 --- a/src/fmgc/src/guidance/lnav/LnavDriver.ts +++ b/src/fmgc/src/guidance/lnav/LnavDriver.ts @@ -411,8 +411,9 @@ export class LnavDriver implements GuidanceComponent { private updateEfisData(activeLeg: Leg, gs: Knots) { const termination = activeLeg instanceof XFLeg ? activeLeg.fix.infos.coordinates : activeLeg.getPathEndPoint(); + const efisTrueBearing = termination ? Avionics.Utils.computeGreatCircleHeading(this.ppos, termination) : -1; const efisBearing = termination ? A32NX_Util.trueToMagnetic( - Avionics.Utils.computeGreatCircleHeading(this.ppos, termination), + efisTrueBearing, Facilities.getMagVar(this.ppos.lat, this.ppos.long), ) : -1; @@ -423,10 +424,12 @@ export class LnavDriver implements GuidanceComponent { // FIXME should be NCD if no FM position SimVar.SetSimVarValue('L:A32NX_EFIS_L_TO_WPT_BEARING', 'Degrees', efisBearing); + SimVar.SetSimVarValue('L:A32NX_EFIS_L_TO_WPT_TRUE_BEARING', 'Degrees', efisTrueBearing); SimVar.SetSimVarValue('L:A32NX_EFIS_L_TO_WPT_DISTANCE', 'Number', efisDistance); SimVar.SetSimVarValue('L:A32NX_EFIS_L_TO_WPT_ETA', 'Seconds', efisEta); SimVar.SetSimVarValue('L:A32NX_EFIS_R_TO_WPT_BEARING', 'Degrees', efisBearing); + SimVar.SetSimVarValue('L:A32NX_EFIS_R_TO_WPT_TRUE_BEARING', 'Degrees', efisTrueBearing); SimVar.SetSimVarValue('L:A32NX_EFIS_R_TO_WPT_DISTANCE', 'Number', efisDistance); SimVar.SetSimVarValue('L:A32NX_EFIS_R_TO_WPT_ETA', 'Seconds', efisEta); } diff --git a/src/instruments/src/Common/utils.tsx b/src/instruments/src/Common/utils.tsx index bfaa693e404c..b9d07f447845 100644 --- a/src/instruments/src/Common/utils.tsx +++ b/src/instruments/src/Common/utils.tsx @@ -1,9 +1,9 @@ import React from 'react'; -export type LayerProps = { x: number, y: number, id?: string, className?: string } +export type LayerProps = { x: number, y: number, id?: string, className?: string, visibility?: string } -export const Layer: React.FC = ({ x = 0, y = 0, id, className, children }) => ( - +export const Layer: React.FC = ({ x = 0, y = 0, id, className, children, visibility = 'visible' }) => ( + {children} ); diff --git a/src/instruments/src/ND/elements/ApproachMessage.tsx b/src/instruments/src/ND/elements/ApproachMessage.tsx deleted file mode 100644 index 7bdcdea63e9d..000000000000 --- a/src/instruments/src/ND/elements/ApproachMessage.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import { Layer } from '@instruments/common/utils'; -import { useSimVar } from '@instruments/common/simVars'; -import { SimVarString } from '@shared/simvar'; -import { EfisSide } from '@shared/NavigationDisplay'; - -type ApproachMessageProps = { - side: EfisSide, -} - -export const ApproachMessage: React.FC = ({ side }) => { - const [apprMsg0] = useSimVar(`L:A32NX_EFIS_${side}_APPR_MSG_0`, 'number', 5000); - const [apprMsg1] = useSimVar(`L:A32NX_EFIS_${side}_APPR_MSG_1`, 'number', 5000); - - const apprMsg = SimVarString.unpack([apprMsg0, apprMsg1]); - - if (apprMsg.length < 1) { - return null; - } - - return ( - - {apprMsg} - - ); -}; diff --git a/src/instruments/src/ND/elements/RadioNavInfo.tsx b/src/instruments/src/ND/elements/RadioNavInfo.tsx index 31439bcd2bc4..a1cb64f9a05c 100644 --- a/src/instruments/src/ND/elements/RadioNavInfo.tsx +++ b/src/instruments/src/ND/elements/RadioNavInfo.tsx @@ -9,6 +9,8 @@ export enum NavAidMode { VOR, } +// TODO true ref + export type RadioNavInfoProps = { index: 1 | 2, side: EfisSide } const TuningModeIndicator: React.FC<{ index: 1 | 2, frequency: number }> = ({ index, frequency }) => { diff --git a/src/instruments/src/ND/elements/ToWaypointIndicator.tsx b/src/instruments/src/ND/elements/ToWaypointIndicator.tsx index 1d11831fa802..e6f8d7f96b9d 100644 --- a/src/instruments/src/ND/elements/ToWaypointIndicator.tsx +++ b/src/instruments/src/ND/elements/ToWaypointIndicator.tsx @@ -6,15 +6,17 @@ import { SimVarString } from '@shared/simvar'; export type ToWaypointIndicatorProps = { side: EfisSide, + trueRef: boolean, } -export const ToWaypointIndicator: FC = memo(({ side }) => { +export const ToWaypointIndicator: FC = memo(({ side, trueRef }) => { // TODO replace with appropriate ARINC 429 labels const [ident, setIdent] = useState(null); const [ident0] = useSimVar(`L:A32NX_EFIS_${side}_TO_WPT_IDENT_0`, 'number', 500); const [ident1] = useSimVar(`L:A32NX_EFIS_${side}_TO_WPT_IDENT_1`, 'number', 500); const [bearing] = useSimVar(`L:A32NX_EFIS_${side}_TO_WPT_BEARING`, 'Degrees'); + const [trueBearing] = useSimVar(`L:A32NX_EFIS_${side}_TO_WPT_TRUE_BEARING`, 'Degrees'); const [distance] = useSimVar(`L:A32NX_EFIS_${side}_TO_WPT_DISTANCE`, 'Number'); const [eta] = useSimVar(`L:A32NX_EFIS_${side}_TO_WPT_ETA`, 'Seconds'); @@ -55,8 +57,8 @@ export const ToWaypointIndicator: FC = memo(({ side }) {bearing && bearing !== -1 && Number.isFinite(bearing) && ( <> - {(Math.round(bearing)).toString().padStart(3, '0')} - ° + {(Math.round(trueRef ? trueBearing : bearing)).toString().padStart(3, '0')} + {trueRef ? 'T' : '°'} )} diff --git a/src/instruments/src/ND/elements/TopMessages.tsx b/src/instruments/src/ND/elements/TopMessages.tsx new file mode 100644 index 000000000000..f049413256cd --- /dev/null +++ b/src/instruments/src/ND/elements/TopMessages.tsx @@ -0,0 +1,76 @@ +import React, { useEffect, useState } from 'react'; +import { Layer } from '@instruments/common/utils'; +import { EfisSide } from '@shared/NavigationDisplay'; +import { Arinc429Word } from '@shared/arinc429'; +import { SimVarString } from '@shared/simvar'; +import { useSimVar } from '@instruments/common/simVars'; + +type TopMessagesProps = { + side: EfisSide, + ppos: LatLongData, + trueTrack: Arinc429Word, + trueRef: boolean, +} + +type GridTrackProps = { + gridTrack: number, +}; + +const GridTrack: React.FC = ({ gridTrack }) => ( + <> + + + + ◇ + {gridTrack?.toFixed(0).padStart(3, '0') ?? ''} + + °G + + +); + +type TrueFlagProps = { + xOffset?: number, + box: boolean, +}; + +const TrueFlag: React.FC = ({ xOffset = 0, box }) => ( + <> + + TRUE + +); + +export const TopMessages: React.FC = ({ side, ppos, trueTrack, trueRef }) => { + const [apprMsg0] = useSimVar(`L:A32NX_EFIS_${side}_APPR_MSG_0`, 'number', 5000); + const [apprMsg1] = useSimVar(`L:A32NX_EFIS_${side}_APPR_MSG_1`, 'number', 5000); + const [apprMsg, setApprMsg] = useState(null); + + const [gridTrack, setGridTrack] = useState(null); + useEffect(() => { + if (trueTrack.isNormalOperation() && Math.abs(ppos.lat) > 65) { + setGridTrack(Math.round(Avionics.Utils.clampAngle(trueTrack.value - Math.sign(ppos.lat) * ppos.long)) % 360); + } else { + setGridTrack(null); + } + }, [ppos.lat.toFixed(0), ppos.long.toFixed(1), trueTrack.value.toFixed(0), trueTrack.ssm]); + + useEffect(() => { + const msg = SimVarString.unpack([apprMsg0, apprMsg1]); + setApprMsg(msg.length > 0 ? msg : null); + }, [apprMsg0, apprMsg1]); + + return ( + <> + + {apprMsg ?? ''} + + + + + + + + + ); +}; diff --git a/src/instruments/src/ND/pages/ArcMode.tsx b/src/instruments/src/ND/pages/ArcMode.tsx index 83f9c8ee3275..25041deeeb4e 100644 --- a/src/instruments/src/ND/pages/ArcMode.tsx +++ b/src/instruments/src/ND/pages/ArcMode.tsx @@ -2,14 +2,14 @@ import React, { memo, useEffect, useState } from 'react'; import { useSimVar } from '@instruments/common/simVars'; import { getSmallestAngle } from '@instruments/common/utils'; import { MathUtils } from '@shared/MathUtils'; -import { LatLongData } from '@typings/fs-base-ui/html_ui/JS/Types'; import { RangeSetting, Mode, EfisSide, NdSymbol } from '@shared/NavigationDisplay'; import { ArmedLateralMode, isArmed, LateralMode } from '@shared/autopilot'; +import { useArinc429Var } from '@instruments/common/arinc429'; +import { TopMessages } from '../elements/TopMessages'; import { FlightPlan } from '../elements/FlightPlan'; import { MapParameters } from '../utils/MapParameters'; import { RadioNeedle } from '../elements/RadioNeedles'; import { ToWaypointIndicator } from '../elements/ToWaypointIndicator'; -import { ApproachMessage } from '../elements/ApproachMessage'; import { CrossTrack } from '../elements/CrossTrack'; import { TrackLine } from '../elements/TrackLine'; import { Traffic } from '../elements/Traffic'; @@ -25,36 +25,38 @@ export interface ArcModeProps { } export const ArcMode: React.FC = ({ symbols, adirsAlign, rangeSetting, side, ppos, mapHidden }) => { - const [magHeading] = useSimVar('PLANE HEADING DEGREES MAGNETIC', 'degrees'); - const [magTrack] = useSimVar('GPS GROUND MAGNETIC TRACK', 'degrees'); - const [trueHeading] = useSimVar('PLANE HEADING DEGREES TRUE', 'degrees'); + // TODO arinc var selector + const magHeading = useArinc429Var('L:A32NX_ADIRS_IR_1_HEADING'); + const magTrack = useArinc429Var('L:A32NX_ADIRS_IR_1_TRACK'); + const trueHeading = useArinc429Var('L:A32NX_ADIRS_IR_1_TRUE_HEADING'); + const trueTrack = useArinc429Var('L:A32NX_ADIRS_IR_1_TRUE_TRACK'); const [tcasMode] = useSimVar('L:A32NX_SWITCH_TCAS_Position', 'number'); const [selectedHeading] = useSimVar('L:A32NX_AUTOPILOT_HEADING_SELECTED', 'degrees'); const [lsCourse] = useSimVar('L:A32NX_FM_LS_COURSE', 'number'); const [lsDisplayed] = useSimVar(`L:BTN_LS_${side === 'L' ? 1 : 2}_FILTER_ACTIVE`, 'bool'); // TODO rename simvar const [fmaLatMode] = useSimVar('L:A32NX_FMA_LATERAL_MODE', 'enum', 200); const [armedLateralBitmask] = useSimVar('L:A32NX_FMA_LATERAL_ARMED', 'enum', 200); - const [groundSpeed] = useSimVar('GPS GROUND SPEED', 'Meters per second', 200); + const irMaint = useArinc429Var('L:A32NX_ADIRS_IR_1_MAINT_WORD'); + const [trueRefPb] = useSimVar('L:A32NX_PUSH_TRUE_REF', 'bool'); + const [trueRef, setTrueRef] = useState(false); - const heading = Number(MathUtils.fastToFixed(magHeading, 2)); - let track = Number(MathUtils.fastToFixed(magTrack, 2)); - - // Workaround for bug with gps ground track simvar - if (groundSpeed < 40) { - track = (0.025 * groundSpeed + 0.00005) * track + (1 - (0.025 * groundSpeed + 0.00005)) * heading; - track = Number(MathUtils.fastToFixed(track, 2)); - } + const heading = Number(MathUtils.fastToFixed((trueRef ? trueHeading.value : magHeading.value), 2)); + const track = Number(MathUtils.fastToFixed((trueRef ? trueTrack.value : magTrack.value), 2)); const [mapParams] = useState(() => { const params = new MapParameters(); - params.compute(ppos, rangeSetting, 492, trueHeading); + params.compute(ppos, rangeSetting, 492, trueHeading.value); return params; }); useEffect(() => { - mapParams.compute(ppos, rangeSetting, 492, trueHeading); - }, [ppos.lat, ppos.long, trueHeading, rangeSetting].map((n) => MathUtils.fastToFixed(n, 6))); + mapParams.compute(ppos, rangeSetting, 492, trueHeading.value); + }, [ppos.lat, ppos.long, trueHeading.value, rangeSetting].map((n) => MathUtils.fastToFixed(n, 6))); + + useEffect(() => { + setTrueRef((irMaint.getBitValueOr(15, false) || trueRefPb) && !irMaint.getBitValueOr(2, false)); + }, [irMaint.value, trueRefPb]); if (adirsAlign) { return ( @@ -89,9 +91,9 @@ export const ArcMode: React.FC = ({ symbols, adirsAlign, rangeSett - + - + { lsDisplayed && } diff --git a/src/instruments/src/ND/pages/PlanMode.tsx b/src/instruments/src/ND/pages/PlanMode.tsx index bd54e210bd88..ef47849f436f 100644 --- a/src/instruments/src/ND/pages/PlanMode.tsx +++ b/src/instruments/src/ND/pages/PlanMode.tsx @@ -2,6 +2,7 @@ import React, { FC, memo, useEffect, useState } from 'react'; import { useSimVar } from '@instruments/common/simVars'; import { Coordinates } from '@fmgc/flightplanning/data/geo'; import { EfisSide, NdSymbol } from '@shared/NavigationDisplay'; +import { useArinc429Var } from '@instruments/common/arinc429'; import { CrossTrack } from '../elements/CrossTrack'; import { ToWaypointIndicator } from '../elements/ToWaypointIndicator'; import { FlightPlan } from '../elements/FlightPlan'; @@ -19,15 +20,20 @@ export interface PlanModeProps { export const PlanMode: FC = ({ side, symbols, adirsAlign, rangeSetting, ppos, mapHidden }) => { const [planCentreLat] = useSimVar('L:A32NX_SELECTED_WAYPOINT_LAT', 'Degrees'); const [planCentreLong] = useSimVar('L:A32NX_SELECTED_WAYPOINT_LONG', 'Degrees'); - - const [trueHeading] = useSimVar('PLANE HEADING DEGREES TRUE', 'degrees'); - + const trueHeading = useArinc429Var('L:A32NX_ADIRS_IR_1_TRUE_HEADING'); + const irMaint = useArinc429Var('L:A32NX_ADIRS_IR_1_MAINT_WORD'); + const [trueRefPb] = useSimVar('L:A32NX_PUSH_TRUE_REF', 'bool'); + const [trueRef, setTrueRef] = useState(false); const [mapParams] = useState(new MapParameters()); useEffect(() => { mapParams.compute({ lat: planCentreLat, long: planCentreLong }, rangeSetting / 2, 250, 0); }, [planCentreLat, planCentreLong, rangeSetting]); + useEffect(() => { + setTrueRef((irMaint.getBitValueOr(15, false) || trueRefPb) && !irMaint.getBitValueOr(2, false)); + }, [irMaint.value, trueRefPb]); + return ( <> @@ -46,10 +52,10 @@ export const PlanMode: FC = ({ side, symbols, adirsAlign, rangeSe {adirsAlign && !mapHidden && mapParams.valid && ( - + )} - + diff --git a/src/instruments/src/ND/pages/RoseMode.tsx b/src/instruments/src/ND/pages/RoseMode.tsx index cda40cec1030..e6b2f8558f3a 100644 --- a/src/instruments/src/ND/pages/RoseMode.tsx +++ b/src/instruments/src/ND/pages/RoseMode.tsx @@ -5,11 +5,12 @@ import { MathUtils } from '@shared/MathUtils'; import { TuningMode } from '@fmgc/radionav'; import { Mode, EfisSide, NdSymbol } from '@shared/NavigationDisplay'; import { ArmedLateralMode, isArmed, LateralMode } from '@shared/autopilot'; +import { useArinc429Var } from '@instruments/common/arinc429'; +import { TopMessages } from '../elements/TopMessages'; import { ToWaypointIndicator } from '../elements/ToWaypointIndicator'; import { FlightPlan } from '../elements/FlightPlan'; import { MapParameters } from '../utils/MapParameters'; import { RadioNeedle } from '../elements/RadioNeedles'; -import { ApproachMessage } from '../elements/ApproachMessage'; import { CrossTrack } from '../elements/CrossTrack'; import { TrackLine } from '../elements/TrackLine'; import { Traffic } from '../elements/Traffic'; @@ -26,35 +27,37 @@ export interface RoseModeProps { } export const RoseMode: FC = ({ symbols, adirsAlign, rangeSetting, mode, side, ppos, mapHidden }) => { - const [magHeading] = useSimVar('PLANE HEADING DEGREES MAGNETIC', 'degrees'); - const [magTrack] = useSimVar('GPS GROUND MAGNETIC TRACK', 'degrees'); - const [trueHeading] = useSimVar('PLANE HEADING DEGREES TRUE', 'degrees'); + const magHeading = useArinc429Var('L:A32NX_ADIRS_IR_1_HEADING'); + const magTrack = useArinc429Var('L:A32NX_ADIRS_IR_1_TRACK'); + const trueHeading = useArinc429Var('L:A32NX_ADIRS_IR_1_TRUE_HEADING'); + const trueTrack = useArinc429Var('L:A32NX_ADIRS_IR_1_TRUE_TRACK'); const [tcasMode] = useSimVar('L:A32NX_SWITCH_TCAS_Position', 'number'); const [selectedHeading] = useSimVar('L:A32NX_AUTOPILOT_HEADING_SELECTED', 'degrees'); const [lsCourse] = useSimVar('L:A32NX_FM_LS_COURSE', 'number'); const [lsDisplayed] = useSimVar(`L:BTN_LS_${side === 'L' ? 1 : 2}_FILTER_ACTIVE`, 'bool'); // TODO rename simvar const [fmaLatMode] = useSimVar('L:A32NX_FMA_LATERAL_MODE', 'enum', 200); const [armedLateralBitmask] = useSimVar('L:A32NX_FMA_LATERAL_ARMED', 'enum', 200); - const [groundSpeed] = useSimVar('GPS GROUND SPEED', 'Meters per second', 200); + const irMaint = useArinc429Var('L:A32NX_ADIRS_IR_1_MAINT_WORD'); + const [trueRefPb] = useSimVar('L:A32NX_PUSH_TRUE_REF', 'bool'); + const [trueRef, setTrueRef] = useState(false); - const heading = Math.round(Number(MathUtils.fastToFixed(magHeading, 1)) * 1000) / 1000; - let track = Math.round(Number(MathUtils.fastToFixed(magTrack, 1)) * 1000) / 1000; - - // Workaround for bug with gps ground track simvar - if (groundSpeed < 40) { - track = (0.025 * groundSpeed + 0.00005) * track + (1 - (0.025 * groundSpeed + 0.00005)) * heading; - } + const heading = Number(MathUtils.fastToFixed((trueRef ? trueHeading.value : magHeading.value), 2)); + const track = Number(MathUtils.fastToFixed((trueRef ? trueTrack.value : magTrack.value), 2)); const [mapParams] = useState(() => { const params = new MapParameters(); - params.compute(ppos, rangeSetting / 2, 250, trueHeading); + params.compute(ppos, rangeSetting / 2, 250, trueHeading.value); return params; }); useEffect(() => { - mapParams.compute(ppos, rangeSetting / 2, 250, trueHeading); - }, [ppos.lat, ppos.long, trueHeading, rangeSetting].map((n) => MathUtils.fastToFixed(n, 6))); + mapParams.compute(ppos, rangeSetting / 2, 250, trueHeading.value); + }, [ppos.lat, ppos.long, trueHeading.value, rangeSetting].map((n) => MathUtils.fastToFixed(n, 6))); + + useEffect(() => { + setTrueRef((irMaint.getBitValueOr(15, false) || trueRefPb) && !irMaint.getBitValueOr(2, false)); + }, [irMaint.value, trueRefPb]); if (adirsAlign) { return ( @@ -89,15 +92,15 @@ export const RoseMode: FC = ({ symbols, adirsAlign, rangeSetting, - { mode === Mode.ROSE_VOR && } + { mode === Mode.ROSE_VOR && } - { mode === Mode.ROSE_ILS && } + { mode === Mode.ROSE_ILS && } - { mode === Mode.ROSE_NAV && } + { mode === Mode.ROSE_NAV && } { mode === Mode.ROSE_VOR && } { mode === Mode.ROSE_ILS && } - + { mode === Mode.ROSE_NAV && lsDisplayed && } @@ -548,6 +551,7 @@ const MapFailOverlay: FC> = memo(({ rangeSett )); +// TODO true ref const VorCaptureOverlay: React.FC<{ heading: number, side: EfisSide, @@ -628,6 +632,7 @@ const VorCaptureOverlay: React.FC<{ ); }; +// TODO true ref const IlsCaptureOverlay: React.FC<{ heading: number, _side: EfisSide, @@ -713,6 +718,7 @@ const TrackBug: React.FC<{heading: number, track: number}> = memo(({ heading, tr ); }); +// TODO true ref const LsCourseBug: React.FC<{heading: number, lsCourse: number}> = ({ heading, lsCourse }) => { if (lsCourse < 0) { return null; diff --git a/src/instruments/src/ND/styles.scss b/src/instruments/src/ND/styles.scss index a545f0acda78..432be1ff60a0 100644 --- a/src/instruments/src/ND/styles.scss +++ b/src/instruments/src/ND/styles.scss @@ -49,7 +49,9 @@ text.Magenta { } .Cyan text, -text.Cyan { +text.Cyan, +.Cyan tspan, +tspan.Cyan { fill: $display-cyan; stroke: none; } @@ -86,7 +88,9 @@ text.White { } .Green text, -text.Green { +text.Green, +.Green tspan, +tspan.Green { fill: $display-green; stroke: none; }