Skip to content

Commit

Permalink
feat(nd): true ref
Browse files Browse the repository at this point in the history
Still work to be done around radio nav indications.
  • Loading branch information
tracernz committed Dec 7, 2022
1 parent ecd34e5 commit 9a69586
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 78 deletions.
5 changes: 4 additions & 1 deletion src/fmgc/src/guidance/lnav/LnavDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
}
Expand Down
6 changes: 3 additions & 3 deletions src/instruments/src/Common/utils.tsx
Original file line number Diff line number Diff line change
@@ -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<LayerProps> = ({ x = 0, y = 0, id, className, children }) => (
<g transform={`translate(${x}, ${y})`} id={id} className={className}>
export const Layer: React.FC<LayerProps> = ({ x = 0, y = 0, id, className, children, visibility = 'visible' }) => (
<g transform={`translate(${x}, ${y})`} id={id} visibility={visibility} className={className}>
{children}
</g>
);
Expand Down
26 changes: 0 additions & 26 deletions src/instruments/src/ND/elements/ApproachMessage.tsx

This file was deleted.

2 changes: 2 additions & 0 deletions src/instruments/src/ND/elements/RadioNavInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => {
Expand Down
8 changes: 5 additions & 3 deletions src/instruments/src/ND/elements/ToWaypointIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import { SimVarString } from '@shared/simvar';

export type ToWaypointIndicatorProps = {
side: EfisSide,
trueRef: boolean,
}

export const ToWaypointIndicator: FC<ToWaypointIndicatorProps> = memo(({ side }) => {
export const ToWaypointIndicator: FC<ToWaypointIndicatorProps> = memo(({ side, trueRef }) => {
// TODO replace with appropriate ARINC 429 labels

const [ident, setIdent] = useState<string | null>(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');

Expand Down Expand Up @@ -55,8 +57,8 @@ export const ToWaypointIndicator: FC<ToWaypointIndicatorProps> = memo(({ side })

{bearing && bearing !== -1 && Number.isFinite(bearing) && (
<>
<text x={54} y={0} fontSize={25} className="Green" textAnchor="end">{(Math.round(bearing)).toString().padStart(3, '0')}</text>
<text x={73} y={2} fontSize={25} className="Cyan" textAnchor="end">&deg;</text>
<text x={54} y={0} fontSize={25} className="Green" textAnchor="end">{(Math.round(trueRef ? trueBearing : bearing)).toString().padStart(3, '0')}</text>
<text x={73} y={trueRef ? 0 : 2} fontSize={25} className="Cyan" textAnchor="end">{trueRef ? 'T' : '°'}</text>
</>
)}

Expand Down
76 changes: 76 additions & 0 deletions src/instruments/src/ND/elements/TopMessages.tsx
Original file line number Diff line number Diff line change
@@ -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<GridTrackProps> = ({ gridTrack }) => (
<>
<rect x={0} width={87} y={-20} height={22} className="White" strokeWidth={1.5} />
<text x={44} fontSize={22} textAnchor="middle">
<tspan className="Green">
{gridTrack?.toFixed(0).padStart(3, '0') ?? ''}
</tspan>
<tspan className="Cyan">°G</tspan>
</text>
</>
);

type TrueFlagProps = {
xOffset?: number,
box: boolean,
};

const TrueFlag: React.FC<TrueFlagProps> = ({ xOffset = 0, box }) => (
<>
<rect x={-30 + xOffset} width={60} y={-20} height={22} className="Cyan" strokeWidth={1.5} visibility={box ? 'visible' : 'hidden'} />
<text x={xOffset} fontSize={22} className="Cyan" textAnchor="middle">TRUE</text>
</>
);

export const TopMessages: React.FC<TopMessagesProps> = ({ 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<string | null>(null);

const [gridTrack, setGridTrack] = useState<number | null>(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 (
<>
<Layer x={384} y={28} visibility={apprMsg === null ? 'hidden' : 'visible'}>
<text x={0} y={0} fontSize={25} className="Green" textAnchor="middle">{apprMsg ?? ''}</text>
</Layer>
<Layer x={384} y={56} visibility={trueRef ? 'visible' : 'hidden'}>
<TrueFlag xOffset={apprMsg === null && gridTrack !== null ? -40 : 0} box={apprMsg === null} />
<Layer x={0} y={0} visibility={apprMsg === null && gridTrack !== null ? 'visible' : 'hidden'}>
<GridTrack gridTrack={gridTrack ?? 0} />
</Layer>
</Layer>
</>
);
};
40 changes: 21 additions & 19 deletions src/instruments/src/ND/pages/ArcMode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -25,36 +25,38 @@ export interface ArcModeProps {
}

export const ArcMode: React.FC<ArcModeProps> = ({ 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 (
Expand Down Expand Up @@ -89,9 +91,9 @@ export const ArcMode: React.FC<ArcModeProps> = ({ symbols, adirsAlign, rangeSett
<RadioNeedle index={2} side={side} displayMode={Mode.ARC} centreHeight={620} />
</g>

<ToWaypointIndicator side={side} />
<ToWaypointIndicator side={side} trueRef={trueRef} />

<ApproachMessage side={side} />
<TopMessages side={side} ppos={ppos} trueTrack={trueTrack} trueRef={trueRef} />
<TrackBug heading={heading} track={track} />
{ lsDisplayed && <LsCourseBug heading={heading} lsCourse={lsCourse} /> }
<SelectedHeadingBug heading={heading} selected={selectedHeading} />
Expand Down
16 changes: 11 additions & 5 deletions src/instruments/src/ND/pages/PlanMode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -19,15 +20,20 @@ export interface PlanModeProps {
export const PlanMode: FC<PlanModeProps> = ({ 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<MapParameters>(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 (
<>
<Overlay rangeSetting={rangeSetting} />
Expand All @@ -46,10 +52,10 @@ export const PlanMode: FC<PlanModeProps> = ({ side, symbols, adirsAlign, rangeSe
</g>

{adirsAlign && !mapHidden && mapParams.valid && (
<Plane location={ppos} heading={trueHeading} mapParams={mapParams} />
<Plane location={ppos} heading={trueHeading.value} mapParams={mapParams} />
)}

<ToWaypointIndicator side={side} />
<ToWaypointIndicator side={side} trueRef={trueRef} />

<CrossTrack x={44} y={690} side={side} isPlanMode />
</>
Expand Down
Loading

0 comments on commit 9a69586

Please sign in to comment.